summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-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")
set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb")
-if ((NOT CAN_BUILD_NX_TZDB OR YUZU_DOWNLOAD_TIME_ZONE_DATA) AND NOT EXISTS ${NX_TZDB_ARCHIVE})
+if ((NOT CAN_BUILD_NX_TZDB OR YUZU_DOWNLOAD_TIME_ZONE_DATA) AND NOT EXISTS ${NX_TZDB_ROMFS_DIR})
set(NX_TZDB_DOWNLOAD_URL "https://github.com/lat9nq/tzdb_to_nx/releases/download/${NX_TZDB_VERSION}/${NX_TZDB_VERSION}.zip")
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(
WORKING_DIRECTORY ${ZONE_PATH}
OUTPUT_VARIABLE FILE_LIST)
+if (NOT FILE_LIST)
+ message(FATAL_ERROR "No timezone files found in directory ${ZONE_PATH}, did the download fail?")
+endif()
+
set(DIRECTORY_NAME ${HEADER_NAME})
set(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 {
/**
* Begins emulation.
*/
- external fun run(path: String?)
+ external fun run(path: String?, programIndex: Int = 0)
// Surface Handling
external fun surfaceChanged(surf: Surface?)
@@ -489,6 +489,12 @@ object NativeLibrary {
sEmulationActivity.get()!!.onEmulationStopped(status)
}
+ @Keep
+ @JvmStatic
+ fun onProgramChanged(programIndex: Int) {
+ sEmulationActivity.get()!!.onProgramChanged(programIndex)
+ }
+
/**
* Logs the Yuzu version, Android version and, CPU.
*/
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 {
override fun onDestroy() {
stopForegroundService(this)
- emulationViewModel.clear()
super.onDestroy()
}
@@ -446,9 +445,14 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
}
fun onEmulationStopped(status: Int) {
- if (status == 0) {
+ if (status == 0 && emulationViewModel.programChanged.value == -1) {
finish()
}
+ emulationViewModel.setEmulationStopped(true)
+ }
+
+ fun onProgramChanged(programIndex: Int) {
+ emulationViewModel.setProgramChanged(programIndex)
}
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 {
R.id.menu_exit -> {
emulationState.stop()
+ NativeConfig.reloadGlobalConfig()
emulationViewModel.setIsEmulationStopping(true)
binding.drawerLayout.close()
binding.inGameMenu.requestFocus()
@@ -423,10 +424,38 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
}
+ launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ emulationViewModel.programChanged.collect {
+ if (it != 0) {
+ emulationViewModel.setEmulationStarted(false)
+ binding.drawerLayout.close()
+ binding.drawerLayout
+ .setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
+ ViewUtils.hideView(binding.surfaceInputOverlay)
+ ViewUtils.showView(binding.loadingIndicator)
+ }
+ }
+ }
+ }
+ launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ emulationViewModel.emulationStopped.collect {
+ if (it && emulationViewModel.programChanged.value != -1) {
+ if (perfStatsUpdater != null) {
+ perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
+ }
+ emulationState.changeProgram(emulationViewModel.programChanged.value)
+ emulationViewModel.setProgramChanged(-1)
+ emulationViewModel.setEmulationStopped(false)
+ }
+ }
+ }
+ }
}
}
- private fun startEmulation() {
+ private fun startEmulation(programIndex: Int = 0) {
if (!NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
if (!DirectoryInitialization.areDirectoriesReady) {
DirectoryInitialization.start()
@@ -434,7 +463,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
updateScreenLayout()
- emulationState.run(emulationActivity!!.isActivityRecreated)
+ emulationState.run(emulationActivity!!.isActivityRecreated, programIndex)
}
}
@@ -832,6 +861,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
) {
private var state: State
private var surface: Surface? = null
+ lateinit var emulationThread: Thread
init {
// Starting state is stopped.
@@ -877,7 +907,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
@Synchronized
- fun run(isActivityRecreated: Boolean) {
+ fun run(isActivityRecreated: Boolean, programIndex: Int = 0) {
if (isActivityRecreated) {
if (NativeLibrary.isRunning()) {
state = State.PAUSED
@@ -888,10 +918,20 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
// If the surface is set, run now. Otherwise, wait for it to get set.
if (surface != null) {
- runWithValidSurface()
+ runWithValidSurface(programIndex)
}
}
+ @Synchronized
+ fun changeProgram(programIndex: Int) {
+ emulationThread.join()
+ emulationThread = Thread({
+ Log.debug("[EmulationFragment] Starting emulation thread.")
+ NativeLibrary.run(gamePath, programIndex)
+ }, "NativeEmulation")
+ emulationThread.start()
+ }
+
// Surface callbacks
@Synchronized
fun newSurface(surface: Surface?) {
@@ -931,7 +971,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
- private fun runWithValidSurface() {
+ private fun runWithValidSurface(programIndex: Int = 0) {
NativeLibrary.surfaceChanged(surface)
if (!emulationCanStart.invoke()) {
return
@@ -939,9 +979,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
when (state) {
State.STOPPED -> {
- val emulationThread = Thread({
+ emulationThread = Thread({
Log.debug("[EmulationFragment] Starting emulation thread.")
- NativeLibrary.run(gamePath)
+ NativeLibrary.run(gamePath, programIndex)
}, "NativeEmulation")
emulationThread.start()
}
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() {
cancellable = true
) { progressCallback, _ ->
val result = NativeLibrary.verifyInstalledContents(progressCallback)
- return@newInstance if (result.isEmpty()) {
+ return@newInstance if (progressCallback.invoke(100, 100)) {
+ // Invoke the progress callback to check if the process was cancelled
+ MessageDialogFragment.newInstance(
+ titleId = R.string.verify_no_result,
+ descriptionId = R.string.verify_no_result_description
+ )
+ } else if (result.isEmpty()) {
MessageDialogFragment.newInstance(
titleId = R.string.verify_success,
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() {
val isEmulationStopping: StateFlow<Boolean> get() = _isEmulationStopping
private val _isEmulationStopping = MutableStateFlow(false)
+ private val _emulationStopped = MutableStateFlow(false)
+ val emulationStopped = _emulationStopped.asStateFlow()
+
+ private val _programChanged = MutableStateFlow(-1)
+ val programChanged = _programChanged.asStateFlow()
+
val shaderProgress: StateFlow<Int> get() = _shaderProgress
private val _shaderProgress = MutableStateFlow(0)
@@ -35,6 +41,17 @@ class EmulationViewModel : ViewModel() {
_isEmulationStopping.value = value
}
+ fun setEmulationStopped(value: Boolean) {
+ if (value) {
+ _emulationStarted.value = false
+ }
+ _emulationStopped.value = value
+ }
+
+ fun setProgramChanged(programIndex: Int) {
+ _programChanged.value = programIndex
+ }
+
fun setShaderProgress(progress: Int) {
_shaderProgress.value = progress
}
@@ -56,20 +73,4 @@ class EmulationViewModel : ViewModel() {
fun setDrawerOpen(value: Boolean) {
_drawerOpen.value = value
}
-
- fun clear() {
- setEmulationStarted(false)
- setIsEmulationStopping(false)
- setShaderProgress(0)
- setTotalShaders(0)
- setShaderMessage("")
- }
-
- companion object {
- const val KEY_EMULATION_STARTED = "EmulationStarted"
- const val KEY_IS_EMULATION_STOPPING = "IsEmulationStarting"
- const val KEY_SHADER_PROGRESS = "ShaderProgress"
- const val KEY_TOTAL_SHADERS = "TotalShaders"
- const val KEY_SHADER_MESSAGE = "ShaderMessage"
- }
}
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 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include <core/core.h>
-#include <core/file_sys/mode.h>
-#include <core/file_sys/patch_manager.h>
-#include <core/loader/nro.h>
-#include <jni.h>
+#include "core/core.h"
+#include "core/file_sys/fs_filesystem.h"
+#include "core/file_sys/patch_manager.h"
#include "core/loader/loader.h"
+#include "core/loader/nro.h"
+#include "jni.h"
#include "jni/android_common/android_common.h"
#include "native.h"
@@ -79,7 +79,7 @@ extern "C" {
jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj,
jstring jpath) {
const auto file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
- GetJString(env, jpath), FileSys::Mode::Read);
+ GetJString(env, jpath), FileSys::OpenMode::Read);
if (!file) {
return false;
}
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;
static jmethodID s_disk_cache_load_progress;
static jmethodID s_on_emulation_started;
static jmethodID s_on_emulation_stopped;
+static jmethodID s_on_program_changed;
static jclass s_game_class;
static jmethodID s_game_constructor;
@@ -123,6 +124,10 @@ jmethodID GetOnEmulationStopped() {
return s_on_emulation_stopped;
}
+jmethodID GetOnProgramChanged() {
+ return s_on_program_changed;
+}
+
jclass GetGameClass() {
return s_game_class;
}
@@ -306,6 +311,8 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
s_on_emulation_stopped =
env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
+ s_on_program_changed =
+ env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V");
const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
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();
jmethodID GetDiskCacheLoadProgress();
jmethodID GetOnEmulationStarted();
jmethodID GetOnEmulationStopped();
+jmethodID GetOnProgramChanged();
jclass GetGameClass();
jmethodID 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 @@
#include "core/crypto/key_manager.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/content_archive.h"
+#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/submission_package.h"
-#include "core/file_sys/vfs.h"
-#include "core/file_sys/vfs_real.h"
+#include "core/file_sys/vfs/vfs.h"
+#include "core/file_sys/vfs/vfs_real.h"
#include "core/frontend/applets/cabinet.h"
#include "core/frontend/applets/controller.h"
#include "core/frontend/applets/error.h"
@@ -154,7 +155,7 @@ void EmulationSession::SurfaceChanged() {
}
void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath) {
- const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read);
+ const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::OpenMode::Read);
if (!file) {
return;
}
@@ -207,7 +208,8 @@ void EmulationSession::InitializeSystem(bool reload) {
m_system.GetFileSystemController().CreateFactories(*m_vfs);
}
-Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath) {
+Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath,
+ const std::size_t program_index) {
std::scoped_lock lock(m_mutex);
// Create the render window.
@@ -237,7 +239,8 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
ConfigureFilesystemProvider(filepath);
// Load the ROM.
- m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath);
+ m_load_result =
+ m_system.Load(EmulationSession::GetInstance().Window(), filepath, 0, program_index);
if (m_load_result != Core::SystemResultStatus::Success) {
return m_load_result;
}
@@ -247,6 +250,12 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
m_system.GetCpuManager().OnGpuReady();
m_system.RegisterExitCallback([&] { HaltEmulation(); });
+ // Register an ExecuteProgram callback such that Core can execute a sub-program
+ m_system.RegisterExecuteProgramCallback([&](std::size_t program_index_) {
+ m_next_program_index = program_index_;
+ EmulationSession::GetInstance().HaltEmulation();
+ });
+
OnEmulationStarted();
return Core::SystemResultStatus::Success;
}
@@ -254,6 +263,11 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
void EmulationSession::ShutdownEmulation() {
std::scoped_lock lock(m_mutex);
+ if (m_next_program_index != -1) {
+ ChangeProgram(m_next_program_index);
+ m_next_program_index = -1;
+ }
+
m_is_running = false;
// Unload user input.
@@ -401,6 +415,12 @@ void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) {
static_cast<jint>(result));
}
+void EmulationSession::ChangeProgram(std::size_t program_index) {
+ JNIEnv* env = IDCache::GetEnvForThread();
+ env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnProgramChanged(),
+ static_cast<jint>(program_index));
+}
+
u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) {
auto program_id_string = GetJString(env, jprogramId);
try {
@@ -410,7 +430,8 @@ u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) {
}
}
-static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
+static Core::SystemResultStatus RunEmulation(const std::string& filepath,
+ const size_t program_index = 0) {
MicroProfileOnThreadCreate("EmuThread");
SCOPE_EXIT({ MicroProfileShutdown(); });
@@ -423,7 +444,7 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); });
- jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath);
+ jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index);
if (result != Core::SystemResultStatus::Success) {
return result;
}
@@ -475,8 +496,8 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* en
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
std::string updatePath = GetJString(env, jupdatePath);
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
- EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(updatePath,
- FileSys::Mode::Read));
+ EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
+ updatePath, FileSys::OpenMode::Read));
for (const auto& item : nsp->GetNCAs()) {
for (const auto& nca_details : item.second) {
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
Settings::LogSettings();
}
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2(JNIEnv* env, jclass clazz,
- jstring j_path) {
+void Java_org_yuzu_yuzu_1emu_NativeLibrary_run(JNIEnv* env, jobject jobj, jstring j_path,
+ jint j_program_index) {
const std::string path = GetJString(env, j_path);
- const Core::SystemResultStatus result{RunEmulation(path)};
+ const Core::SystemResultStatus result{RunEmulation(path, j_program_index)};
if (result != Core::SystemResultStatus::Success) {
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
IDCache::GetExitEmulationActivity(), static_cast<int>(result));
@@ -719,7 +740,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv*
jobject instance) {
const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
auto vfs_nand_dir = EmulationSession::GetInstance().System().GetFilesystem()->OpenDirectory(
- Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read);
+ Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read);
const auto user_id = EmulationSession::GetInstance().System().GetProfileManager().GetUser(
static_cast<std::size_t>(0));
@@ -889,7 +910,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j
const auto nandDir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
auto vfsNandDir = system.GetFilesystem()->OpenDirectory(Common::FS::PathToUTF8String(nandDir),
- FileSys::Mode::Read);
+ FileSys::OpenMode::Read);
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
{}, 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:
const Core::PerfStatsResults& PerfStats();
void ConfigureFilesystemProvider(const std::string& filepath);
void InitializeSystem(bool reload);
- Core::SystemResultStatus InitializeEmulation(const std::string& filepath);
+ Core::SystemResultStatus InitializeEmulation(const std::string& filepath,
+ const std::size_t program_index = 0);
bool IsHandheldOnly();
void SetDeviceType([[maybe_unused]] int index, int type);
@@ -60,6 +61,7 @@ public:
private:
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
static void OnEmulationStopped(Core::SystemResultStatus result);
+ static void ChangeProgram(std::size_t program_index);
private:
// Window management
@@ -84,4 +86,7 @@ private:
// Synchronization
std::condition_variable_any m_cv;
mutable std::mutex m_mutex;
+
+ // Program index for next boot
+ std::atomic<s32> m_next_program_index = -1;
};
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 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
+ android:focusable="false"
android:defaultFocusHighlightEnabled="false"
- android:clickable="false">
+ android:clickable="false"
+ app:rippleColor="@android:color/transparent">
<androidx.constraintlayout.widget.ConstraintLayout
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 @@
#pragma once
+#include <algorithm>
#include <type_traits>
#include "bit_cast.h"
@@ -19,4 +20,21 @@ inline T WrappingAdd(T lhs, T rhs) {
return BitCast<T>(lhs_u + rhs_u);
}
+template <typename T>
+ requires(std::is_integral_v<T> && std::is_signed_v<T>)
+inline bool CanAddWithoutOverflow(T lhs, T rhs) {
+#ifdef _MSC_VER
+ if (lhs >= 0 && rhs >= 0) {
+ return WrappingAdd(lhs, rhs) >= std::max(lhs, rhs);
+ } else if (lhs < 0 && rhs < 0) {
+ return WrappingAdd(lhs, rhs) <= std::min(lhs, rhs);
+ } else {
+ return true;
+ }
+#else
+ T res;
+ return !__builtin_add_overflow(lhs, rhs, &res);
+#endif
+}
+
} // 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
cpu_manager.h
crypto/aes_util.cpp
crypto/aes_util.h
+ crypto/ctr_encryption_layer.cpp
+ crypto/ctr_encryption_layer.h
crypto/encryption_layer.cpp
crypto/encryption_layer.h
crypto/key_manager.cpp
crypto/key_manager.h
crypto/partition_data_manager.cpp
crypto/partition_data_manager.h
- crypto/ctr_encryption_layer.cpp
- crypto/ctr_encryption_layer.h
crypto/xts_encryption_layer.cpp
crypto/xts_encryption_layer.h
- debugger/debugger_interface.h
debugger/debugger.cpp
debugger/debugger.h
- debugger/gdbstub_arch.cpp
- debugger/gdbstub_arch.h
+ debugger/debugger_interface.h
debugger/gdbstub.cpp
debugger/gdbstub.h
+ debugger/gdbstub_arch.cpp
+ debugger/gdbstub_arch.h
device_memory_manager.h
device_memory_manager.inc
device_memory.cpp
device_memory.h
+ file_sys/bis_factory.cpp
+ file_sys/bis_factory.h
+ file_sys/card_image.cpp
+ file_sys/card_image.h
+ file_sys/common_funcs.h
+ file_sys/content_archive.cpp
+ file_sys/content_archive.h
+ file_sys/control_metadata.cpp
+ file_sys/control_metadata.h
+ file_sys/errors.h
+ file_sys/fs_directory.h
+ file_sys/fs_file.h
+ file_sys/fs_filesystem.h
+ file_sys/fs_memory_management.h
+ file_sys/fs_operate_range.h
+ file_sys/fs_path.h
+ file_sys/fs_path_utility.h
+ file_sys/fs_string_util.h
+ file_sys/fsmitm_romfsbuild.cpp
+ file_sys/fsmitm_romfsbuild.h
file_sys/fssystem/fs_i_storage.h
+ file_sys/fssystem/fs_types.h
file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp
file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.h
file_sys/fssystem/fssystem_aes_ctr_storage.cpp
@@ -83,25 +104,10 @@ add_library(core STATIC
file_sys/fssystem/fssystem_switch_storage.h
file_sys/fssystem/fssystem_utility.cpp
file_sys/fssystem/fssystem_utility.h
- file_sys/fssystem/fs_types.h
- file_sys/bis_factory.cpp
- file_sys/bis_factory.h
- file_sys/card_image.cpp
- file_sys/card_image.h
- file_sys/common_funcs.h
- file_sys/content_archive.cpp
- file_sys/content_archive.h
- file_sys/control_metadata.cpp
- file_sys/control_metadata.h
- file_sys/directory.h
- file_sys/errors.h
- file_sys/fsmitm_romfsbuild.cpp
- file_sys/fsmitm_romfsbuild.h
file_sys/ips_layer.cpp
file_sys/ips_layer.h
file_sys/kernel_executable.cpp
file_sys/kernel_executable.h
- file_sys/mode.h
file_sys/nca_metadata.cpp
file_sys/nca_metadata.h
file_sys/partition_filesystem.cpp
@@ -146,22 +152,22 @@ add_library(core STATIC
file_sys/system_archive/system_version.h
file_sys/system_archive/time_zone_binary.cpp
file_sys/system_archive/time_zone_binary.h
- file_sys/vfs.cpp
- file_sys/vfs.h
- file_sys/vfs_cached.cpp
- file_sys/vfs_cached.h
- file_sys/vfs_concat.cpp
- file_sys/vfs_concat.h
- file_sys/vfs_layered.cpp
- file_sys/vfs_layered.h
- file_sys/vfs_offset.cpp
- file_sys/vfs_offset.h
- file_sys/vfs_real.cpp
- file_sys/vfs_real.h
- file_sys/vfs_static.h
- file_sys/vfs_types.h
- file_sys/vfs_vector.cpp
- file_sys/vfs_vector.h
+ file_sys/vfs/vfs.cpp
+ file_sys/vfs/vfs.h
+ file_sys/vfs/vfs_cached.cpp
+ file_sys/vfs/vfs_cached.h
+ file_sys/vfs/vfs_concat.cpp
+ file_sys/vfs/vfs_concat.h
+ file_sys/vfs/vfs_layered.cpp
+ file_sys/vfs/vfs_layered.h
+ file_sys/vfs/vfs_offset.cpp
+ file_sys/vfs/vfs_offset.h
+ file_sys/vfs/vfs_real.cpp
+ file_sys/vfs/vfs_real.h
+ file_sys/vfs/vfs_static.h
+ file_sys/vfs/vfs_types.h
+ file_sys/vfs/vfs_vector.cpp
+ file_sys/vfs/vfs_vector.h
file_sys/xts_archive.cpp
file_sys/xts_archive.h
frontend/applets/cabinet.cpp
@@ -194,7 +200,6 @@ add_library(core STATIC
hle/kernel/board/nintendo/nx/secure_monitor.h
hle/kernel/code_set.cpp
hle/kernel/code_set.h
- hle/kernel/svc_results.h
hle/kernel/global_scheduler_context.cpp
hle/kernel/global_scheduler_context.h
hle/kernel/init/init_slab_setup.cpp
@@ -204,11 +209,11 @@ add_library(core STATIC
hle/kernel/k_address_arbiter.h
hle/kernel/k_address_space_info.cpp
hle/kernel/k_address_space_info.h
+ hle/kernel/k_affinity_mask.h
hle/kernel/k_auto_object.cpp
hle/kernel/k_auto_object.h
hle/kernel/k_auto_object_container.cpp
hle/kernel/k_auto_object_container.h
- hle/kernel/k_affinity_mask.h
hle/kernel/k_capabilities.cpp
hle/kernel/k_capabilities.h
hle/kernel/k_class_token.cpp
@@ -232,9 +237,9 @@ add_library(core STATIC
hle/kernel/k_event_info.h
hle/kernel/k_handle_table.cpp
hle/kernel/k_handle_table.h
- hle/kernel/k_hardware_timer_base.h
hle/kernel/k_hardware_timer.cpp
hle/kernel/k_hardware_timer.h
+ hle/kernel/k_hardware_timer_base.h
hle/kernel/k_interrupt_manager.cpp
hle/kernel/k_interrupt_manager.h
hle/kernel/k_light_client_session.cpp
@@ -261,10 +266,10 @@ add_library(core STATIC
hle/kernel/k_page_bitmap.h
hle/kernel/k_page_buffer.cpp
hle/kernel/k_page_buffer.h
- hle/kernel/k_page_heap.cpp
- hle/kernel/k_page_heap.h
hle/kernel/k_page_group.cpp
hle/kernel/k_page_group.h
+ hle/kernel/k_page_heap.cpp
+ hle/kernel/k_page_heap.h
hle/kernel/k_page_table.h
hle/kernel/k_page_table_base.cpp
hle/kernel/k_page_table_base.h
@@ -329,8 +334,6 @@ add_library(core STATIC
hle/kernel/slab_helpers.h
hle/kernel/svc.cpp
hle/kernel/svc.h
- hle/kernel/svc_common.h
- hle/kernel/svc_types.h
hle/kernel/svc/svc_activity.cpp
hle/kernel/svc/svc_address_arbiter.cpp
hle/kernel/svc/svc_address_translation.cpp
@@ -368,6 +371,9 @@ add_library(core STATIC
hle/kernel/svc/svc_thread_profiler.cpp
hle/kernel/svc/svc_tick.cpp
hle/kernel/svc/svc_transfer_memory.cpp
+ hle/kernel/svc_common.h
+ hle/kernel/svc_results.h
+ hle/kernel/svc_types.h
hle/result.h
hle/service/acc/acc.cpp
hle/service/acc/acc.h
@@ -486,14 +492,25 @@ add_library(core STATIC
hle/service/fatal/fatal_p.h
hle/service/fatal/fatal_u.cpp
hle/service/fatal/fatal_u.h
+ hle/service/fgm/fgm.cpp
+ hle/service/fgm/fgm.h
hle/service/filesystem/filesystem.cpp
hle/service/filesystem/filesystem.h
- hle/service/filesystem/fsp_ldr.cpp
- hle/service/filesystem/fsp_ldr.h
- hle/service/filesystem/fsp_pr.cpp
- hle/service/filesystem/fsp_pr.h
- hle/service/filesystem/fsp_srv.cpp
- hle/service/filesystem/fsp_srv.h
+ hle/service/filesystem/fsp/fs_i_directory.cpp
+ hle/service/filesystem/fsp/fs_i_directory.h
+ hle/service/filesystem/fsp/fs_i_file.cpp
+ hle/service/filesystem/fsp/fs_i_file.h
+ hle/service/filesystem/fsp/fs_i_filesystem.cpp
+ hle/service/filesystem/fsp/fs_i_filesystem.h
+ hle/service/filesystem/fsp/fs_i_storage.cpp
+ hle/service/filesystem/fsp/fs_i_storage.h
+ hle/service/filesystem/fsp/fsp_ldr.cpp
+ hle/service/filesystem/fsp/fsp_ldr.h
+ hle/service/filesystem/fsp/fsp_pr.cpp
+ hle/service/filesystem/fsp/fsp_pr.h
+ hle/service/filesystem/fsp/fsp_srv.cpp
+ hle/service/filesystem/fsp/fsp_srv.h
+ hle/service/filesystem/fsp/fsp_util.h
hle/service/filesystem/romfs_controller.cpp
hle/service/filesystem/romfs_controller.h
hle/service/filesystem/save_data_controller.cpp
@@ -551,13 +568,18 @@ add_library(core STATIC
hle/service/hid/irs.h
hle/service/hid/xcd.cpp
hle/service/hid/xcd.h
+ hle/service/hle_ipc.cpp
+ hle/service/hle_ipc.h
+ hle/service/ipc_helpers.h
+ hle/service/kernel_helpers.cpp
+ hle/service/kernel_helpers.h
hle/service/lbl/lbl.cpp
hle/service/lbl/lbl.h
hle/service/ldn/lan_discovery.cpp
hle/service/ldn/lan_discovery.h
- hle/service/ldn/ldn_results.h
hle/service/ldn/ldn.cpp
hle/service/ldn/ldn.h
+ hle/service/ldn/ldn_results.h
hle/service/ldn/ldn_types.h
hle/service/ldr/ldr.cpp
hle/service/ldr/ldr.h
@@ -565,16 +587,6 @@ add_library(core STATIC
hle/service/lm/lm.h
hle/service/mig/mig.cpp
hle/service/mig/mig.h
- hle/service/mii/types/char_info.cpp
- hle/service/mii/types/char_info.h
- hle/service/mii/types/core_data.cpp
- hle/service/mii/types/core_data.h
- hle/service/mii/types/raw_data.cpp
- hle/service/mii/types/raw_data.h
- hle/service/mii/types/store_data.cpp
- hle/service/mii/types/store_data.h
- hle/service/mii/types/ver3_store_data.cpp
- hle/service/mii/types/ver3_store_data.h
hle/service/mii/mii.cpp
hle/service/mii/mii.h
hle/service/mii/mii_database.cpp
@@ -586,10 +598,22 @@ add_library(core STATIC
hle/service/mii/mii_result.h
hle/service/mii/mii_types.h
hle/service/mii/mii_util.h
+ hle/service/mii/types/char_info.cpp
+ hle/service/mii/types/char_info.h
+ hle/service/mii/types/core_data.cpp
+ hle/service/mii/types/core_data.h
+ hle/service/mii/types/raw_data.cpp
+ hle/service/mii/types/raw_data.h
+ hle/service/mii/types/store_data.cpp
+ hle/service/mii/types/store_data.h
+ hle/service/mii/types/ver3_store_data.cpp
+ hle/service/mii/types/ver3_store_data.h
hle/service/mm/mm_u.cpp
hle/service/mm/mm_u.h
hle/service/mnpp/mnpp_app.cpp
hle/service/mnpp/mnpp_app.h
+ hle/service/mutex.cpp
+ hle/service/mutex.h
hle/service/ncm/ncm.cpp
hle/service/ncm/ncm.h
hle/service/nfc/common/amiibo_crypto.cpp
@@ -759,19 +783,12 @@ add_library(core STATIC
hle/service/ptm/ptm.h
hle/service/ptm/ts.cpp
hle/service/ptm/ts.h
- hle/service/hle_ipc.cpp
- hle/service/hle_ipc.h
- hle/service/ipc_helpers.h
- hle/service/kernel_helpers.cpp
- hle/service/kernel_helpers.h
- hle/service/mutex.cpp
- hle/service/mutex.h
+ hle/service/ro/ro.cpp
+ hle/service/ro/ro.h
hle/service/ro/ro_nro_utils.cpp
hle/service/ro/ro_nro_utils.h
hle/service/ro/ro_results.h
hle/service/ro/ro_types.h
- hle/service/ro/ro.cpp
- hle/service/ro/ro.h
hle/service/server_manager.cpp
hle/service/server_manager.h
hle/service/service.cpp
@@ -838,9 +855,9 @@ add_library(core STATIC
internal_network/network.h
internal_network/network_interface.cpp
internal_network/network_interface.h
- internal_network/sockets.h
internal_network/socket_proxy.cpp
internal_network/socket_proxy.h
+ internal_network/sockets.h
loader/deconstructed_rom_directory.cpp
loader/deconstructed_rom_directory.h
loader/kip.cpp
@@ -859,13 +876,13 @@ add_library(core STATIC
loader/nsp.h
loader/xci.cpp
loader/xci.h
+ memory.cpp
+ memory.h
memory/cheat_engine.cpp
memory/cheat_engine.h
memory/dmnt_cheat_types.h
memory/dmnt_cheat_vm.cpp
memory/dmnt_cheat_vm.h
- memory.cpp
- memory.h
perf_stats.cpp
perf_stats.h
precompiled_headers.h
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 @@
#include "core/debugger/debugger.h"
#include "core/device_memory.h"
#include "core/file_sys/bis_factory.h"
-#include "core/file_sys/mode.h"
+#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/savedata_factory.h"
-#include "core/file_sys/vfs_concat.h"
-#include "core/file_sys/vfs_real.h"
+#include "core/file_sys/vfs/vfs_concat.h"
+#include "core/file_sys/vfs/vfs_real.h"
#include "core/gpu_dirty_memory_manager.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_process.h"
@@ -102,7 +102,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
Common::SplitPath(path, &dir_name, &filename, nullptr);
if (filename == "00") {
- const auto dir = vfs->OpenDirectory(dir_name, FileSys::Mode::Read);
+ const auto dir = vfs->OpenDirectory(dir_name, FileSys::OpenMode::Read);
std::vector<FileSys::VirtualFile> concat;
for (u32 i = 0; i < 0x10; ++i) {
@@ -127,10 +127,10 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
}
if (Common::FS::IsDir(path)) {
- return vfs->OpenFile(path + "/main", FileSys::Mode::Read);
+ return vfs->OpenFile(path + "/main", FileSys::OpenMode::Read);
}
- return vfs->OpenFile(path, FileSys::Mode::Read);
+ return vfs->OpenFile(path, FileSys::OpenMode::Read);
}
struct System::Impl {
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 @@
#include <vector>
#include "common/common_types.h"
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
namespace Core::Frontend {
class EmuWindow;
diff --git a/src/core/crypto/aes_util.h b/src/core/crypto/aes_util.h
index a67ba5352..c2fd587a7 100644
--- a/src/core/crypto/aes_util.h
+++ b/src/core/crypto/aes_util.h
@@ -7,7 +7,7 @@
#include <span>
#include <type_traits>
#include "common/common_types.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace Core::Crypto {
diff --git a/src/core/crypto/encryption_layer.h b/src/core/crypto/encryption_layer.h
index d3082ba53..b53f0b12e 100644
--- a/src/core/crypto/encryption_layer.h
+++ b/src/core/crypto/encryption_layer.h
@@ -4,7 +4,7 @@
#pragma once
#include "common/common_types.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace Core::Crypto {
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index 97f5c8cea..4b45e72c4 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -21,9 +21,9 @@
#include "core/crypto/partition_data_manager.h"
#include "core/crypto/xts_encryption_layer.h"
#include "core/file_sys/kernel_executable.h"
-#include "core/file_sys/vfs.h"
-#include "core/file_sys/vfs_offset.h"
-#include "core/file_sys/vfs_vector.h"
+#include "core/file_sys/vfs/vfs.h"
+#include "core/file_sys/vfs/vfs_offset.h"
+#include "core/file_sys/vfs/vfs_vector.h"
#include "core/loader/loader.h"
using Common::AsArray;
diff --git a/src/core/crypto/partition_data_manager.h b/src/core/crypto/partition_data_manager.h
index 057a70683..4354a21e6 100644
--- a/src/core/crypto/partition_data_manager.h
+++ b/src/core/crypto/partition_data_manager.h
@@ -5,7 +5,7 @@
#include <vector>
#include "common/common_types.h"
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
namespace Core::Crypto {
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index c750c0da7..db667438e 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -4,9 +4,8 @@
#include <fmt/format.h>
#include "common/fs/path_util.h"
#include "core/file_sys/bis_factory.h"
-#include "core/file_sys/mode.h"
#include "core/file_sys/registered_cache.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
@@ -84,7 +83,7 @@ VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id,
VirtualFilesystem file_system) const {
auto& keys = Core::Crypto::KeyManager::Instance();
Core::Crypto::PartitionDataManager pdm{file_system->OpenDirectory(
- Common::FS::GetYuzuPathString(Common::FS::YuzuPath::NANDDir), Mode::Read)};
+ Common::FS::GetYuzuPathString(Common::FS::YuzuPath::NANDDir), OpenMode::Read)};
keys.PopulateFromPartitionData(pdm);
switch (id) {
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index 26f0c6e5e..23680b60c 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -6,7 +6,7 @@
#include <memory>
#include "common/common_types.h"
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
namespace FileSys {
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 8b9a4fc5a..0bcf40cf8 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -13,8 +13,8 @@
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/submission_package.h"
-#include "core/file_sys/vfs_offset.h"
-#include "core/file_sys/vfs_vector.h"
+#include "core/file_sys/vfs/vfs_offset.h"
+#include "core/file_sys/vfs/vfs_vector.h"
#include "core/loader/loader.h"
namespace FileSys {
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index 9886123e7..97871da4a 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -8,7 +8,7 @@
#include <vector>
#include "common/common_types.h"
#include "common/swap.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace Core::Crypto {
class KeyManager;
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 7d2f0abb8..285fe4db6 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -13,7 +13,7 @@
#include "core/crypto/key_manager.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/partition_filesystem.h"
-#include "core/file_sys/vfs_offset.h"
+#include "core/file_sys/vfs/vfs_offset.h"
#include "core/loader/loader.h"
#include "core/file_sys/fssystem/fssystem_compression_configuration.h"
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index af521d453..f68464eb0 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -13,7 +13,7 @@
#include "common/common_types.h"
#include "common/swap.h"
#include "core/crypto/key_manager.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace Loader {
enum class ResultStatus : u16;
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index 0697c29ae..f98594335 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -5,7 +5,7 @@
#include "common/string_util.h"
#include "common/swap.h"
#include "core/file_sys/control_metadata.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index c98efb00d..555b9d8f7 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -8,7 +8,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
namespace FileSys {
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h
deleted file mode 100644
index a853c00f3..000000000
--- a/src/core/file_sys/directory.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <cstddef>
-#include "common/common_funcs.h"
-#include "common/common_types.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
-
-namespace FileSys {
-
-enum class EntryType : u8 {
- Directory = 0,
- File = 1,
-};
-
-// Structure of a directory entry, from
-// http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry
-struct Entry {
- Entry(std::string_view view, EntryType entry_type, u64 entry_size)
- : type{entry_type}, file_size{entry_size} {
- const std::size_t copy_size = view.copy(filename, std::size(filename) - 1);
- filename[copy_size] = '\0';
- }
-
- char filename[0x301];
- INSERT_PADDING_BYTES(3);
- EntryType type;
- INSERT_PADDING_BYTES(3);
- u64 file_size;
-};
-static_assert(sizeof(Entry) == 0x310, "Directory Entry struct isn't exactly 0x310 bytes long!");
-static_assert(offsetof(Entry, type) == 0x304, "Wrong offset for type in Entry.");
-static_assert(offsetof(Entry, file_size) == 0x308, "Wrong offset for file_size in Entry.");
-
-} // namespace FileSys
diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h
index 2f5045a67..d4e0eb6f4 100644
--- a/src/core/file_sys/errors.h
+++ b/src/core/file_sys/errors.h
@@ -7,18 +7,13 @@
namespace FileSys {
-constexpr Result ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1};
-constexpr Result ERROR_PATH_ALREADY_EXISTS{ErrorModule::FS, 2};
-constexpr Result ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002};
-constexpr Result ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001};
-constexpr Result ERROR_OUT_OF_BOUNDS{ErrorModule::FS, 3005};
-constexpr Result ERROR_FAILED_MOUNT_ARCHIVE{ErrorModule::FS, 3223};
-constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::FS, 6001};
-constexpr Result ERROR_INVALID_OFFSET{ErrorModule::FS, 6061};
-constexpr Result ERROR_INVALID_SIZE{ErrorModule::FS, 6062};
-
+constexpr Result ResultPathNotFound{ErrorModule::FS, 1};
+constexpr Result ResultPathAlreadyExists{ErrorModule::FS, 2};
constexpr Result ResultUnsupportedSdkVersion{ErrorModule::FS, 50};
constexpr Result ResultPartitionNotFound{ErrorModule::FS, 1001};
+constexpr Result ResultTargetNotFound{ErrorModule::FS, 1002};
+constexpr Result ResultPortSdCardNoDevice{ErrorModule::FS, 2001};
+constexpr Result ResultNotImplemented{ErrorModule::FS, 3001};
constexpr Result ResultUnsupportedVersion{ErrorModule::FS, 3002};
constexpr Result ResultOutOfRange{ErrorModule::FS, 3005};
constexpr Result ResultAllocationMemoryFailedInFileSystemBuddyHeapA{ErrorModule::FS, 3294};
@@ -78,10 +73,21 @@ constexpr Result ResultUnexpectedInCompressedStorageA{ErrorModule::FS, 5324};
constexpr Result ResultUnexpectedInCompressedStorageB{ErrorModule::FS, 5325};
constexpr Result ResultUnexpectedInCompressedStorageC{ErrorModule::FS, 5326};
constexpr Result ResultUnexpectedInCompressedStorageD{ErrorModule::FS, 5327};
+constexpr Result ResultUnexpectedInPathA{ErrorModule::FS, 5328};
constexpr Result ResultInvalidArgument{ErrorModule::FS, 6001};
+constexpr Result ResultInvalidPath{ErrorModule::FS, 6002};
+constexpr Result ResultTooLongPath{ErrorModule::FS, 6003};
+constexpr Result ResultInvalidCharacter{ErrorModule::FS, 6004};
+constexpr Result ResultInvalidPathFormat{ErrorModule::FS, 6005};
+constexpr Result ResultDirectoryUnobtainable{ErrorModule::FS, 6006};
+constexpr Result ResultNotNormalized{ErrorModule::FS, 6007};
constexpr Result ResultInvalidOffset{ErrorModule::FS, 6061};
constexpr Result ResultInvalidSize{ErrorModule::FS, 6062};
constexpr Result ResultNullptrArgument{ErrorModule::FS, 6063};
+constexpr Result ResultInvalidOpenMode{ErrorModule::FS, 6072};
+constexpr Result ResultFileExtensionWithoutOpenModeAllowAppend{ErrorModule::FS, 6201};
+constexpr Result ResultReadNotPermitted{ErrorModule::FS, 6202};
+constexpr Result ResultWriteNotPermitted{ErrorModule::FS, 6203};
constexpr Result ResultUnsupportedSetSizeForIndirectStorage{ErrorModule::FS, 6325};
constexpr Result ResultUnsupportedWriteForCompressedStorage{ErrorModule::FS, 6387};
constexpr Result ResultUnsupportedOperateRangeForCompressedStorage{ErrorModule::FS, 6388};
diff --git a/src/core/file_sys/fs_directory.h b/src/core/file_sys/fs_directory.h
new file mode 100644
index 000000000..25c9cb18a
--- /dev/null
+++ b/src/core/file_sys/fs_directory.h
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+namespace FileSys {
+
+constexpr inline size_t EntryNameLengthMax = 0x300;
+
+struct DirectoryEntry {
+ DirectoryEntry(std::string_view view, s8 entry_type, u64 entry_size)
+ : type{entry_type}, file_size{static_cast<s64>(entry_size)} {
+ const std::size_t copy_size = view.copy(name, std::size(name) - 1);
+ name[copy_size] = '\0';
+ }
+
+ char name[EntryNameLengthMax + 1];
+ INSERT_PADDING_BYTES(3);
+ s8 type;
+ INSERT_PADDING_BYTES(3);
+ s64 file_size;
+};
+
+static_assert(sizeof(DirectoryEntry) == 0x310,
+ "Directory Entry struct isn't exactly 0x310 bytes long!");
+static_assert(offsetof(DirectoryEntry, type) == 0x304, "Wrong offset for type in Entry.");
+static_assert(offsetof(DirectoryEntry, file_size) == 0x308, "Wrong offset for file_size in Entry.");
+
+struct DirectoryHandle {
+ void* handle;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/fs_file.h b/src/core/file_sys/fs_file.h
new file mode 100644
index 000000000..4fb77e8db
--- /dev/null
+++ b/src/core/file_sys/fs_file.h
@@ -0,0 +1,65 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace FileSys {
+
+struct ReadOption {
+ u32 value;
+
+ static const ReadOption None;
+};
+
+enum ReadOptionFlag : u32 {
+ ReadOptionFlag_None = (0 << 0),
+};
+
+inline constexpr const ReadOption ReadOption::None = {ReadOptionFlag_None};
+
+inline constexpr bool operator==(const ReadOption& lhs, const ReadOption& rhs) {
+ return lhs.value == rhs.value;
+}
+
+inline constexpr bool operator!=(const ReadOption& lhs, const ReadOption& rhs) {
+ return !(lhs == rhs);
+}
+
+static_assert(sizeof(ReadOption) == sizeof(u32));
+
+enum WriteOptionFlag : u32 {
+ WriteOptionFlag_None = (0 << 0),
+ WriteOptionFlag_Flush = (1 << 0),
+};
+
+struct WriteOption {
+ u32 value;
+
+ constexpr inline bool HasFlushFlag() const {
+ return value & WriteOptionFlag_Flush;
+ }
+
+ static const WriteOption None;
+ static const WriteOption Flush;
+};
+
+inline constexpr const WriteOption WriteOption::None = {WriteOptionFlag_None};
+inline constexpr const WriteOption WriteOption::Flush = {WriteOptionFlag_Flush};
+
+inline constexpr bool operator==(const WriteOption& lhs, const WriteOption& rhs) {
+ return lhs.value == rhs.value;
+}
+
+inline constexpr bool operator!=(const WriteOption& lhs, const WriteOption& rhs) {
+ return !(lhs == rhs);
+}
+
+static_assert(sizeof(WriteOption) == sizeof(u32));
+
+struct FileHandle {
+ void* handle;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/fs_filesystem.h b/src/core/file_sys/fs_filesystem.h
new file mode 100644
index 000000000..7f237b7fa
--- /dev/null
+++ b/src/core/file_sys/fs_filesystem.h
@@ -0,0 +1,39 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace FileSys {
+
+enum class OpenMode : u32 {
+ Read = (1 << 0),
+ Write = (1 << 1),
+ AllowAppend = (1 << 2),
+
+ ReadWrite = (Read | Write),
+ All = (ReadWrite | AllowAppend),
+};
+DECLARE_ENUM_FLAG_OPERATORS(OpenMode)
+
+enum class OpenDirectoryMode : u64 {
+ Directory = (1 << 0),
+ File = (1 << 1),
+
+ All = (Directory | File),
+};
+DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode)
+
+enum class DirectoryEntryType : u8 {
+ Directory = 0,
+ File = 1,
+};
+
+enum class CreateOption : u8 {
+ None = (0 << 0),
+ BigFile = (1 << 0),
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/fs_memory_management.h b/src/core/file_sys/fs_memory_management.h
new file mode 100644
index 000000000..f03c6354b
--- /dev/null
+++ b/src/core/file_sys/fs_memory_management.h
@@ -0,0 +1,40 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <mutex>
+#include "common/alignment.h"
+
+namespace FileSys {
+
+constexpr size_t RequiredAlignment = alignof(u64);
+
+void* AllocateUnsafe(size_t size) {
+ // Allocate
+ void* const ptr = ::operator new(size, std::align_val_t{RequiredAlignment});
+
+ // Check alignment
+ ASSERT(Common::IsAligned(reinterpret_cast<uintptr_t>(ptr), RequiredAlignment));
+
+ // Return allocated pointer
+ return ptr;
+}
+
+void DeallocateUnsafe(void* ptr, size_t size) {
+ // Deallocate the pointer
+ ::operator delete(ptr, std::align_val_t{RequiredAlignment});
+}
+
+void* Allocate(size_t size) {
+ return AllocateUnsafe(size);
+}
+
+void Deallocate(void* ptr, size_t size) {
+ // If the pointer is non-null, deallocate it
+ if (ptr != nullptr) {
+ DeallocateUnsafe(ptr, size);
+ }
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/fs_operate_range.h b/src/core/file_sys/fs_operate_range.h
new file mode 100644
index 000000000..04ea64cc0
--- /dev/null
+++ b/src/core/file_sys/fs_operate_range.h
@@ -0,0 +1,22 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace FileSys {
+
+enum class OperationId : s64 {
+ FillZero = 0,
+ DestroySignature = 1,
+ Invalidate = 2,
+ QueryRange = 3,
+ QueryUnpreparedRange = 4,
+ QueryLazyLoadCompletionRate = 5,
+ SetLazyLoadPriority = 6,
+
+ ReadLazyLoadFileForciblyForDebug = 10001,
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/fs_path.h b/src/core/file_sys/fs_path.h
new file mode 100644
index 000000000..56ba08a6a
--- /dev/null
+++ b/src/core/file_sys/fs_path.h
@@ -0,0 +1,566 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/alignment.h"
+#include "common/common_funcs.h"
+#include "core/file_sys/errors.h"
+#include "core/file_sys/fs_memory_management.h"
+#include "core/file_sys/fs_path_utility.h"
+#include "core/file_sys/fs_string_util.h"
+#include "core/hle/result.h"
+
+namespace FileSys {
+class DirectoryPathParser;
+
+class Path {
+ YUZU_NON_COPYABLE(Path);
+ YUZU_NON_MOVEABLE(Path);
+
+private:
+ static constexpr const char* EmptyPath = "";
+ static constexpr size_t WriteBufferAlignmentLength = 8;
+
+private:
+ friend class DirectoryPathParser;
+
+public:
+ class WriteBuffer {
+ YUZU_NON_COPYABLE(WriteBuffer);
+
+ private:
+ char* m_buffer;
+ size_t m_length_and_is_normalized;
+
+ public:
+ constexpr WriteBuffer() : m_buffer(nullptr), m_length_and_is_normalized(0) {}
+
+ constexpr ~WriteBuffer() {
+ if (m_buffer != nullptr) {
+ Deallocate(m_buffer, this->GetLength());
+ this->ResetBuffer();
+ }
+ }
+
+ constexpr WriteBuffer(WriteBuffer&& rhs)
+ : m_buffer(rhs.m_buffer), m_length_and_is_normalized(rhs.m_length_and_is_normalized) {
+ rhs.ResetBuffer();
+ }
+
+ constexpr WriteBuffer& operator=(WriteBuffer&& rhs) {
+ if (m_buffer != nullptr) {
+ Deallocate(m_buffer, this->GetLength());
+ }
+
+ m_buffer = rhs.m_buffer;
+ m_length_and_is_normalized = rhs.m_length_and_is_normalized;
+
+ rhs.ResetBuffer();
+
+ return *this;
+ }
+
+ constexpr void ResetBuffer() {
+ m_buffer = nullptr;
+ this->SetLength(0);
+ }
+
+ constexpr char* Get() const {
+ return m_buffer;
+ }
+
+ constexpr size_t GetLength() const {
+ return m_length_and_is_normalized >> 1;
+ }
+
+ constexpr bool IsNormalized() const {
+ return static_cast<bool>(m_length_and_is_normalized & 1);
+ }
+
+ constexpr void SetNormalized() {
+ m_length_and_is_normalized |= static_cast<size_t>(1);
+ }
+
+ constexpr void SetNotNormalized() {
+ m_length_and_is_normalized &= ~static_cast<size_t>(1);
+ }
+
+ private:
+ constexpr WriteBuffer(char* buffer, size_t length)
+ : m_buffer(buffer), m_length_and_is_normalized(0) {
+ this->SetLength(length);
+ }
+
+ public:
+ static WriteBuffer Make(size_t length) {
+ if (void* alloc = Allocate(length); alloc != nullptr) {
+ return WriteBuffer(static_cast<char*>(alloc), length);
+ } else {
+ return WriteBuffer();
+ }
+ }
+
+ private:
+ constexpr void SetLength(size_t size) {
+ m_length_and_is_normalized = (m_length_and_is_normalized & 1) | (size << 1);
+ }
+ };
+
+private:
+ const char* m_str;
+ WriteBuffer m_write_buffer;
+
+public:
+ constexpr Path() : m_str(EmptyPath), m_write_buffer() {}
+
+ constexpr Path(const char* s) : m_str(s), m_write_buffer() {
+ m_write_buffer.SetNormalized();
+ }
+
+ constexpr ~Path() = default;
+
+ constexpr Result SetShallowBuffer(const char* buffer) {
+ // Check pre-conditions
+ ASSERT(m_write_buffer.GetLength() == 0);
+
+ // Check the buffer is valid
+ R_UNLESS(buffer != nullptr, ResultNullptrArgument);
+
+ // Set buffer
+ this->SetReadOnlyBuffer(buffer);
+
+ // Note that we're normalized
+ this->SetNormalized();
+
+ R_SUCCEED();
+ }
+
+ constexpr const char* GetString() const {
+ // Check pre-conditions
+ ASSERT(this->IsNormalized());
+
+ return m_str;
+ }
+
+ constexpr size_t GetLength() const {
+ if (std::is_constant_evaluated()) {
+ return Strlen(this->GetString());
+ } else {
+ return std::strlen(this->GetString());
+ }
+ }
+
+ constexpr bool IsEmpty() const {
+ return *m_str == '\x00';
+ }
+
+ constexpr bool IsMatchHead(const char* p, size_t len) const {
+ return Strncmp(this->GetString(), p, len) == 0;
+ }
+
+ Result Initialize(const Path& rhs) {
+ // Check the other path is normalized
+ const bool normalized = rhs.IsNormalized();
+ R_UNLESS(normalized, ResultNotNormalized);
+
+ // Allocate buffer for our path
+ const auto len = rhs.GetLength();
+ R_TRY(this->Preallocate(len + 1));
+
+ // Copy the path
+ const size_t copied = Strlcpy<char>(m_write_buffer.Get(), rhs.GetString(), len + 1);
+ R_UNLESS(copied == len, ResultUnexpectedInPathA);
+
+ // Set normalized
+ this->SetNormalized();
+ R_SUCCEED();
+ }
+
+ Result Initialize(const char* path, size_t len) {
+ // Check the path is valid
+ R_UNLESS(path != nullptr, ResultNullptrArgument);
+
+ // Initialize
+ R_TRY(this->InitializeImpl(path, len));
+
+ // Set not normalized
+ this->SetNotNormalized();
+
+ R_SUCCEED();
+ }
+
+ Result Initialize(const char* path) {
+ // Check the path is valid
+ R_UNLESS(path != nullptr, ResultNullptrArgument);
+
+ R_RETURN(this->Initialize(path, std::strlen(path)));
+ }
+
+ Result InitializeWithReplaceBackslash(const char* path) {
+ // Check the path is valid
+ R_UNLESS(path != nullptr, ResultNullptrArgument);
+
+ // Initialize
+ R_TRY(this->InitializeImpl(path, std::strlen(path)));
+
+ // Replace slashes as desired
+ if (const auto write_buffer_length = m_write_buffer.GetLength(); write_buffer_length > 1) {
+ Replace(m_write_buffer.Get(), write_buffer_length - 1, '\\', '/');
+ }
+
+ // Set not normalized
+ this->SetNotNormalized();
+
+ R_SUCCEED();
+ }
+
+ Result InitializeWithReplaceForwardSlashes(const char* path) {
+ // Check the path is valid
+ R_UNLESS(path != nullptr, ResultNullptrArgument);
+
+ // Initialize
+ R_TRY(this->InitializeImpl(path, std::strlen(path)));
+
+ // Replace slashes as desired
+ if (m_write_buffer.GetLength() > 1) {
+ if (auto* p = m_write_buffer.Get(); p[0] == '/' && p[1] == '/') {
+ p[0] = '\\';
+ p[1] = '\\';
+ }
+ }
+
+ // Set not normalized
+ this->SetNotNormalized();
+
+ R_SUCCEED();
+ }
+
+ Result InitializeWithNormalization(const char* path, size_t size) {
+ // Check the path is valid
+ R_UNLESS(path != nullptr, ResultNullptrArgument);
+
+ // Initialize
+ R_TRY(this->InitializeImpl(path, size));
+
+ // Set not normalized
+ this->SetNotNormalized();
+
+ // Perform normalization
+ PathFlags path_flags;
+ if (IsPathRelative(m_str)) {
+ path_flags.AllowRelativePath();
+ } else if (IsWindowsPath(m_str, true)) {
+ path_flags.AllowWindowsPath();
+ } else {
+ /* NOTE: In this case, Nintendo checks is normalized, then sets is normalized, then
+ * returns success. */
+ /* This seems like a bug. */
+ size_t dummy;
+ bool normalized;
+ R_TRY(PathFormatter::IsNormalized(std::addressof(normalized), std::addressof(dummy),
+ m_str));
+
+ this->SetNormalized();
+ R_SUCCEED();
+ }
+
+ // Normalize
+ R_TRY(this->Normalize(path_flags));
+
+ this->SetNormalized();
+ R_SUCCEED();
+ }
+
+ Result InitializeWithNormalization(const char* path) {
+ // Check the path is valid
+ R_UNLESS(path != nullptr, ResultNullptrArgument);
+
+ R_RETURN(this->InitializeWithNormalization(path, std::strlen(path)));
+ }
+
+ Result InitializeAsEmpty() {
+ // Clear our buffer
+ this->ClearBuffer();
+
+ // Set normalized
+ this->SetNormalized();
+
+ R_SUCCEED();
+ }
+
+ Result AppendChild(const char* child) {
+ // Check the path is valid
+ R_UNLESS(child != nullptr, ResultNullptrArgument);
+
+ // Basic checks. If we have a path and the child is empty, we have nothing to do
+ const char* c = child;
+ if (m_str[0]) {
+ // Skip an early separator
+ if (*c == '/') {
+ ++c;
+ }
+
+ R_SUCCEED_IF(*c == '\x00');
+ }
+
+ // If we don't have a string, we can just initialize
+ auto cur_len = std::strlen(m_str);
+ if (cur_len == 0) {
+ R_RETURN(this->Initialize(child));
+ }
+
+ // Remove a trailing separator
+ if (m_str[cur_len - 1] == '/' || m_str[cur_len - 1] == '\\') {
+ --cur_len;
+ }
+
+ // Get the child path's length
+ auto child_len = std::strlen(c);
+
+ // Reset our write buffer
+ WriteBuffer old_write_buffer;
+ if (m_write_buffer.Get() != nullptr) {
+ old_write_buffer = std::move(m_write_buffer);
+ this->ClearBuffer();
+ }
+
+ // Pre-allocate the new buffer
+ R_TRY(this->Preallocate(cur_len + 1 + child_len + 1));
+
+ // Get our write buffer
+ auto* dst = m_write_buffer.Get();
+ if (old_write_buffer.Get() != nullptr && cur_len > 0) {
+ Strlcpy<char>(dst, old_write_buffer.Get(), cur_len + 1);
+ }
+
+ // Add separator
+ dst[cur_len] = '/';
+
+ // Copy the child path
+ const size_t copied = Strlcpy<char>(dst + cur_len + 1, c, child_len + 1);
+ R_UNLESS(copied == child_len, ResultUnexpectedInPathA);
+
+ R_SUCCEED();
+ }
+
+ Result AppendChild(const Path& rhs) {
+ R_RETURN(this->AppendChild(rhs.GetString()));
+ }
+
+ Result Combine(const Path& parent, const Path& child) {
+ // Get the lengths
+ const auto p_len = parent.GetLength();
+ const auto c_len = child.GetLength();
+
+ // Allocate our buffer
+ R_TRY(this->Preallocate(p_len + c_len + 1));
+
+ // Initialize as parent
+ R_TRY(this->Initialize(parent));
+
+ // If we're empty, we can just initialize as child
+ if (this->IsEmpty()) {
+ R_TRY(this->Initialize(child));
+ } else {
+ // Otherwise, we should append the child
+ R_TRY(this->AppendChild(child));
+ }
+
+ R_SUCCEED();
+ }
+
+ Result RemoveChild() {
+ // If we don't have a write-buffer, ensure that we have one
+ if (m_write_buffer.Get() == nullptr) {
+ if (const auto len = std::strlen(m_str); len > 0) {
+ R_TRY(this->Preallocate(len));
+ Strlcpy<char>(m_write_buffer.Get(), m_str, len + 1);
+ }
+ }
+
+ // Check that it's possible for us to remove a child
+ auto* p = m_write_buffer.Get();
+ s32 len = std::strlen(p);
+ R_UNLESS(len != 1 || (p[0] != '/' && p[0] != '.'), ResultNotImplemented);
+
+ // Handle a trailing separator
+ if (len > 0 && (p[len - 1] == '\\' || p[len - 1] == '/')) {
+ --len;
+ }
+
+ // Remove the child path segment
+ while ((--len) >= 0 && p[len]) {
+ if (p[len] == '/' || p[len] == '\\') {
+ if (len > 0) {
+ p[len] = 0;
+ } else {
+ p[1] = 0;
+ len = 1;
+ }
+ break;
+ }
+ }
+
+ // Check that length remains > 0
+ R_UNLESS(len > 0, ResultNotImplemented);
+
+ R_SUCCEED();
+ }
+
+ Result Normalize(const PathFlags& flags) {
+ // If we're already normalized, nothing to do
+ R_SUCCEED_IF(this->IsNormalized());
+
+ // Check if we're normalized
+ bool normalized;
+ size_t dummy;
+ R_TRY(PathFormatter::IsNormalized(std::addressof(normalized), std::addressof(dummy), m_str,
+ flags));
+
+ // If we're not normalized, normalize
+ if (!normalized) {
+ // Determine necessary buffer length
+ auto len = m_write_buffer.GetLength();
+ if (flags.IsRelativePathAllowed() && IsPathRelative(m_str)) {
+ len += 2;
+ }
+ if (flags.IsWindowsPathAllowed() && IsWindowsPath(m_str, true)) {
+ len += 1;
+ }
+
+ // Allocate a new buffer
+ const size_t size = Common::AlignUp(len, WriteBufferAlignmentLength);
+ auto buf = WriteBuffer::Make(size);
+ R_UNLESS(buf.Get() != nullptr, ResultAllocationMemoryFailedMakeUnique);
+
+ // Normalize into it
+ R_TRY(PathFormatter::Normalize(buf.Get(), size, m_write_buffer.Get(),
+ m_write_buffer.GetLength(), flags));
+
+ // Set the normalized buffer as our buffer
+ this->SetModifiableBuffer(std::move(buf));
+ }
+
+ // Set normalized
+ this->SetNormalized();
+ R_SUCCEED();
+ }
+
+private:
+ void ClearBuffer() {
+ m_write_buffer.ResetBuffer();
+ m_str = EmptyPath;
+ }
+
+ void SetModifiableBuffer(WriteBuffer&& buffer) {
+ // Check pre-conditions
+ ASSERT(buffer.Get() != nullptr);
+ ASSERT(buffer.GetLength() > 0);
+ ASSERT(Common::IsAligned(buffer.GetLength(), WriteBufferAlignmentLength));
+
+ // Get whether we're normalized
+ if (m_write_buffer.IsNormalized()) {
+ buffer.SetNormalized();
+ } else {
+ buffer.SetNotNormalized();
+ }
+
+ // Set write buffer
+ m_write_buffer = std::move(buffer);
+ m_str = m_write_buffer.Get();
+ }
+
+ constexpr void SetReadOnlyBuffer(const char* buffer) {
+ m_str = buffer;
+ m_write_buffer.ResetBuffer();
+ }
+
+ Result Preallocate(size_t length) {
+ // Allocate additional space, if needed
+ if (length > m_write_buffer.GetLength()) {
+ // Allocate buffer
+ const size_t size = Common::AlignUp(length, WriteBufferAlignmentLength);
+ auto buf = WriteBuffer::Make(size);
+ R_UNLESS(buf.Get() != nullptr, ResultAllocationMemoryFailedMakeUnique);
+
+ // Set write buffer
+ this->SetModifiableBuffer(std::move(buf));
+ }
+
+ R_SUCCEED();
+ }
+
+ Result InitializeImpl(const char* path, size_t size) {
+ if (size > 0 && path[0]) {
+ // Pre allocate a buffer for the path
+ R_TRY(this->Preallocate(size + 1));
+
+ // Copy the path
+ const size_t copied = Strlcpy<char>(m_write_buffer.Get(), path, size + 1);
+ R_UNLESS(copied >= size, ResultUnexpectedInPathA);
+ } else {
+ // We can just clear the buffer
+ this->ClearBuffer();
+ }
+
+ R_SUCCEED();
+ }
+
+ constexpr char* GetWriteBuffer() {
+ ASSERT(m_write_buffer.Get() != nullptr);
+ return m_write_buffer.Get();
+ }
+
+ constexpr size_t GetWriteBufferLength() const {
+ return m_write_buffer.GetLength();
+ }
+
+ constexpr bool IsNormalized() const {
+ return m_write_buffer.IsNormalized();
+ }
+
+ constexpr void SetNormalized() {
+ m_write_buffer.SetNormalized();
+ }
+
+ constexpr void SetNotNormalized() {
+ m_write_buffer.SetNotNormalized();
+ }
+
+public:
+ bool operator==(const FileSys::Path& rhs) const {
+ return std::strcmp(this->GetString(), rhs.GetString()) == 0;
+ }
+ bool operator!=(const FileSys::Path& rhs) const {
+ return !(*this == rhs);
+ }
+ bool operator==(const char* p) const {
+ return std::strcmp(this->GetString(), p) == 0;
+ }
+ bool operator!=(const char* p) const {
+ return !(*this == p);
+ }
+};
+
+inline Result SetUpFixedPath(FileSys::Path* out, const char* s) {
+ // Verify the path is normalized
+ bool normalized;
+ size_t dummy;
+ R_TRY(PathNormalizer::IsNormalized(std::addressof(normalized), std::addressof(dummy), s));
+
+ R_UNLESS(normalized, ResultInvalidPathFormat);
+
+ // Set the fixed path
+ R_RETURN(out->SetShallowBuffer(s));
+}
+
+constexpr inline bool IsWindowsDriveRootPath(const FileSys::Path& path) {
+ const char* const str = path.GetString();
+ return IsWindowsDrive(str) &&
+ (str[2] == StringTraits::DirectorySeparator ||
+ str[2] == StringTraits::AlternateDirectorySeparator) &&
+ str[3] == StringTraits::NullTerminator;
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/fs_path_utility.h b/src/core/file_sys/fs_path_utility.h
new file mode 100644
index 000000000..e9011d065
--- /dev/null
+++ b/src/core/file_sys/fs_path_utility.h
@@ -0,0 +1,1239 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/assert.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/scope_exit.h"
+#include "core/file_sys/fs_directory.h"
+#include "core/file_sys/fs_memory_management.h"
+#include "core/file_sys/fs_string_util.h"
+#include "core/hle/result.h"
+
+namespace FileSys {
+
+constexpr inline size_t MountNameLengthMax = 15;
+
+namespace StringTraits {
+
+constexpr inline char DirectorySeparator = '/';
+constexpr inline char DriveSeparator = ':';
+constexpr inline char Dot = '.';
+constexpr inline char NullTerminator = '\x00';
+
+constexpr inline char AlternateDirectorySeparator = '\\';
+
+constexpr inline const char InvalidCharacters[6] = {':', '*', '?', '<', '>', '|'};
+constexpr inline const char InvalidCharactersForHostName[6] = {':', '*', '<', '>', '|', '$'};
+constexpr inline const char InvalidCharactersForMountName[5] = {'*', '?', '<', '>', '|'};
+
+namespace impl {
+
+template <const char* InvalidCharacterSet, size_t NumInvalidCharacters>
+consteval u64 MakeInvalidCharacterMask(size_t n) {
+ u64 mask = 0;
+ for (size_t i = 0; i < NumInvalidCharacters; ++i) {
+ if ((static_cast<u64>(InvalidCharacterSet[i]) >> 6) == n) {
+ mask |= static_cast<u64>(1) << (static_cast<u64>(InvalidCharacterSet[i]) & 0x3F);
+ }
+ }
+ return mask;
+}
+
+template <const char* InvalidCharacterSet, size_t NumInvalidCharacters>
+constexpr bool IsInvalidCharacterImpl(char c) {
+ constexpr u64 Masks[4] = {
+ MakeInvalidCharacterMask<InvalidCharacterSet, NumInvalidCharacters>(0),
+ MakeInvalidCharacterMask<InvalidCharacterSet, NumInvalidCharacters>(1),
+ MakeInvalidCharacterMask<InvalidCharacterSet, NumInvalidCharacters>(2),
+ MakeInvalidCharacterMask<InvalidCharacterSet, NumInvalidCharacters>(3)};
+
+ return (Masks[static_cast<u64>(c) >> 6] &
+ (static_cast<u64>(1) << (static_cast<u64>(c) & 0x3F))) != 0;
+}
+
+} // namespace impl
+
+constexpr bool IsInvalidCharacter(char c) {
+ return impl::IsInvalidCharacterImpl<InvalidCharacters, Common::Size(InvalidCharacters)>(c);
+}
+constexpr bool IsInvalidCharacterForHostName(char c) {
+ return impl::IsInvalidCharacterImpl<InvalidCharactersForHostName,
+ Common::Size(InvalidCharactersForHostName)>(c);
+}
+constexpr bool IsInvalidCharacterForMountName(char c) {
+ return impl::IsInvalidCharacterImpl<InvalidCharactersForMountName,
+ Common::Size(InvalidCharactersForMountName)>(c);
+}
+
+} // namespace StringTraits
+
+constexpr inline size_t WindowsDriveLength = 2;
+constexpr inline size_t UncPathPrefixLength = 2;
+constexpr inline size_t DosDevicePathPrefixLength = 4;
+
+class PathFlags {
+private:
+ static constexpr u32 WindowsPathFlag = (1 << 0);
+ static constexpr u32 RelativePathFlag = (1 << 1);
+ static constexpr u32 EmptyPathFlag = (1 << 2);
+ static constexpr u32 MountNameFlag = (1 << 3);
+ static constexpr u32 BackslashFlag = (1 << 4);
+ static constexpr u32 AllCharactersFlag = (1 << 5);
+
+private:
+ u32 m_value;
+
+public:
+ constexpr PathFlags() : m_value(0) { /* ... */
+ }
+
+#define DECLARE_PATH_FLAG_HANDLER(__WHICH__) \
+ constexpr bool Is##__WHICH__##Allowed() const { return (m_value & __WHICH__##Flag) != 0; } \
+ constexpr void Allow##__WHICH__() { m_value |= __WHICH__##Flag; }
+
+ DECLARE_PATH_FLAG_HANDLER(WindowsPath)
+ DECLARE_PATH_FLAG_HANDLER(RelativePath)
+ DECLARE_PATH_FLAG_HANDLER(EmptyPath)
+ DECLARE_PATH_FLAG_HANDLER(MountName)
+ DECLARE_PATH_FLAG_HANDLER(Backslash)
+ DECLARE_PATH_FLAG_HANDLER(AllCharacters)
+
+#undef DECLARE_PATH_FLAG_HANDLER
+};
+
+template <typename T>
+ requires(std::same_as<T, char> || std::same_as<T, wchar_t>)
+constexpr inline bool IsDosDevicePath(const T* path) {
+ ASSERT(path != nullptr);
+
+ using namespace StringTraits;
+
+ return path[0] == AlternateDirectorySeparator && path[1] == AlternateDirectorySeparator &&
+ (path[2] == Dot || path[2] == '?') &&
+ (path[3] == DirectorySeparator || path[3] == AlternateDirectorySeparator);
+}
+
+template <typename T>
+ requires(std::same_as<T, char> || std::same_as<T, wchar_t>)
+constexpr inline bool IsUncPath(const T* path, bool allow_forward_slash = true,
+ bool allow_back_slash = true) {
+ ASSERT(path != nullptr);
+
+ using namespace StringTraits;
+
+ return (allow_forward_slash && path[0] == DirectorySeparator &&
+ path[1] == DirectorySeparator) ||
+ (allow_back_slash && path[0] == AlternateDirectorySeparator &&
+ path[1] == AlternateDirectorySeparator);
+}
+
+constexpr inline bool IsWindowsDrive(const char* path) {
+ ASSERT(path != nullptr);
+
+ return (('a' <= path[0] && path[0] <= 'z') || ('A' <= path[0] && path[0] <= 'Z')) &&
+ path[1] == StringTraits::DriveSeparator;
+}
+
+constexpr inline bool IsWindowsPath(const char* path, bool allow_forward_slash_unc) {
+ return IsWindowsDrive(path) || IsDosDevicePath(path) ||
+ IsUncPath(path, allow_forward_slash_unc, true);
+}
+
+constexpr inline int GetWindowsSkipLength(const char* path) {
+ if (IsDosDevicePath(path)) {
+ return DosDevicePathPrefixLength;
+ } else if (IsWindowsDrive(path)) {
+ return WindowsDriveLength;
+ } else if (IsUncPath(path)) {
+ return UncPathPrefixLength;
+ } else {
+ return 0;
+ }
+}
+
+constexpr inline bool IsPathAbsolute(const char* path) {
+ return IsWindowsPath(path, false) || path[0] == StringTraits::DirectorySeparator;
+}
+
+constexpr inline bool IsPathRelative(const char* path) {
+ return path[0] && !IsPathAbsolute(path);
+}
+
+constexpr inline bool IsCurrentDirectory(const char* path) {
+ return path[0] == StringTraits::Dot &&
+ (path[1] == StringTraits::NullTerminator || path[1] == StringTraits::DirectorySeparator);
+}
+
+constexpr inline bool IsParentDirectory(const char* path) {
+ return path[0] == StringTraits::Dot && path[1] == StringTraits::Dot &&
+ (path[2] == StringTraits::NullTerminator || path[2] == StringTraits::DirectorySeparator);
+}
+
+constexpr inline bool IsPathStartWithCurrentDirectory(const char* path) {
+ return IsCurrentDirectory(path) || IsParentDirectory(path);
+}
+
+constexpr inline bool IsSubPath(const char* lhs, const char* rhs) {
+ // Check pre-conditions
+ ASSERT(lhs != nullptr);
+ ASSERT(rhs != nullptr);
+
+ // Import StringTraits names for current scope
+ using namespace StringTraits;
+
+ // Special case certain paths
+ if (IsUncPath(lhs) && !IsUncPath(rhs)) {
+ return false;
+ }
+ if (!IsUncPath(lhs) && IsUncPath(rhs)) {
+ return false;
+ }
+
+ if (lhs[0] == DirectorySeparator && lhs[1] == NullTerminator && rhs[0] == DirectorySeparator &&
+ rhs[1] != NullTerminator) {
+ return true;
+ }
+ if (rhs[0] == DirectorySeparator && rhs[1] == NullTerminator && lhs[0] == DirectorySeparator &&
+ lhs[1] != NullTerminator) {
+ return true;
+ }
+
+ // Check subpath
+ for (size_t i = 0; /* ... */; ++i) {
+ if (lhs[i] == NullTerminator) {
+ return rhs[i] == DirectorySeparator;
+ } else if (rhs[i] == NullTerminator) {
+ return lhs[i] == DirectorySeparator;
+ } else if (lhs[i] != rhs[i]) {
+ return false;
+ }
+ }
+}
+
+// Path utilities
+constexpr inline void Replace(char* dst, size_t dst_size, char old_char, char new_char) {
+ ASSERT(dst != nullptr);
+ for (char* cur = dst; cur < dst + dst_size && *cur; ++cur) {
+ if (*cur == old_char) {
+ *cur = new_char;
+ }
+ }
+}
+
+constexpr inline Result CheckUtf8(const char* s) {
+ // Check pre-conditions
+ ASSERT(s != nullptr);
+
+ // Iterate, checking for utf8-validity
+ while (*s) {
+ char utf8_buf[4] = {};
+
+ const auto pick_res = PickOutCharacterFromUtf8String(utf8_buf, std::addressof(s));
+ R_UNLESS(pick_res == CharacterEncodingResult_Success, ResultInvalidPathFormat);
+
+ u32 dummy;
+ const auto cvt_res = ConvertCharacterUtf8ToUtf32(std::addressof(dummy), utf8_buf);
+ R_UNLESS(cvt_res == CharacterEncodingResult_Success, ResultInvalidPathFormat);
+ }
+
+ R_SUCCEED();
+}
+
+// Path formatting
+class PathNormalizer {
+private:
+ enum class PathState {
+ Start,
+ Normal,
+ FirstSeparator,
+ Separator,
+ CurrentDir,
+ ParentDir,
+ };
+
+private:
+ static constexpr void ReplaceParentDirectoryPath(char* dst, const char* src) {
+ // Use StringTraits names for remainder of scope
+ using namespace StringTraits;
+
+ // Start with a dir-separator
+ dst[0] = DirectorySeparator;
+
+ auto i = 1;
+ while (src[i] != NullTerminator) {
+ if ((src[i - 1] == DirectorySeparator || src[i - 1] == AlternateDirectorySeparator) &&
+ src[i + 0] == Dot && src[i + 1] == Dot &&
+ (src[i + 2] == DirectorySeparator || src[i + 2] == AlternateDirectorySeparator)) {
+ dst[i - 1] = DirectorySeparator;
+ dst[i + 0] = Dot;
+ dst[i + 1] = Dot;
+ dst[i + 2] = DirectorySeparator;
+ i += 3;
+ } else {
+ if (src[i - 1] == AlternateDirectorySeparator && src[i + 0] == Dot &&
+ src[i + 1] == Dot && src[i + 2] == NullTerminator) {
+ dst[i - 1] = DirectorySeparator;
+ dst[i + 0] = Dot;
+ dst[i + 1] = Dot;
+ i += 2;
+ break;
+ }
+
+ dst[i] = src[i];
+ ++i;
+ }
+ }
+
+ dst[i] = StringTraits::NullTerminator;
+ }
+
+public:
+ static constexpr bool IsParentDirectoryPathReplacementNeeded(const char* path) {
+ // Use StringTraits names for remainder of scope
+ using namespace StringTraits;
+
+ if (path[0] != DirectorySeparator && path[0] != AlternateDirectorySeparator) {
+ return false;
+ }
+
+ // Check to find a parent reference using alternate separators
+ if (path[0] != NullTerminator && path[1] != NullTerminator && path[2] != NullTerminator) {
+ size_t i;
+ for (i = 0; path[i + 3] != NullTerminator; ++path) {
+ if (path[i + 1] != Dot || path[i + 2] != Dot) {
+ continue;
+ }
+
+ const char c0 = path[i + 0];
+ const char c3 = path[i + 3];
+
+ if (c0 == AlternateDirectorySeparator &&
+ (c3 == DirectorySeparator || c3 == AlternateDirectorySeparator ||
+ c3 == NullTerminator)) {
+ return true;
+ }
+
+ if (c3 == AlternateDirectorySeparator &&
+ (c0 == DirectorySeparator || c0 == AlternateDirectorySeparator)) {
+ return true;
+ }
+ }
+
+ if (path[i + 0] == AlternateDirectorySeparator && path[i + 1] == Dot &&
+ path[i + 2] == Dot /* && path[i + 3] == NullTerminator */) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ static constexpr Result IsNormalized(bool* out, size_t* out_len, const char* path,
+ bool allow_all_characters = false) {
+ // Use StringTraits names for remainder of scope
+ using namespace StringTraits;
+
+ // Parse the path
+ auto state = PathState::Start;
+ size_t len = 0;
+ while (path[len] != NullTerminator) {
+ // Get the current character
+ const char c = path[len++];
+
+ // Check the current character is valid
+ if (!allow_all_characters && state != PathState::Start) {
+ R_UNLESS(!IsInvalidCharacter(c), ResultInvalidCharacter);
+ }
+
+ // Process depending on current state
+ switch (state) {
+ // Import the PathState enums for convenience
+ using enum PathState;
+
+ case Start:
+ R_UNLESS(c == DirectorySeparator, ResultInvalidPathFormat);
+ state = FirstSeparator;
+ break;
+ case Normal:
+ if (c == DirectorySeparator) {
+ state = Separator;
+ }
+ break;
+ case FirstSeparator:
+ case Separator:
+ if (c == DirectorySeparator) {
+ *out = false;
+ R_SUCCEED();
+ }
+
+ if (c == Dot) {
+ state = CurrentDir;
+ } else {
+ state = Normal;
+ }
+ break;
+ case CurrentDir:
+ if (c == DirectorySeparator) {
+ *out = false;
+ R_SUCCEED();
+ }
+
+ if (c == Dot) {
+ state = ParentDir;
+ } else {
+ state = Normal;
+ }
+ break;
+ case ParentDir:
+ if (c == DirectorySeparator) {
+ *out = false;
+ R_SUCCEED();
+ }
+
+ state = Normal;
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+
+ // Check the final state
+ switch (state) {
+ // Import the PathState enums for convenience
+ using enum PathState;
+ case Start:
+ R_THROW(ResultInvalidPathFormat);
+ case Normal:
+ case FirstSeparator:
+ *out = true;
+ break;
+ case Separator:
+ case CurrentDir:
+ case ParentDir:
+ *out = false;
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+
+ // Set the output length
+ *out_len = len;
+ R_SUCCEED();
+ }
+
+ static Result Normalize(char* dst, size_t* out_len, const char* path, size_t max_out_size,
+ bool is_windows_path, bool is_drive_relative_path,
+ bool allow_all_characters = false) {
+ // Use StringTraits names for remainder of scope
+ using namespace StringTraits;
+
+ // Prepare to iterate
+ const char* cur_path = path;
+ size_t total_len = 0;
+
+ // If path begins with a separator, check that we're not drive relative
+ if (cur_path[0] != DirectorySeparator) {
+ R_UNLESS(is_drive_relative_path, ResultInvalidPathFormat);
+
+ dst[total_len++] = DirectorySeparator;
+ }
+
+ // We're going to need to do path replacement, potentially
+ char* replacement_path = nullptr;
+ size_t replacement_path_size = 0;
+
+ SCOPE_EXIT({
+ if (replacement_path != nullptr) {
+ if (std::is_constant_evaluated()) {
+ delete[] replacement_path;
+ } else {
+ Deallocate(replacement_path, replacement_path_size);
+ }
+ }
+ });
+
+ // Perform path replacement, if necessary
+ if (IsParentDirectoryPathReplacementNeeded(cur_path)) {
+ if (std::is_constant_evaluated()) {
+ replacement_path_size = EntryNameLengthMax + 1;
+ replacement_path = new char[replacement_path_size];
+ } else {
+ replacement_path_size = EntryNameLengthMax + 1;
+ replacement_path = static_cast<char*>(Allocate(replacement_path_size));
+ }
+
+ ReplaceParentDirectoryPath(replacement_path, cur_path);
+
+ cur_path = replacement_path;
+ }
+
+ // Iterate, normalizing path components
+ bool skip_next_sep = false;
+ size_t i = 0;
+
+ while (cur_path[i] != NullTerminator) {
+ // Process a directory separator, if we run into one
+ if (cur_path[i] == DirectorySeparator) {
+ // Swallow separators
+ do {
+ ++i;
+ } while (cur_path[i] == DirectorySeparator);
+
+ // Check if we hit end of string
+ if (cur_path[i] == NullTerminator) {
+ break;
+ }
+
+ // If we aren't skipping the separator, write it, checking that we remain in bounds.
+ if (!skip_next_sep) {
+ if (total_len + 1 == max_out_size) {
+ dst[total_len] = NullTerminator;
+ *out_len = total_len;
+ R_THROW(ResultTooLongPath);
+ }
+
+ dst[total_len++] = DirectorySeparator;
+ }
+
+ // Don't skip the next separator
+ skip_next_sep = false;
+ }
+
+ // Get the length of the current directory component
+ size_t dir_len = 0;
+ while (cur_path[i + dir_len] != DirectorySeparator &&
+ cur_path[i + dir_len] != NullTerminator) {
+ // Check for validity
+ if (!allow_all_characters) {
+ R_UNLESS(!IsInvalidCharacter(cur_path[i + dir_len]), ResultInvalidCharacter);
+ }
+
+ ++dir_len;
+ }
+
+ // Handle the current dir component
+ if (IsCurrentDirectory(cur_path + i)) {
+ skip_next_sep = true;
+ } else if (IsParentDirectory(cur_path + i)) {
+ // We should have just written a separator
+ ASSERT(dst[total_len - 1] == DirectorySeparator);
+
+ // We should have started with a separator, for non-windows paths
+ if (!is_windows_path) {
+ ASSERT(dst[0] == DirectorySeparator);
+ }
+
+ // Remove the previous component
+ if (total_len == 1) {
+ R_UNLESS(is_windows_path, ResultDirectoryUnobtainable);
+
+ --total_len;
+ } else {
+ total_len -= 2;
+
+ do {
+ if (dst[total_len] == DirectorySeparator) {
+ break;
+ }
+ } while ((--total_len) != 0);
+ }
+
+ // We should be pointing to a directory separator, for non-windows paths
+ if (!is_windows_path) {
+ ASSERT(dst[total_len] == DirectorySeparator);
+ }
+
+ // We should remain in bounds
+ ASSERT(total_len < max_out_size);
+ } else {
+ // Copy, possibly truncating
+ if (total_len + dir_len + 1 > max_out_size) {
+ const size_t copy_len = max_out_size - (total_len + 1);
+
+ for (size_t j = 0; j < copy_len; ++j) {
+ dst[total_len++] = cur_path[i + j];
+ }
+
+ dst[total_len] = NullTerminator;
+ *out_len = total_len;
+ R_THROW(ResultTooLongPath);
+ }
+
+ for (size_t j = 0; j < dir_len; ++j) {
+ dst[total_len++] = cur_path[i + j];
+ }
+ }
+
+ // Advance past the current directory component
+ i += dir_len;
+ }
+
+ if (skip_next_sep) {
+ --total_len;
+ }
+
+ if (total_len == 0 && max_out_size != 0) {
+ total_len = 1;
+ dst[0] = DirectorySeparator;
+ }
+
+ // NOTE: Probable nintendo bug, as max_out_size must be at least total_len + 1 for the null
+ // terminator.
+ R_UNLESS(max_out_size >= total_len - 1, ResultTooLongPath);
+
+ dst[total_len] = NullTerminator;
+
+ // Check that the result path is normalized
+ bool is_normalized;
+ size_t dummy;
+ R_TRY(IsNormalized(std::addressof(is_normalized), std::addressof(dummy), dst,
+ allow_all_characters));
+
+ // Assert that the result path is normalized
+ ASSERT(is_normalized);
+
+ // Set the output length
+ *out_len = total_len;
+ R_SUCCEED();
+ }
+};
+
+class PathFormatter {
+private:
+ static constexpr Result CheckSharedName(const char* name, size_t len) {
+ // Use StringTraits names for remainder of scope
+ using namespace StringTraits;
+
+ if (len == 1) {
+ R_UNLESS(name[0] != Dot, ResultInvalidPathFormat);
+ } else if (len == 2) {
+ R_UNLESS(name[0] != Dot || name[1] != Dot, ResultInvalidPathFormat);
+ }
+
+ for (size_t i = 0; i < len; ++i) {
+ R_UNLESS(!IsInvalidCharacter(name[i]), ResultInvalidCharacter);
+ }
+
+ R_SUCCEED();
+ }
+
+ static constexpr Result CheckHostName(const char* name, size_t len) {
+ // Use StringTraits names for remainder of scope
+ using namespace StringTraits;
+
+ if (len == 2) {
+ R_UNLESS(name[0] != Dot || name[1] != Dot, ResultInvalidPathFormat);
+ }
+
+ for (size_t i = 0; i < len; ++i) {
+ R_UNLESS(!IsInvalidCharacterForHostName(name[i]), ResultInvalidCharacter);
+ }
+
+ R_SUCCEED();
+ }
+
+ static constexpr Result CheckInvalidBackslash(bool* out_contains_backslash, const char* path,
+ bool allow_backslash) {
+ // Use StringTraits names for remainder of scope
+ using namespace StringTraits;
+
+ // Default to no backslashes, so we can just write if we see one
+ *out_contains_backslash = false;
+
+ while (*path != NullTerminator) {
+ if (*(path++) == AlternateDirectorySeparator) {
+ *out_contains_backslash = true;
+
+ R_UNLESS(allow_backslash, ResultInvalidCharacter);
+ }
+ }
+
+ R_SUCCEED();
+ }
+
+public:
+ static constexpr Result CheckPathFormat(const char* path, const PathFlags& flags) {
+ bool normalized;
+ size_t len;
+ R_RETURN(IsNormalized(std::addressof(normalized), std::addressof(len), path, flags));
+ }
+
+ static constexpr Result SkipMountName(const char** out, size_t* out_len, const char* path) {
+ R_RETURN(ParseMountName(out, out_len, nullptr, 0, path));
+ }
+
+ static constexpr Result ParseMountName(const char** out, size_t* out_len, char* out_mount_name,
+ size_t out_mount_name_buffer_size, const char* path) {
+ // Check pre-conditions
+ ASSERT(path != nullptr);
+ ASSERT(out_len != nullptr);
+ ASSERT(out != nullptr);
+ ASSERT((out_mount_name == nullptr) == (out_mount_name_buffer_size == 0));
+
+ // Use StringTraits names for remainder of scope
+ using namespace StringTraits;
+
+ // Determine max mount length
+ const auto max_mount_len =
+ out_mount_name_buffer_size == 0
+ ? MountNameLengthMax + 1
+ : std::min(MountNameLengthMax + 1, out_mount_name_buffer_size);
+
+ // Parse the path until we see a drive separator
+ size_t mount_len = 0;
+ for (/* ... */; mount_len < max_mount_len && path[mount_len]; ++mount_len) {
+ const char c = path[mount_len];
+
+ // If we see a drive separator, advance, then we're done with the pre-drive separator
+ // part of the mount.
+ if (c == DriveSeparator) {
+ ++mount_len;
+ break;
+ }
+
+ // If we see a directory separator, we're not in a mount name
+ if (c == DirectorySeparator || c == AlternateDirectorySeparator) {
+ *out = path;
+ *out_len = 0;
+ R_SUCCEED();
+ }
+ }
+
+ // Check to be sure we're actually looking at a mount name
+ if (mount_len <= 2 || path[mount_len - 1] != DriveSeparator) {
+ *out = path;
+ *out_len = 0;
+ R_SUCCEED();
+ }
+
+ // Check that all characters in the mount name are allowable
+ for (size_t i = 0; i < mount_len; ++i) {
+ R_UNLESS(!IsInvalidCharacterForMountName(path[i]), ResultInvalidCharacter);
+ }
+
+ // Copy out the mount name
+ if (out_mount_name_buffer_size > 0) {
+ R_UNLESS(mount_len < out_mount_name_buffer_size, ResultTooLongPath);
+
+ for (size_t i = 0; i < mount_len; ++i) {
+ out_mount_name[i] = path[i];
+ }
+ out_mount_name[mount_len] = NullTerminator;
+ }
+
+ // Set the output
+ *out = path + mount_len;
+ *out_len = mount_len;
+ R_SUCCEED();
+ }
+
+ static constexpr Result SkipRelativeDotPath(const char** out, size_t* out_len,
+ const char* path) {
+ R_RETURN(ParseRelativeDotPath(out, out_len, nullptr, 0, path));
+ }
+
+ static constexpr Result ParseRelativeDotPath(const char** out, size_t* out_len,
+ char* out_relative,
+ size_t out_relative_buffer_size,
+ const char* path) {
+ // Check pre-conditions
+ ASSERT(path != nullptr);
+ ASSERT(out_len != nullptr);
+ ASSERT(out != nullptr);
+ ASSERT((out_relative == nullptr) == (out_relative_buffer_size == 0));
+
+ // Use StringTraits names for remainder of scope
+ using namespace StringTraits;
+
+ // Initialize the output buffer, if we have one
+ if (out_relative_buffer_size > 0) {
+ out_relative[0] = NullTerminator;
+ }
+
+ // Check if the path is relative
+ if (path[0] == Dot && (path[1] == NullTerminator || path[1] == DirectorySeparator ||
+ path[1] == AlternateDirectorySeparator)) {
+ if (out_relative_buffer_size > 0) {
+ R_UNLESS(out_relative_buffer_size >= 2, ResultTooLongPath);
+
+ out_relative[0] = Dot;
+ out_relative[1] = NullTerminator;
+ }
+
+ *out = path + 1;
+ *out_len = 1;
+ R_SUCCEED();
+ }
+
+ // Ensure the path isn't a parent directory
+ R_UNLESS(!(path[0] == Dot && path[1] == Dot), ResultDirectoryUnobtainable);
+
+ // There was no relative dot path
+ *out = path;
+ *out_len = 0;
+ R_SUCCEED();
+ }
+
+ static constexpr Result SkipWindowsPath(const char** out, size_t* out_len, bool* out_normalized,
+ const char* path, bool has_mount_name) {
+ // We're normalized if and only if the parsing doesn't throw ResultNotNormalized()
+ *out_normalized = true;
+
+ R_TRY_CATCH(ParseWindowsPath(out, out_len, nullptr, 0, path, has_mount_name)) {
+ R_CATCH(ResultNotNormalized) {
+ *out_normalized = false;
+ }
+ }
+ R_END_TRY_CATCH;
+ ON_RESULT_INCLUDED(ResultNotNormalized) {
+ *out_normalized = false;
+ };
+
+ R_SUCCEED();
+ }
+
+ static constexpr Result ParseWindowsPath(const char** out, size_t* out_len, char* out_win,
+ size_t out_win_buffer_size, const char* path,
+ bool has_mount_name) {
+ // Check pre-conditions
+ ASSERT(path != nullptr);
+ ASSERT(out_len != nullptr);
+ ASSERT(out != nullptr);
+ ASSERT((out_win == nullptr) == (out_win_buffer_size == 0));
+
+ // Use StringTraits names for remainder of scope
+ using namespace StringTraits;
+
+ // Initialize the output buffer, if we have one
+ if (out_win_buffer_size > 0) {
+ out_win[0] = NullTerminator;
+ }
+
+ // Handle path start
+ const char* cur_path = path;
+ if (has_mount_name && path[0] == DirectorySeparator) {
+ if (path[1] == AlternateDirectorySeparator && path[2] == AlternateDirectorySeparator) {
+ R_UNLESS(out_win_buffer_size > 0, ResultNotNormalized);
+
+ ++cur_path;
+ } else if (IsWindowsDrive(path + 1)) {
+ R_UNLESS(out_win_buffer_size > 0, ResultNotNormalized);
+
+ ++cur_path;
+ }
+ }
+
+ // Handle windows drive
+ if (IsWindowsDrive(cur_path)) {
+ // Parse up to separator
+ size_t win_path_len = WindowsDriveLength;
+ for (/* ... */; cur_path[win_path_len] != NullTerminator; ++win_path_len) {
+ R_UNLESS(!IsInvalidCharacter(cur_path[win_path_len]), ResultInvalidCharacter);
+
+ if (cur_path[win_path_len] == DirectorySeparator ||
+ cur_path[win_path_len] == AlternateDirectorySeparator) {
+ break;
+ }
+ }
+
+ // Ensure that we're normalized, if we're required to be
+ if (out_win_buffer_size == 0) {
+ for (size_t i = 0; i < win_path_len; ++i) {
+ R_UNLESS(cur_path[i] != AlternateDirectorySeparator, ResultNotNormalized);
+ }
+ } else {
+ // Ensure we can copy into the normalized buffer
+ R_UNLESS(win_path_len < out_win_buffer_size, ResultTooLongPath);
+
+ for (size_t i = 0; i < win_path_len; ++i) {
+ out_win[i] = cur_path[i];
+ }
+ out_win[win_path_len] = NullTerminator;
+
+ Replace(out_win, win_path_len, AlternateDirectorySeparator, DirectorySeparator);
+ }
+
+ *out = cur_path + win_path_len;
+ *out_len = win_path_len;
+ R_SUCCEED();
+ }
+
+ // Handle DOS device
+ if (IsDosDevicePath(cur_path)) {
+ size_t dos_prefix_len = DosDevicePathPrefixLength;
+
+ if (IsWindowsDrive(cur_path + dos_prefix_len)) {
+ dos_prefix_len += WindowsDriveLength;
+ } else {
+ --dos_prefix_len;
+ }
+
+ if (out_win_buffer_size > 0) {
+ // Ensure we can copy into the normalized buffer
+ R_UNLESS(dos_prefix_len < out_win_buffer_size, ResultTooLongPath);
+
+ for (size_t i = 0; i < dos_prefix_len; ++i) {
+ out_win[i] = cur_path[i];
+ }
+ out_win[dos_prefix_len] = NullTerminator;
+
+ Replace(out_win, dos_prefix_len, DirectorySeparator, AlternateDirectorySeparator);
+ }
+
+ *out = cur_path + dos_prefix_len;
+ *out_len = dos_prefix_len;
+ R_SUCCEED();
+ }
+
+ // Handle UNC path
+ if (IsUncPath(cur_path, false, true)) {
+ const char* final_path = cur_path;
+
+ R_UNLESS(cur_path[UncPathPrefixLength] != DirectorySeparator, ResultInvalidPathFormat);
+ R_UNLESS(cur_path[UncPathPrefixLength] != AlternateDirectorySeparator,
+ ResultInvalidPathFormat);
+
+ size_t cur_component_offset = 0;
+ size_t pos = UncPathPrefixLength;
+ for (/* ... */; cur_path[pos] != NullTerminator; ++pos) {
+ if (cur_path[pos] == DirectorySeparator ||
+ cur_path[pos] == AlternateDirectorySeparator) {
+ if (cur_component_offset != 0) {
+ R_TRY(CheckSharedName(cur_path + cur_component_offset,
+ pos - cur_component_offset));
+
+ final_path = cur_path + pos;
+ break;
+ }
+
+ R_UNLESS(cur_path[pos + 1] != DirectorySeparator, ResultInvalidPathFormat);
+ R_UNLESS(cur_path[pos + 1] != AlternateDirectorySeparator,
+ ResultInvalidPathFormat);
+
+ R_TRY(CheckHostName(cur_path + 2, pos - 2));
+
+ cur_component_offset = pos + 1;
+ }
+ }
+
+ R_UNLESS(cur_component_offset != pos, ResultInvalidPathFormat);
+
+ if (cur_component_offset != 0 && final_path == cur_path) {
+ R_TRY(CheckSharedName(cur_path + cur_component_offset, pos - cur_component_offset));
+
+ final_path = cur_path + pos;
+ }
+
+ size_t unc_prefix_len = final_path - cur_path;
+
+ // Ensure that we're normalized, if we're required to be
+ if (out_win_buffer_size == 0) {
+ for (size_t i = 0; i < unc_prefix_len; ++i) {
+ R_UNLESS(cur_path[i] != DirectorySeparator, ResultNotNormalized);
+ }
+ } else {
+ // Ensure we can copy into the normalized buffer
+ R_UNLESS(unc_prefix_len < out_win_buffer_size, ResultTooLongPath);
+
+ for (size_t i = 0; i < unc_prefix_len; ++i) {
+ out_win[i] = cur_path[i];
+ }
+ out_win[unc_prefix_len] = NullTerminator;
+
+ Replace(out_win, unc_prefix_len, DirectorySeparator, AlternateDirectorySeparator);
+ }
+
+ *out = cur_path + unc_prefix_len;
+ *out_len = unc_prefix_len;
+ R_SUCCEED();
+ }
+
+ // There's no windows path to parse
+ *out = path;
+ *out_len = 0;
+ R_SUCCEED();
+ }
+
+ static constexpr Result IsNormalized(bool* out, size_t* out_len, const char* path,
+ const PathFlags& flags = {}) {
+ // Ensure nothing is null
+ R_UNLESS(out != nullptr, ResultNullptrArgument);
+ R_UNLESS(out_len != nullptr, ResultNullptrArgument);
+ R_UNLESS(path != nullptr, ResultNullptrArgument);
+
+ // Verify that the path is valid utf-8
+ R_TRY(CheckUtf8(path));
+
+ // Use StringTraits names for remainder of scope
+ using namespace StringTraits;
+
+ // Handle the case where the path is empty
+ if (path[0] == NullTerminator) {
+ R_UNLESS(flags.IsEmptyPathAllowed(), ResultInvalidPathFormat);
+
+ *out = true;
+ *out_len = 0;
+ R_SUCCEED();
+ }
+
+ // All normalized paths start with a directory separator...unless they're windows paths,
+ // relative paths, or have mount names.
+ if (path[0] != DirectorySeparator) {
+ R_UNLESS(flags.IsWindowsPathAllowed() || flags.IsRelativePathAllowed() ||
+ flags.IsMountNameAllowed(),
+ ResultInvalidPathFormat);
+ }
+
+ // Check that the path is allowed to be a windows path, if it is
+ if (IsWindowsPath(path, false)) {
+ R_UNLESS(flags.IsWindowsPathAllowed(), ResultInvalidPathFormat);
+ }
+
+ // Skip past the mount name, if one is present
+ size_t total_len = 0;
+ size_t mount_name_len = 0;
+ R_TRY(SkipMountName(std::addressof(path), std::addressof(mount_name_len), path));
+
+ // If we had a mount name, check that that was allowed
+ if (mount_name_len > 0) {
+ R_UNLESS(flags.IsMountNameAllowed(), ResultInvalidPathFormat);
+
+ total_len += mount_name_len;
+ }
+
+ // Check that the path starts as a normalized path should
+ if (path[0] != DirectorySeparator && !IsPathStartWithCurrentDirectory(path) &&
+ !IsWindowsPath(path, false)) {
+ R_UNLESS(flags.IsRelativePathAllowed(), ResultInvalidPathFormat);
+ R_UNLESS(!IsInvalidCharacter(path[0]), ResultInvalidPathFormat);
+
+ *out = false;
+ R_SUCCEED();
+ }
+
+ // Process relative path
+ size_t relative_len = 0;
+ R_TRY(SkipRelativeDotPath(std::addressof(path), std::addressof(relative_len), path));
+
+ // If we have a relative path, check that was allowed
+ if (relative_len > 0) {
+ R_UNLESS(flags.IsRelativePathAllowed(), ResultInvalidPathFormat);
+
+ total_len += relative_len;
+
+ if (path[0] == NullTerminator) {
+ *out = true;
+ *out_len = total_len;
+ R_SUCCEED();
+ }
+ }
+
+ // Process windows path
+ size_t windows_len = 0;
+ bool normalized_win = false;
+ R_TRY(SkipWindowsPath(std::addressof(path), std::addressof(windows_len),
+ std::addressof(normalized_win), path, mount_name_len > 0));
+
+ // If the windows path wasn't normalized, we're not normalized
+ if (!normalized_win) {
+ R_UNLESS(flags.IsWindowsPathAllowed(), ResultInvalidPathFormat);
+
+ *out = false;
+ R_SUCCEED();
+ }
+
+ // If we had a windows path, check that was allowed
+ if (windows_len > 0) {
+ R_UNLESS(flags.IsWindowsPathAllowed(), ResultInvalidPathFormat);
+
+ total_len += windows_len;
+
+ // We can't have both a relative path and a windows path
+ R_UNLESS(relative_len == 0, ResultInvalidPathFormat);
+
+ // A path ending in a windows path isn't normalized
+ if (path[0] == NullTerminator) {
+ *out = false;
+ R_SUCCEED();
+ }
+
+ // Check that there are no windows directory separators in the path
+ for (size_t i = 0; path[i] != NullTerminator; ++i) {
+ if (path[i] == AlternateDirectorySeparator) {
+ *out = false;
+ R_SUCCEED();
+ }
+ }
+ }
+
+ // Check that parent directory replacement is not needed if backslashes are allowed
+ if (flags.IsBackslashAllowed() &&
+ PathNormalizer::IsParentDirectoryPathReplacementNeeded(path)) {
+ *out = false;
+ R_SUCCEED();
+ }
+
+ // Check that the backslash state is valid
+ bool is_backslash_contained = false;
+ R_TRY(CheckInvalidBackslash(std::addressof(is_backslash_contained), path,
+ flags.IsWindowsPathAllowed() || flags.IsBackslashAllowed()));
+
+ // Check that backslashes are contained only if allowed
+ if (is_backslash_contained && !flags.IsBackslashAllowed()) {
+ *out = false;
+ R_SUCCEED();
+ }
+
+ // Check that the final result path is normalized
+ size_t normal_len = 0;
+ R_TRY(PathNormalizer::IsNormalized(out, std::addressof(normal_len), path,
+ flags.IsAllCharactersAllowed()));
+
+ // Add the normal length
+ total_len += normal_len;
+
+ // Set the output length
+ *out_len = total_len;
+ R_SUCCEED();
+ }
+
+ static Result Normalize(char* dst, size_t dst_size, const char* path, size_t path_len,
+ const PathFlags& flags) {
+ // Use StringTraits names for remainder of scope
+ using namespace StringTraits;
+
+ // Prepare to iterate
+ const char* src = path;
+ size_t cur_pos = 0;
+ bool is_windows_path = false;
+
+ // Check if the path is empty
+ if (src[0] == NullTerminator) {
+ if (dst_size != 0) {
+ dst[0] = NullTerminator;
+ }
+
+ R_UNLESS(flags.IsEmptyPathAllowed(), ResultInvalidPathFormat);
+
+ R_SUCCEED();
+ }
+
+ // Handle a mount name
+ size_t mount_name_len = 0;
+ if (flags.IsMountNameAllowed()) {
+ R_TRY(ParseMountName(std::addressof(src), std::addressof(mount_name_len), dst + cur_pos,
+ dst_size - cur_pos, src));
+
+ cur_pos += mount_name_len;
+ }
+
+ // Handle a drive-relative prefix
+ bool is_drive_relative = false;
+ if (src[0] != DirectorySeparator && !IsPathStartWithCurrentDirectory(src) &&
+ !IsWindowsPath(src, false)) {
+ R_UNLESS(flags.IsRelativePathAllowed(), ResultInvalidPathFormat);
+ R_UNLESS(!IsInvalidCharacter(src[0]), ResultInvalidPathFormat);
+
+ dst[cur_pos++] = Dot;
+ is_drive_relative = true;
+ }
+
+ size_t relative_len = 0;
+ if (flags.IsRelativePathAllowed()) {
+ R_UNLESS(cur_pos < dst_size, ResultTooLongPath);
+
+ R_TRY(ParseRelativeDotPath(std::addressof(src), std::addressof(relative_len),
+ dst + cur_pos, dst_size - cur_pos, src));
+
+ cur_pos += relative_len;
+
+ if (src[0] == NullTerminator) {
+ R_UNLESS(cur_pos < dst_size, ResultTooLongPath);
+
+ dst[cur_pos] = NullTerminator;
+ R_SUCCEED();
+ }
+ }
+
+ // Handle a windows path
+ if (flags.IsWindowsPathAllowed()) {
+ const char* const orig = src;
+
+ R_UNLESS(cur_pos < dst_size, ResultTooLongPath);
+
+ size_t windows_len = 0;
+ R_TRY(ParseWindowsPath(std::addressof(src), std::addressof(windows_len), dst + cur_pos,
+ dst_size - cur_pos, src, mount_name_len != 0));
+
+ cur_pos += windows_len;
+
+ if (src[0] == NullTerminator) {
+ /* NOTE: Bug in original code here repeated, should be checking cur_pos + 2. */
+ R_UNLESS(cur_pos + 1 < dst_size, ResultTooLongPath);
+
+ dst[cur_pos + 0] = DirectorySeparator;
+ dst[cur_pos + 1] = NullTerminator;
+ R_SUCCEED();
+ }
+
+ if ((src - orig) > 0) {
+ is_windows_path = true;
+ }
+ }
+
+ // Check for invalid backslash
+ bool backslash_contained = false;
+ R_TRY(CheckInvalidBackslash(std::addressof(backslash_contained), src,
+ flags.IsWindowsPathAllowed() || flags.IsBackslashAllowed()));
+
+ // Handle backslash replacement as necessary
+ if (backslash_contained && flags.IsWindowsPathAllowed()) {
+ // Create a temporary buffer holding a slash-replaced version of the path.
+ // NOTE: Nintendo unnecessarily allocates and replaces here a fully copy of the path,
+ // despite having skipped some of it already.
+ const size_t replaced_src_len = path_len - (src - path);
+
+ char* replaced_src = nullptr;
+ SCOPE_EXIT({
+ if (replaced_src != nullptr) {
+ if (std::is_constant_evaluated()) {
+ delete[] replaced_src;
+ } else {
+ Deallocate(replaced_src, replaced_src_len);
+ }
+ }
+ });
+
+ if (std::is_constant_evaluated()) {
+ replaced_src = new char[replaced_src_len];
+ } else {
+ replaced_src = static_cast<char*>(Allocate(replaced_src_len));
+ }
+
+ Strlcpy<char>(replaced_src, src, replaced_src_len);
+
+ Replace(replaced_src, replaced_src_len, AlternateDirectorySeparator,
+ DirectorySeparator);
+
+ size_t dummy;
+ R_TRY(PathNormalizer::Normalize(dst + cur_pos, std::addressof(dummy), replaced_src,
+ dst_size - cur_pos, is_windows_path, is_drive_relative,
+ flags.IsAllCharactersAllowed()));
+ } else {
+ // We can just do normalization
+ size_t dummy;
+ R_TRY(PathNormalizer::Normalize(dst + cur_pos, std::addressof(dummy), src,
+ dst_size - cur_pos, is_windows_path, is_drive_relative,
+ flags.IsAllCharactersAllowed()));
+ }
+
+ R_SUCCEED();
+ }
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/fs_string_util.h b/src/core/file_sys/fs_string_util.h
new file mode 100644
index 000000000..874e09054
--- /dev/null
+++ b/src/core/file_sys/fs_string_util.h
@@ -0,0 +1,226 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/assert.h"
+
+namespace FileSys {
+
+template <typename T>
+constexpr int Strlen(const T* str) {
+ ASSERT(str != nullptr);
+
+ int length = 0;
+ while (*str++) {
+ ++length;
+ }
+
+ return length;
+}
+
+template <typename T>
+constexpr int Strnlen(const T* str, int count) {
+ ASSERT(str != nullptr);
+ ASSERT(count >= 0);
+
+ int length = 0;
+ while (count-- && *str++) {
+ ++length;
+ }
+
+ return length;
+}
+
+template <typename T>
+constexpr int Strncmp(const T* lhs, const T* rhs, int count) {
+ ASSERT(lhs != nullptr);
+ ASSERT(rhs != nullptr);
+ ASSERT(count >= 0);
+
+ if (count == 0) {
+ return 0;
+ }
+
+ T l, r;
+ do {
+ l = *(lhs++);
+ r = *(rhs++);
+ } while (l && (l == r) && (--count));
+
+ return l - r;
+}
+
+template <typename T>
+static constexpr int Strlcpy(T* dst, const T* src, int count) {
+ ASSERT(dst != nullptr);
+ ASSERT(src != nullptr);
+
+ const T* cur = src;
+ if (count > 0) {
+ while ((--count) && *cur) {
+ *(dst++) = *(cur++);
+ }
+ *dst = 0;
+ }
+
+ while (*cur) {
+ cur++;
+ }
+
+ return static_cast<int>(cur - src);
+}
+
+enum CharacterEncodingResult {
+ CharacterEncodingResult_Success = 0,
+ CharacterEncodingResult_InsufficientLength = 1,
+ CharacterEncodingResult_InvalidFormat = 2,
+};
+
+namespace impl {
+
+class CharacterEncodingHelper {
+public:
+ static constexpr int8_t Utf8NBytesInnerTable[0x100 + 1] = {
+ -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8,
+ };
+
+ static constexpr char GetUtf8NBytes(size_t i) {
+ return static_cast<char>(Utf8NBytesInnerTable[1 + i]);
+ }
+};
+
+} // namespace impl
+
+constexpr inline CharacterEncodingResult ConvertCharacterUtf8ToUtf32(u32* dst, const char* src) {
+ // Check pre-conditions
+ ASSERT(dst != nullptr);
+ ASSERT(src != nullptr);
+
+ // Perform the conversion
+ const auto* p = src;
+ switch (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[0]))) {
+ case 1:
+ *dst = static_cast<u32>(p[0]);
+ return CharacterEncodingResult_Success;
+ case 2:
+ if ((static_cast<u32>(p[0]) & 0x1E) != 0) {
+ if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) ==
+ 0) {
+ *dst = (static_cast<u32>(p[0] & 0x1F) << 6) | (static_cast<u32>(p[1] & 0x3F) << 0);
+ return CharacterEncodingResult_Success;
+ }
+ }
+ break;
+ case 3:
+ if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 &&
+ impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0) {
+ const u32 c = (static_cast<u32>(p[0] & 0xF) << 12) |
+ (static_cast<u32>(p[1] & 0x3F) << 6) |
+ (static_cast<u32>(p[2] & 0x3F) << 0);
+ if ((c & 0xF800) != 0 && (c & 0xF800) != 0xD800) {
+ *dst = c;
+ return CharacterEncodingResult_Success;
+ }
+ }
+ return CharacterEncodingResult_InvalidFormat;
+ case 4:
+ if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 &&
+ impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0 &&
+ impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[3])) == 0) {
+ const u32 c =
+ (static_cast<u32>(p[0] & 0x7) << 18) | (static_cast<u32>(p[1] & 0x3F) << 12) |
+ (static_cast<u32>(p[2] & 0x3F) << 6) | (static_cast<u32>(p[3] & 0x3F) << 0);
+ if (c >= 0x10000 && c < 0x110000) {
+ *dst = c;
+ return CharacterEncodingResult_Success;
+ }
+ }
+ return CharacterEncodingResult_InvalidFormat;
+ default:
+ break;
+ }
+
+ // We failed to convert
+ return CharacterEncodingResult_InvalidFormat;
+}
+
+constexpr inline CharacterEncodingResult PickOutCharacterFromUtf8String(char* dst,
+ const char** str) {
+ // Check pre-conditions
+ ASSERT(dst != nullptr);
+ ASSERT(str != nullptr);
+ ASSERT(*str != nullptr);
+
+ // Clear the output
+ dst[0] = 0;
+ dst[1] = 0;
+ dst[2] = 0;
+ dst[3] = 0;
+
+ // Perform the conversion
+ const auto* p = *str;
+ u32 c = static_cast<u32>(*p);
+ switch (impl::CharacterEncodingHelper::GetUtf8NBytes(c)) {
+ case 1:
+ dst[0] = (*str)[0];
+ ++(*str);
+ break;
+ case 2:
+ if ((p[0] & 0x1E) != 0) {
+ if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) ==
+ 0) {
+ c = (static_cast<u32>(p[0] & 0x1F) << 6) | (static_cast<u32>(p[1] & 0x3F) << 0);
+ dst[0] = (*str)[0];
+ dst[1] = (*str)[1];
+ (*str) += 2;
+ break;
+ }
+ }
+ return CharacterEncodingResult_InvalidFormat;
+ case 3:
+ if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 &&
+ impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0) {
+ c = (static_cast<u32>(p[0] & 0xF) << 12) | (static_cast<u32>(p[1] & 0x3F) << 6) |
+ (static_cast<u32>(p[2] & 0x3F) << 0);
+ if ((c & 0xF800) != 0 && (c & 0xF800) != 0xD800) {
+ dst[0] = (*str)[0];
+ dst[1] = (*str)[1];
+ dst[2] = (*str)[2];
+ (*str) += 3;
+ break;
+ }
+ }
+ return CharacterEncodingResult_InvalidFormat;
+ case 4:
+ if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 &&
+ impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0 &&
+ impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[3])) == 0) {
+ c = (static_cast<u32>(p[0] & 0x7) << 18) | (static_cast<u32>(p[1] & 0x3F) << 12) |
+ (static_cast<u32>(p[2] & 0x3F) << 6) | (static_cast<u32>(p[3] & 0x3F) << 0);
+ if (c >= 0x10000 && c < 0x110000) {
+ dst[0] = (*str)[0];
+ dst[1] = (*str)[1];
+ dst[2] = (*str)[2];
+ dst[3] = (*str)[3];
+ (*str) += 4;
+ break;
+ }
+ }
+ return CharacterEncodingResult_InvalidFormat;
+ default:
+ return CharacterEncodingResult_InvalidFormat;
+ }
+
+ return CharacterEncodingResult_Success;
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp
index dd9cca103..8807bbd0f 100644
--- a/src/core/file_sys/fsmitm_romfsbuild.cpp
+++ b/src/core/file_sys/fsmitm_romfsbuild.cpp
@@ -8,8 +8,8 @@
#include "common/assert.h"
#include "core/file_sys/fsmitm_romfsbuild.h"
#include "core/file_sys/ips_layer.h"
-#include "core/file_sys/vfs.h"
-#include "core/file_sys/vfs_vector.h"
+#include "core/file_sys/vfs/vfs.h"
+#include "core/file_sys/vfs/vfs_vector.h"
namespace FileSys {
diff --git a/src/core/file_sys/fsmitm_romfsbuild.h b/src/core/file_sys/fsmitm_romfsbuild.h
index f387c79f1..dd7ed4a7b 100644
--- a/src/core/file_sys/fsmitm_romfsbuild.h
+++ b/src/core/file_sys/fsmitm_romfsbuild.h
@@ -7,7 +7,7 @@
#include <memory>
#include <string>
#include "common/common_types.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
diff --git a/src/core/file_sys/fssystem/fs_i_storage.h b/src/core/file_sys/fssystem/fs_i_storage.h
index 416dd57b8..37336c9ae 100644
--- a/src/core/file_sys/fssystem/fs_i_storage.h
+++ b/src/core/file_sys/fssystem/fs_i_storage.h
@@ -5,7 +5,7 @@
#include "common/overflow.h"
#include "core/file_sys/errors.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
diff --git a/src/core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp b/src/core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp
index f25c95472..bc1cddbb0 100644
--- a/src/core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp
+++ b/src/core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp
@@ -4,7 +4,7 @@
#include "core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.h"
#include "core/file_sys/fssystem/fssystem_aes_ctr_storage.h"
#include "core/file_sys/fssystem/fssystem_nca_header.h"
-#include "core/file_sys/vfs_offset.h"
+#include "core/file_sys/vfs/vfs_offset.h"
namespace FileSys {
diff --git a/src/core/file_sys/fssystem/fssystem_aes_ctr_storage.h b/src/core/file_sys/fssystem/fssystem_aes_ctr_storage.h
index 339e49697..5abd93d33 100644
--- a/src/core/file_sys/fssystem/fssystem_aes_ctr_storage.h
+++ b/src/core/file_sys/fssystem/fssystem_aes_ctr_storage.h
@@ -9,7 +9,7 @@
#include "core/crypto/key_manager.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/fssystem/fs_i_storage.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
diff --git a/src/core/file_sys/fssystem/fssystem_bucket_tree.h b/src/core/file_sys/fssystem/fssystem_bucket_tree.h
index 46850cd48..3a5e21d1a 100644
--- a/src/core/file_sys/fssystem/fssystem_bucket_tree.h
+++ b/src/core/file_sys/fssystem/fssystem_bucket_tree.h
@@ -10,7 +10,7 @@
#include "common/common_types.h"
#include "common/literals.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
#include "core/hle/result.h"
namespace FileSys {
diff --git a/src/core/file_sys/fssystem/fssystem_compressed_storage.h b/src/core/file_sys/fssystem/fssystem_compressed_storage.h
index 33d93938e..74c98630e 100644
--- a/src/core/file_sys/fssystem/fssystem_compressed_storage.h
+++ b/src/core/file_sys/fssystem/fssystem_compressed_storage.h
@@ -10,7 +10,7 @@
#include "core/file_sys/fssystem/fssystem_bucket_tree.h"
#include "core/file_sys/fssystem/fssystem_compression_common.h"
#include "core/file_sys/fssystem/fssystem_pooled_buffer.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
diff --git a/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.cpp b/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.cpp
index 4a75b5308..39bb7b808 100644
--- a/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.cpp
+++ b/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.cpp
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h"
-#include "core/file_sys/vfs_offset.h"
+#include "core/file_sys/vfs/vfs_offset.h"
namespace FileSys {
diff --git a/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h b/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h
index 5cf697efe..bd129db47 100644
--- a/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h
+++ b/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h
@@ -8,7 +8,7 @@
#include "core/file_sys/fssystem/fs_types.h"
#include "core/file_sys/fssystem/fssystem_alignment_matching_storage.h"
#include "core/file_sys/fssystem/fssystem_integrity_verification_storage.h"
-#include "core/file_sys/vfs_offset.h"
+#include "core/file_sys/vfs/vfs_offset.h"
namespace FileSys {
diff --git a/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.h b/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.h
index 18df400af..41d3960b8 100644
--- a/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.h
+++ b/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.h
@@ -7,7 +7,7 @@
#include "core/file_sys/errors.h"
#include "core/file_sys/fssystem/fs_i_storage.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
diff --git a/src/core/file_sys/fssystem/fssystem_indirect_storage.h b/src/core/file_sys/fssystem/fssystem_indirect_storage.h
index 7854335bf..d4b95fd27 100644
--- a/src/core/file_sys/fssystem/fssystem_indirect_storage.h
+++ b/src/core/file_sys/fssystem/fssystem_indirect_storage.h
@@ -7,8 +7,8 @@
#include "core/file_sys/fssystem/fs_i_storage.h"
#include "core/file_sys/fssystem/fssystem_bucket_tree.h"
#include "core/file_sys/fssystem/fssystem_bucket_tree_template_impl.h"
-#include "core/file_sys/vfs.h"
-#include "core/file_sys/vfs_offset.h"
+#include "core/file_sys/vfs/vfs.h"
+#include "core/file_sys/vfs/vfs_offset.h"
namespace FileSys {
diff --git a/src/core/file_sys/fssystem/fssystem_integrity_romfs_storage.h b/src/core/file_sys/fssystem/fssystem_integrity_romfs_storage.h
index 5f8512b2a..240d1e388 100644
--- a/src/core/file_sys/fssystem/fssystem_integrity_romfs_storage.h
+++ b/src/core/file_sys/fssystem/fssystem_integrity_romfs_storage.h
@@ -5,7 +5,7 @@
#include "core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h"
#include "core/file_sys/fssystem/fssystem_nca_header.h"
-#include "core/file_sys/vfs_vector.h"
+#include "core/file_sys/vfs/vfs_vector.h"
namespace FileSys {
diff --git a/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp
index 0f5432203..ab5a7984e 100644
--- a/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp
+++ b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp
@@ -14,8 +14,8 @@
#include "core/file_sys/fssystem/fssystem_nca_file_system_driver.h"
#include "core/file_sys/fssystem/fssystem_sparse_storage.h"
#include "core/file_sys/fssystem/fssystem_switch_storage.h"
-#include "core/file_sys/vfs_offset.h"
-#include "core/file_sys/vfs_vector.h"
+#include "core/file_sys/vfs/vfs_offset.h"
+#include "core/file_sys/vfs/vfs_vector.h"
namespace FileSys {
diff --git a/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h
index 5771a21fc..5bc838de6 100644
--- a/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h
+++ b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h
@@ -5,7 +5,7 @@
#include "core/file_sys/fssystem/fssystem_compression_common.h"
#include "core/file_sys/fssystem/fssystem_nca_header.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
diff --git a/src/core/file_sys/fssystem/fssystem_nca_reader.cpp b/src/core/file_sys/fssystem/fssystem_nca_reader.cpp
index a3714ab37..08924e2a6 100644
--- a/src/core/file_sys/fssystem/fssystem_nca_reader.cpp
+++ b/src/core/file_sys/fssystem/fssystem_nca_reader.cpp
@@ -3,7 +3,7 @@
#include "core/file_sys/fssystem/fssystem_aes_xts_storage.h"
#include "core/file_sys/fssystem/fssystem_nca_file_system_driver.h"
-#include "core/file_sys/vfs_offset.h"
+#include "core/file_sys/vfs/vfs_offset.h"
namespace FileSys {
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index 31033634c..d1ac24072 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -12,7 +12,7 @@
#include "common/logging/log.h"
#include "common/swap.h"
#include "core/file_sys/ips_layer.h"
-#include "core/file_sys/vfs_vector.h"
+#include "core/file_sys/vfs/vfs_vector.h"
namespace FileSys {
diff --git a/src/core/file_sys/ips_layer.h b/src/core/file_sys/ips_layer.h
index f2717bae7..d81378e8a 100644
--- a/src/core/file_sys/ips_layer.h
+++ b/src/core/file_sys/ips_layer.h
@@ -8,7 +8,7 @@
#include <vector>
#include "common/common_types.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
diff --git a/src/core/file_sys/kernel_executable.cpp b/src/core/file_sys/kernel_executable.cpp
index 70c062f4c..b84492d30 100644
--- a/src/core/file_sys/kernel_executable.cpp
+++ b/src/core/file_sys/kernel_executable.cpp
@@ -5,7 +5,7 @@
#include "common/string_util.h"
#include "core/file_sys/kernel_executable.h"
-#include "core/file_sys/vfs_offset.h"
+#include "core/file_sys/vfs/vfs_offset.h"
#include "core/loader/loader.h"
namespace FileSys {
diff --git a/src/core/file_sys/kernel_executable.h b/src/core/file_sys/kernel_executable.h
index d5b9199b5..928ba2d99 100644
--- a/src/core/file_sys/kernel_executable.h
+++ b/src/core/file_sys/kernel_executable.h
@@ -10,7 +10,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
namespace Loader {
enum class ResultStatus : u16;
diff --git a/src/core/file_sys/mode.h b/src/core/file_sys/mode.h
deleted file mode 100644
index 9596ef4fd..000000000
--- a/src/core/file_sys/mode.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "common/common_funcs.h"
-#include "common/common_types.h"
-
-namespace FileSys {
-
-enum class Mode : u32 {
- Read = 1 << 0,
- Write = 1 << 1,
- ReadWrite = Read | Write,
- Append = 1 << 2,
- ReadAppend = Read | Append,
- WriteAppend = Write | Append,
- All = ReadWrite | Append,
-};
-
-DECLARE_ENUM_FLAG_OPERATORS(Mode)
-
-} // namespace FileSys
diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp
index f4a774675..9e855c50d 100644
--- a/src/core/file_sys/nca_metadata.cpp
+++ b/src/core/file_sys/nca_metadata.cpp
@@ -6,7 +6,7 @@
#include "common/logging/log.h"
#include "common/swap.h"
#include "core/file_sys/nca_metadata.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h
index 68e463b5f..6243b822a 100644
--- a/src/core/file_sys/nca_metadata.h
+++ b/src/core/file_sys/nca_metadata.h
@@ -8,7 +8,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
namespace FileSys {
class CNMT;
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp
index 2422cb51b..dd8de9d8a 100644
--- a/src/core/file_sys/partition_filesystem.cpp
+++ b/src/core/file_sys/partition_filesystem.cpp
@@ -9,7 +9,7 @@
#include "common/logging/log.h"
#include "core/file_sys/partition_filesystem.h"
-#include "core/file_sys/vfs_offset.h"
+#include "core/file_sys/vfs/vfs_offset.h"
#include "core/loader/loader.h"
namespace FileSys {
diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h
index b6e3a2b0c..777b9ead9 100644
--- a/src/core/file_sys/partition_filesystem.h
+++ b/src/core/file_sys/partition_filesystem.h
@@ -9,7 +9,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace Loader {
enum class ResultStatus : u16;
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 612122224..21d45235e 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -21,9 +21,9 @@
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
-#include "core/file_sys/vfs_cached.h"
-#include "core/file_sys/vfs_layered.h"
-#include "core/file_sys/vfs_vector.h"
+#include "core/file_sys/vfs/vfs_cached.h"
+#include "core/file_sys/vfs/vfs_layered.h"
+#include "core/file_sys/vfs/vfs_vector.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ns/language.h"
#include "core/hle/service/set/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 @@
#include <string>
#include "common/common_types.h"
#include "core/file_sys/nca_metadata.h"
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
#include "core/memory/dmnt_cheat_types.h"
namespace Core {
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index 539c7f7af..ae4e441c9 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -7,7 +7,7 @@
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "core/file_sys/program_metadata.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
#include "core/loader/loader.h"
namespace FileSys {
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index a53092b87..115e6d6cd 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -10,7 +10,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
namespace Loader {
enum class ResultStatus : u16;
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 1cc77ad14..85d30543c 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -17,7 +17,7 @@
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/submission_package.h"
-#include "core/file_sys/vfs_concat.h"
+#include "core/file_sys/vfs/vfs_concat.h"
#include "core/loader/loader.h"
namespace FileSys {
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index 64815a845..a7fc55673 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -11,7 +11,7 @@
#include <boost/container/flat_map.hpp>
#include "common/common_types.h"
#include "core/crypto/key_manager.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
class CNMT;
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp
index 6182598ae..a2b280973 100644
--- a/src/core/file_sys/romfs.cpp
+++ b/src/core/file_sys/romfs.cpp
@@ -9,11 +9,11 @@
#include "common/swap.h"
#include "core/file_sys/fsmitm_romfsbuild.h"
#include "core/file_sys/romfs.h"
-#include "core/file_sys/vfs.h"
-#include "core/file_sys/vfs_cached.h"
-#include "core/file_sys/vfs_concat.h"
-#include "core/file_sys/vfs_offset.h"
-#include "core/file_sys/vfs_vector.h"
+#include "core/file_sys/vfs/vfs.h"
+#include "core/file_sys/vfs/vfs_cached.h"
+#include "core/file_sys/vfs/vfs_concat.h"
+#include "core/file_sys/vfs/vfs_offset.h"
+#include "core/file_sys/vfs/vfs_vector.h"
namespace FileSys {
namespace {
diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h
index b75ff1aad..3c0aca291 100644
--- a/src/core/file_sys/romfs.h
+++ b/src/core/file_sys/romfs.h
@@ -3,7 +3,7 @@
#pragma once
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h
index e4809bc94..11ecfabdf 100644
--- a/src/core/file_sys/romfs_factory.h
+++ b/src/core/file_sys/romfs_factory.h
@@ -6,7 +6,7 @@
#include <memory>
#include "common/common_types.h"
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/result.h"
namespace Loader {
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 23196cd5f..cbf411a20 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -8,7 +8,7 @@
#include "common/uuid.h"
#include "core/core.h"
#include "core/file_sys/savedata_factory.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
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 @@
#include <string>
#include "common/common_funcs.h"
#include "common/common_types.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
#include "core/hle/result.h"
namespace Core {
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index d5158cd64..f3e2e21f4 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -4,7 +4,7 @@
#include <memory>
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/sdmc_factory.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
#include "core/file_sys/xts_archive.h"
namespace FileSys {
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h
index a445fdb16..ee69ccd07 100644
--- a/src/core/file_sys/sdmc_factory.h
+++ b/src/core/file_sys/sdmc_factory.h
@@ -4,7 +4,7 @@
#pragma once
#include <memory>
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/result.h"
namespace FileSys {
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index 915bffca9..935e9589d 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -9,7 +9,7 @@
#include <vector>
#include "common/common_types.h"
#include "core/file_sys/nca_metadata.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace Core::Crypto {
class KeyManager;
diff --git a/src/core/file_sys/system_archive/mii_model.cpp b/src/core/file_sys/system_archive/mii_model.cpp
index 5c87b42f8..a96cb2cd2 100644
--- a/src/core/file_sys/system_archive/mii_model.cpp
+++ b/src/core/file_sys/system_archive/mii_model.cpp
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/file_sys/system_archive/mii_model.h"
-#include "core/file_sys/vfs_vector.h"
+#include "core/file_sys/vfs/vfs_vector.h"
namespace FileSys::SystemArchive {
diff --git a/src/core/file_sys/system_archive/mii_model.h b/src/core/file_sys/system_archive/mii_model.h
index b6cbefe24..61723ed0d 100644
--- a/src/core/file_sys/system_archive/mii_model.h
+++ b/src/core/file_sys/system_archive/mii_model.h
@@ -3,7 +3,7 @@
#pragma once
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
namespace FileSys::SystemArchive {
diff --git a/src/core/file_sys/system_archive/ng_word.cpp b/src/core/file_sys/system_archive/ng_word.cpp
index 5cf6749da..1fa67877d 100644
--- a/src/core/file_sys/system_archive/ng_word.cpp
+++ b/src/core/file_sys/system_archive/ng_word.cpp
@@ -4,7 +4,7 @@
#include <fmt/format.h>
#include "common/common_types.h"
#include "core/file_sys/system_archive/ng_word.h"
-#include "core/file_sys/vfs_vector.h"
+#include "core/file_sys/vfs/vfs_vector.h"
namespace FileSys::SystemArchive {
diff --git a/src/core/file_sys/system_archive/ng_word.h b/src/core/file_sys/system_archive/ng_word.h
index 1d7b49532..51bcc3327 100644
--- a/src/core/file_sys/system_archive/ng_word.h
+++ b/src/core/file_sys/system_archive/ng_word.h
@@ -3,7 +3,7 @@
#pragma once
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
namespace FileSys::SystemArchive {
diff --git a/src/core/file_sys/system_archive/shared_font.cpp b/src/core/file_sys/system_archive/shared_font.cpp
index 3210583f0..deb52069d 100644
--- a/src/core/file_sys/system_archive/shared_font.cpp
+++ b/src/core/file_sys/system_archive/shared_font.cpp
@@ -8,7 +8,7 @@
#include "core/file_sys/system_archive/data/font_nintendo_extended.h"
#include "core/file_sys/system_archive/data/font_standard.h"
#include "core/file_sys/system_archive/shared_font.h"
-#include "core/file_sys/vfs_vector.h"
+#include "core/file_sys/vfs/vfs_vector.h"
#include "core/hle/service/ns/iplatform_service_manager.h"
namespace FileSys::SystemArchive {
diff --git a/src/core/file_sys/system_archive/shared_font.h b/src/core/file_sys/system_archive/shared_font.h
index d1cd1dc44..2d19fcde3 100644
--- a/src/core/file_sys/system_archive/shared_font.h
+++ b/src/core/file_sys/system_archive/shared_font.h
@@ -3,7 +3,7 @@
#pragma once
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
namespace FileSys::SystemArchive {
diff --git a/src/core/file_sys/system_archive/system_archive.h b/src/core/file_sys/system_archive/system_archive.h
index 02d9157bb..2f64247bc 100644
--- a/src/core/file_sys/system_archive/system_archive.h
+++ b/src/core/file_sys/system_archive/system_archive.h
@@ -4,7 +4,7 @@
#pragma once
#include "common/common_types.h"
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
namespace FileSys::SystemArchive {
diff --git a/src/core/file_sys/system_archive/system_version.cpp b/src/core/file_sys/system_archive/system_version.cpp
index e4751c2b4..5662004b7 100644
--- a/src/core/file_sys/system_archive/system_version.cpp
+++ b/src/core/file_sys/system_archive/system_version.cpp
@@ -3,7 +3,7 @@
#include "common/logging/log.h"
#include "core/file_sys/system_archive/system_version.h"
-#include "core/file_sys/vfs_vector.h"
+#include "core/file_sys/vfs/vfs_vector.h"
#include "core/hle/api_version.h"
namespace FileSys::SystemArchive {
diff --git a/src/core/file_sys/system_archive/system_version.h b/src/core/file_sys/system_archive/system_version.h
index 21b5514a9..e5f7b952e 100644
--- a/src/core/file_sys/system_archive/system_version.h
+++ b/src/core/file_sys/system_archive/system_version.h
@@ -4,7 +4,7 @@
#pragma once
#include <string>
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
namespace FileSys::SystemArchive {
diff --git a/src/core/file_sys/system_archive/time_zone_binary.cpp b/src/core/file_sys/system_archive/time_zone_binary.cpp
index 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 @@
#include "common/swap.h"
#include "core/file_sys/system_archive/time_zone_binary.h"
-#include "core/file_sys/vfs_vector.h"
+#include "core/file_sys/vfs/vfs_vector.h"
#include "nx_tzdb.h"
diff --git a/src/core/file_sys/system_archive/time_zone_binary.h b/src/core/file_sys/system_archive/time_zone_binary.h
index d0e1a4acd..e44fc5007 100644
--- a/src/core/file_sys/system_archive/time_zone_binary.h
+++ b/src/core/file_sys/system_archive/time_zone_binary.h
@@ -3,7 +3,7 @@
#pragma once
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
namespace FileSys::SystemArchive {
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs/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 @@
#include <numeric>
#include <string>
#include "common/fs/path_util.h"
-#include "core/file_sys/mode.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
@@ -36,12 +35,12 @@ VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
return VfsEntryType::None;
}
-VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
+VirtualFile VfsFilesystem::OpenFile(std::string_view path_, OpenMode perms) {
const auto path = Common::FS::SanitizePath(path_);
return root->GetFileRelative(path);
}
-VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
+VirtualFile VfsFilesystem::CreateFile(std::string_view path_, OpenMode perms) {
const auto path = Common::FS::SanitizePath(path_);
return root->CreateFileRelative(path);
}
@@ -54,17 +53,17 @@ VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view
if (Common::FS::GetParentPath(old_path) == Common::FS::GetParentPath(new_path)) {
if (!root->Copy(Common::FS::GetFilename(old_path), Common::FS::GetFilename(new_path)))
return nullptr;
- return OpenFile(new_path, Mode::ReadWrite);
+ return OpenFile(new_path, OpenMode::ReadWrite);
}
// Do it using RawCopy. Non-default impls are encouraged to optimize this.
- const auto old_file = OpenFile(old_path, Mode::Read);
+ const auto old_file = OpenFile(old_path, OpenMode::Read);
if (old_file == nullptr)
return nullptr;
- auto new_file = OpenFile(new_path, Mode::Read);
+ auto new_file = OpenFile(new_path, OpenMode::Read);
if (new_file != nullptr)
return nullptr;
- new_file = CreateFile(new_path, Mode::Write);
+ new_file = CreateFile(new_path, OpenMode::Write);
if (new_file == nullptr)
return nullptr;
if (!VfsRawCopy(old_file, new_file))
@@ -87,18 +86,18 @@ VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view
bool VfsFilesystem::DeleteFile(std::string_view path_) {
const auto path = Common::FS::SanitizePath(path_);
- auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write);
+ auto parent = OpenDirectory(Common::FS::GetParentPath(path), OpenMode::Write);
if (parent == nullptr)
return false;
return parent->DeleteFile(Common::FS::GetFilename(path));
}
-VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
+VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) {
const auto path = Common::FS::SanitizePath(path_);
return root->GetDirectoryRelative(path);
}
-VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
+VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
const auto path = Common::FS::SanitizePath(path_);
return root->CreateDirectoryRelative(path);
}
@@ -108,13 +107,13 @@ VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_
const auto new_path = Common::FS::SanitizePath(new_path_);
// Non-default impls are highly encouraged to provide a more optimized version of this.
- auto old_dir = OpenDirectory(old_path, Mode::Read);
+ auto old_dir = OpenDirectory(old_path, OpenMode::Read);
if (old_dir == nullptr)
return nullptr;
- auto new_dir = OpenDirectory(new_path, Mode::Read);
+ auto new_dir = OpenDirectory(new_path, OpenMode::Read);
if (new_dir != nullptr)
return nullptr;
- new_dir = CreateDirectory(new_path, Mode::Write);
+ new_dir = CreateDirectory(new_path, OpenMode::Write);
if (new_dir == nullptr)
return nullptr;
@@ -149,7 +148,7 @@ VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_v
bool VfsFilesystem::DeleteDirectory(std::string_view path_) {
const auto path = Common::FS::SanitizePath(path_);
- auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write);
+ auto parent = OpenDirectory(Common::FS::GetParentPath(path), OpenMode::Write);
if (parent == nullptr)
return false;
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 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/fs_filesystem.h"
+#include "core/file_sys/vfs/vfs_types.h"
namespace FileSys {
-enum class Mode : u32;
-
// An enumeration representing what can be at the end of a path in a VfsFilesystem
enum class VfsEntryType {
None,
@@ -49,9 +48,9 @@ public:
virtual VfsEntryType GetEntryType(std::string_view path) const;
// Opens the file with path relative to root. If it doesn't exist, returns nullptr.
- virtual VirtualFile OpenFile(std::string_view path, Mode perms);
+ virtual VirtualFile OpenFile(std::string_view path, OpenMode perms);
// Creates a new, empty file at path
- virtual VirtualFile CreateFile(std::string_view path, Mode perms);
+ virtual VirtualFile CreateFile(std::string_view path, OpenMode perms);
// Copies the file from old_path to new_path, returning the new file on success and nullptr on
// failure.
virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path);
@@ -62,9 +61,9 @@ public:
virtual bool DeleteFile(std::string_view path);
// Opens the directory with path relative to root. If it doesn't exist, returns nullptr.
- virtual VirtualDir OpenDirectory(std::string_view path, Mode perms);
+ virtual VirtualDir OpenDirectory(std::string_view path, OpenMode perms);
// Creates a new, empty directory at path
- virtual VirtualDir CreateDirectory(std::string_view path, Mode perms);
+ virtual VirtualDir CreateDirectory(std::string_view path, OpenMode perms);
// Copies the directory from old_path to new_path, returning the new directory on success and
// nullptr on failure.
virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path);
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 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include "core/file_sys/vfs_cached.h"
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_cached.h"
+#include "core/file_sys/vfs/vfs_types.h"
namespace FileSys {
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 @@
#include <string_view>
#include <vector>
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
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 @@
#include <utility>
#include "common/assert.h"
-#include "core/file_sys/vfs_concat.h"
-#include "core/file_sys/vfs_static.h"
+#include "core/file_sys/vfs/vfs_concat.h"
+#include "core/file_sys/vfs/vfs_static.h"
namespace FileSys {
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 @@
#include <compare>
#include <map>
#include <memory>
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
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 @@
#include <set>
#include <unordered_set>
#include <utility>
-#include "core/file_sys/vfs_layered.h"
+#include "core/file_sys/vfs/vfs_layered.h"
namespace FileSys {
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 @@
#pragma once
#include <memory>
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
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 @@
#include <algorithm>
#include <utility>
-#include "core/file_sys/vfs_offset.h"
+#include "core/file_sys/vfs/vfs_offset.h"
namespace FileSys {
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 @@
#include <memory>
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
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 @@
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
-#include "core/file_sys/vfs.h"
-#include "core/file_sys/vfs_real.h"
+#include "core/file_sys/vfs/vfs.h"
+#include "core/file_sys/vfs/vfs_real.h"
// For FileTimeStampRaw
#include <sys/stat.h>
@@ -20,6 +20,10 @@
#define stat _stat64
#endif
+#ifdef ANDROID
+#include "common/fs/fs_android.h"
+#endif
+
namespace FileSys {
namespace FS = Common::FS;
@@ -28,16 +32,14 @@ namespace {
constexpr size_t MaxOpenFiles = 512;
-constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(Mode mode) {
+constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(OpenMode mode) {
switch (mode) {
- case Mode::Read:
+ case OpenMode::Read:
return FS::FileAccessMode::Read;
- case Mode::Write:
- case Mode::ReadWrite:
- case Mode::Append:
- case Mode::ReadAppend:
- case Mode::WriteAppend:
- case Mode::All:
+ case OpenMode::Write:
+ case OpenMode::ReadWrite:
+ case OpenMode::AllowAppend:
+ case OpenMode::All:
return FS::FileAccessMode::ReadWrite;
default:
return {};
@@ -74,7 +76,7 @@ VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
}
VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::optional<u64> size,
- Mode perms) {
+ OpenMode perms) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
std::scoped_lock lk{list_lock};
@@ -98,11 +100,11 @@ VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::op
return file;
}
-VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
+VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, OpenMode perms) {
return OpenFileFromEntry(path_, {}, perms);
}
-VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
+VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, OpenMode perms) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
{
std::scoped_lock lk{list_lock};
@@ -145,7 +147,7 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
if (!FS::RenameFile(old_path, new_path)) {
return nullptr;
}
- return OpenFile(new_path, Mode::ReadWrite);
+ return OpenFile(new_path, OpenMode::ReadWrite);
}
bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
@@ -157,12 +159,12 @@ bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
return FS::RemoveFile(path);
}
-VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
+VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
}
-VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
+VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
if (!FS::CreateDirs(path)) {
return nullptr;
@@ -184,7 +186,7 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
if (!FS::RenameDir(old_path, new_path)) {
return nullptr;
}
- return OpenDirectory(new_path, Mode::ReadWrite);
+ return OpenDirectory(new_path, OpenMode::ReadWrite);
}
bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
@@ -193,7 +195,7 @@ bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
}
std::unique_lock<std::mutex> RealVfsFilesystem::RefreshReference(const std::string& path,
- Mode perms,
+ OpenMode perms,
FileReference& reference) {
std::unique_lock lk{list_lock};
@@ -266,7 +268,7 @@ void RealVfsFilesystem::RemoveReferenceFromListLocked(FileReference& reference)
}
RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_,
- const std::string& path_, Mode perms_, std::optional<u64> size_)
+ const std::string& path_, OpenMode perms_, std::optional<u64> size_)
: base(base_), reference(std::move(reference_)), path(path_),
parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponentsCopy(path_)),
size(size_), perms(perms_) {}
@@ -276,6 +278,11 @@ RealVfsFile::~RealVfsFile() {
}
std::string RealVfsFile::GetName() const {
+#ifdef ANDROID
+ if (path[0] != '/') {
+ return FS::Android::GetFilename(path);
+ }
+#endif
return path_components.empty() ? "" : std::string(path_components.back());
}
@@ -298,11 +305,11 @@ VirtualDir RealVfsFile::GetContainingDirectory() const {
}
bool RealVfsFile::IsWritable() const {
- return True(perms & Mode::Write);
+ return True(perms & OpenMode::Write);
}
bool RealVfsFile::IsReadable() const {
- return True(perms & Mode::Read);
+ return True(perms & OpenMode::Read);
}
std::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) {
template <>
std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const {
- if (perms == Mode::Append) {
+ if (perms == OpenMode::AllowAppend) {
return {};
}
@@ -353,7 +360,7 @@ std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>(
template <>
std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const {
- if (perms == Mode::Append) {
+ if (perms == OpenMode::AllowAppend) {
return {};
}
@@ -373,10 +380,11 @@ std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDi
return out;
}
-RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_)
+RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_,
+ OpenMode perms_)
: base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)),
path_components(FS::SplitPathComponentsCopy(path)), perms(perms_) {
- if (!FS::Exists(path) && True(perms & Mode::Write)) {
+ if (!FS::Exists(path) && True(perms & OpenMode::Write)) {
void(FS::CreateDirs(path));
}
}
@@ -456,11 +464,11 @@ std::vector<VirtualDir> RealVfsDirectory::GetSubdirectories() const {
}
bool RealVfsDirectory::IsWritable() const {
- return True(perms & Mode::Write);
+ return True(perms & OpenMode::Write);
}
bool RealVfsDirectory::IsReadable() const {
- return True(perms & Mode::Read);
+ return True(perms & OpenMode::Read);
}
std::string RealVfsDirectory::GetName() const {
@@ -507,7 +515,7 @@ std::string RealVfsDirectory::GetFullPath() const {
}
std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const {
- if (perms == Mode::Append) {
+ if (perms == OpenMode::AllowAppend) {
return {};
}
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 @@
#include <optional>
#include <string_view>
#include "common/intrusive_list.h"
-#include "core/file_sys/mode.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/fs_filesystem.h"
+#include "core/file_sys/vfs/vfs.h"
namespace Common::FS {
class IOFile;
@@ -33,13 +33,14 @@ public:
bool IsReadable() const override;
bool IsWritable() const override;
VfsEntryType GetEntryType(std::string_view path) const override;
- VirtualFile OpenFile(std::string_view path, Mode perms = Mode::Read) override;
- VirtualFile CreateFile(std::string_view path, Mode perms = Mode::ReadWrite) override;
+ VirtualFile OpenFile(std::string_view path, OpenMode perms = OpenMode::Read) override;
+ VirtualFile CreateFile(std::string_view path, OpenMode perms = OpenMode::ReadWrite) override;
VirtualFile CopyFile(std::string_view old_path, std::string_view new_path) override;
VirtualFile MoveFile(std::string_view old_path, std::string_view new_path) override;
bool DeleteFile(std::string_view path) override;
- VirtualDir OpenDirectory(std::string_view path, Mode perms = Mode::Read) override;
- VirtualDir CreateDirectory(std::string_view path, Mode perms = Mode::ReadWrite) override;
+ VirtualDir OpenDirectory(std::string_view path, OpenMode perms = OpenMode::Read) override;
+ VirtualDir CreateDirectory(std::string_view path,
+ OpenMode perms = OpenMode::ReadWrite) override;
VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path) override;
VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path) override;
bool DeleteDirectory(std::string_view path) override;
@@ -54,14 +55,14 @@ private:
private:
friend class RealVfsFile;
- std::unique_lock<std::mutex> RefreshReference(const std::string& path, Mode perms,
+ std::unique_lock<std::mutex> RefreshReference(const std::string& path, OpenMode perms,
FileReference& reference);
void DropReference(std::unique_ptr<FileReference>&& reference);
private:
friend class RealVfsDirectory;
VirtualFile OpenFileFromEntry(std::string_view path, std::optional<u64> size,
- Mode perms = Mode::Read);
+ OpenMode perms = OpenMode::Read);
private:
void EvictSingleReferenceLocked();
@@ -89,7 +90,8 @@ public:
private:
RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
- const std::string& path, Mode perms = Mode::Read, std::optional<u64> size = {});
+ const std::string& path, OpenMode perms = OpenMode::Read,
+ std::optional<u64> size = {});
RealVfsFilesystem& base;
std::unique_ptr<FileReference> reference;
@@ -97,7 +99,7 @@ private:
std::string parent_path;
std::vector<std::string> path_components;
std::optional<u64> size;
- Mode perms;
+ OpenMode perms;
};
// An implementation of VfsDirectory that represents a directory on the user's computer.
@@ -130,7 +132,8 @@ public:
std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
private:
- RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
+ RealVfsDirectory(RealVfsFilesystem& base, const std::string& path,
+ OpenMode perms = OpenMode::Read);
template <typename T, typename R>
std::vector<std::shared_ptr<R>> IterateEntries() const;
@@ -139,7 +142,7 @@ private:
std::string path;
std::string parent_path;
std::vector<std::string> path_components;
- Mode perms;
+ OpenMode perms;
};
} // 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 @@
#include <memory>
#include <string_view>
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
diff --git a/src/core/file_sys/vfs_types.h b/src/core/file_sys/vfs/vfs_types.h
index 4a583ed64..4a583ed64 100644
--- a/src/core/file_sys/vfs_types.h
+++ b/src/core/file_sys/vfs/vfs_types.h
diff --git a/src/core/file_sys/vfs_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 @@
#include <algorithm>
#include <utility>
-#include "core/file_sys/vfs_vector.h"
+#include "core/file_sys/vfs/vfs_vector.h"
namespace FileSys {
VectorVfsFile::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 @@
#include <memory>
#include <string>
#include <vector>
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp
index ede0aa11a..6692211e1 100644
--- a/src/core/file_sys/xts_archive.cpp
+++ b/src/core/file_sys/xts_archive.cpp
@@ -17,7 +17,7 @@
#include "core/crypto/key_manager.h"
#include "core/crypto/xts_encryption_layer.h"
#include "core/file_sys/content_archive.h"
-#include "core/file_sys/vfs_offset.h"
+#include "core/file_sys/vfs/vfs_offset.h"
#include "core/file_sys/xts_archive.h"
#include "core/loader/loader.h"
diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h
index abbe5f716..7589b7c38 100644
--- a/src/core/file_sys/xts_archive.h
+++ b/src/core/file_sys/xts_archive.h
@@ -8,7 +8,7 @@
#include "common/common_types.h"
#include "common/swap.h"
#include "core/crypto/key_manager.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace Loader {
enum class ResultStatus : u16;
diff --git a/src/core/frontend/applets/error.cpp b/src/core/frontend/applets/error.cpp
index 2e6f7a3d9..53d4be2e5 100644
--- a/src/core/frontend/applets/error.cpp
+++ b/src/core/frontend/applets/error.cpp
@@ -12,7 +12,7 @@ void DefaultErrorApplet::Close() const {}
void DefaultErrorApplet::ShowError(Result error, FinishedCallback finished) const {
LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})",
- error.module.Value(), error.description.Value(), error.raw);
+ error.GetModule(), error.GetDescription(), error.raw);
}
void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::seconds time,
@@ -20,7 +20,7 @@ void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::secon
LOG_CRITICAL(
Service_Fatal,
"Application requested error display: {:04X}-{:04X} (raw={:08X}) with timestamp={:016X}",
- error.module.Value(), error.description.Value(), error.raw, time.count());
+ error.GetModule(), error.GetDescription(), error.raw, time.count());
}
void DefaultErrorApplet::ShowCustomErrorText(Result error, std::string main_text,
@@ -28,7 +28,7 @@ void DefaultErrorApplet::ShowCustomErrorText(Result error, std::string main_text
FinishedCallback finished) const {
LOG_CRITICAL(Service_Fatal,
"Application requested custom error with error_code={:04X}-{:04X} (raw={:08X})",
- error.module.Value(), error.description.Value(), error.raw);
+ error.GetModule(), error.GetDescription(), error.raw);
LOG_CRITICAL(Service_Fatal, " Main Text: {}", main_text);
LOG_CRITICAL(Service_Fatal, " Detail Text: {}", detail_text);
}
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 749f51f69..316370266 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -189,14 +189,14 @@ enum class ErrorModule : u32 {
union Result {
u32 raw;
- BitField<0, 9, ErrorModule> module;
- BitField<9, 13, u32> description;
+ using Module = BitField<0, 9, ErrorModule>;
+ using Description = BitField<9, 13, u32>;
Result() = default;
constexpr explicit Result(u32 raw_) : raw(raw_) {}
constexpr Result(ErrorModule module_, u32 description_)
- : raw(module.FormatValue(module_) | description.FormatValue(description_)) {}
+ : raw(Module::FormatValue(module_) | Description::FormatValue(description_)) {}
[[nodiscard]] constexpr bool IsSuccess() const {
return raw == 0;
@@ -211,7 +211,15 @@ union Result {
}
[[nodiscard]] constexpr u32 GetInnerValue() const {
- return static_cast<u32>(module.Value()) | (description << module.bits);
+ return raw;
+ }
+
+ [[nodiscard]] constexpr ErrorModule GetModule() const {
+ return Module::ExtractValue(raw);
+ }
+
+ [[nodiscard]] constexpr u32 GetDescription() const {
+ return Description::ExtractValue(raw);
}
[[nodiscard]] constexpr bool Includes(Result result) const {
@@ -274,8 +282,9 @@ public:
}
[[nodiscard]] constexpr bool Includes(Result other) const {
- return code.module == other.module && code.description <= other.description &&
- other.description <= description_end;
+ return code.GetModule() == other.GetModule() &&
+ code.GetDescription() <= other.GetDescription() &&
+ other.GetDescription() <= description_end;
}
private:
@@ -330,6 +339,16 @@ constexpr bool EvaluateResultFailure(const Result& r) {
return R_FAILED(r);
}
+template <auto... R>
+constexpr bool EvaluateAnyResultIncludes(const Result& r) {
+ return ((r == R) || ...);
+}
+
+template <auto... R>
+constexpr bool EvaluateResultNotIncluded(const Result& r) {
+ return !EvaluateAnyResultIncludes<R...>(r);
+}
+
template <typename T>
constexpr void UpdateCurrentResultReference(T result_reference, Result result) = delete;
// Intentionally not defined
@@ -371,6 +390,13 @@ constexpr void UpdateCurrentResultReference<const Result>(Result result_referenc
DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \
ON_RESULT_SUCCESS_2
+#define ON_RESULT_INCLUDED_2(...) \
+ ON_RESULT_RETURN_IMPL(ResultImpl::EvaluateAnyResultIncludes<__VA_ARGS__>)
+
+#define ON_RESULT_INCLUDED(...) \
+ DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \
+ ON_RESULT_INCLUDED_2(__VA_ARGS__)
+
constexpr inline Result __TmpCurrentResultReference = ResultSuccess;
/// Returns a result.
diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp
index 5d17c353f..084bc138c 100644
--- a/src/core/hle/service/am/applets/applet_error.cpp
+++ b/src/core/hle/service/am/applets/applet_error.cpp
@@ -27,8 +27,8 @@ struct ErrorCode {
static constexpr ErrorCode FromResult(Result result) {
return {
- .error_category{2000 + static_cast<u32>(result.module.Value())},
- .error_number{result.description.Value()},
+ .error_category{2000 + static_cast<u32>(result.GetModule())},
+ .error_number{result.GetDescription()},
};
}
diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp
index b0ea2b381..19057ad7b 100644
--- a/src/core/hle/service/am/applets/applet_web_browser.cpp
+++ b/src/core/hle/service/am/applets/applet_web_browser.cpp
@@ -9,13 +9,13 @@
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/content_archive.h"
-#include "core/file_sys/mode.h"
+#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/system_archive/system_archive.h"
-#include "core/file_sys/vfs_vector.h"
+#include "core/file_sys/vfs/vfs_vector.h"
#include "core/frontend/applets/web_browser.h"
#include "core/hle/result.h"
#include "core/hle/service/am/am.h"
@@ -213,7 +213,7 @@ void ExtractSharedFonts(Core::System& system) {
std::move(decrypted_data), DECRYPTED_SHARED_FONTS[i]);
const auto temp_dir = system.GetFilesystem()->CreateDirectory(
- Common::FS::PathToUTF8String(fonts_dir), FileSys::Mode::ReadWrite);
+ Common::FS::PathToUTF8String(fonts_dir), FileSys::OpenMode::ReadWrite);
const auto out_file = temp_dir->CreateFile(DECRYPTED_SHARED_FONTS[i]);
@@ -333,7 +333,7 @@ void WebBrowser::ExtractOfflineRomFS() {
const auto extracted_romfs_dir = FileSys::ExtractRomFS(offline_romfs);
const auto temp_dir = system.GetFilesystem()->CreateDirectory(
- Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite);
+ Common::FS::PathToUTF8String(offline_cache_dir), FileSys::OpenMode::ReadWrite);
FileSys::VfsRawCopyD(extracted_romfs_dir, temp_dir);
}
diff --git a/src/core/hle/service/am/applets/applet_web_browser.h b/src/core/hle/service/am/applets/applet_web_browser.h
index 99fe18659..36adb2510 100644
--- a/src/core/hle/service/am/applets/applet_web_browser.h
+++ b/src/core/hle/service/am/applets/applet_web_browser.h
@@ -7,7 +7,7 @@
#include <optional>
#include "common/common_types.h"
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/result.h"
#include "core/hle/service/am/applets/applet_web_browser_types.h"
#include "core/hle/service/am/applets/applets.h"
diff --git a/src/core/hle/service/audio/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:
ctx.WriteBufferC(performance_buffer.data(), performance_buffer.size(), 1);
}
} else {
- LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description);
+ LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!",
+ result.GetDescription());
}
IPC::ResponseBuilder rb{ctx, 2};
diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h
index 205ed0702..aa36d29d5 100644
--- a/src/core/hle/service/bcat/backend/backend.h
+++ b/src/core/hle/service/bcat/backend/backend.h
@@ -8,7 +8,7 @@
#include <string>
#include "common/common_types.h"
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/result.h"
#include "core/hle/service/kernel_helpers.h"
diff --git a/src/core/hle/service/bcat/bcat_module.cpp b/src/core/hle/service/bcat/bcat_module.cpp
index a6281913a..76d7bb139 100644
--- a/src/core/hle/service/bcat/bcat_module.cpp
+++ b/src/core/hle/service/bcat/bcat_module.cpp
@@ -8,7 +8,7 @@
#include "common/settings.h"
#include "common/string_util.h"
#include "core/core.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/bcat/backend/backend.h"
#include "core/hle/service/bcat/bcat.h"
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index c65e32489..2dc23e674 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -283,7 +283,7 @@ public:
{17, &IBtmSystemCore::GetConnectedAudioDevices, "GetConnectedAudioDevices"},
{18, nullptr, "DisconnectAudioDevice"},
{19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"},
- {20, nullptr, "GetPairedAudioDevices"},
+ {20, &IBtmSystemCore::GetPairedAudioDevices, "GetPairedAudioDevices"},
{21, nullptr, "RemoveAudioDevicePairing"},
{22, &IBtmSystemCore::RequestAudioDeviceConnectionRejection, "RequestAudioDeviceConnectionRejection"},
{23, &IBtmSystemCore::CancelAudioDeviceConnectionRejection, "CancelAudioDeviceConnectionRejection"}
@@ -327,6 +327,13 @@ private:
rb.Push<u32>(0);
}
+ void GetPairedAudioDevices(HLERequestContext& ctx) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u32>(0);
+ }
+
void RequestAudioDeviceConnectionRejection(HLERequestContext& ctx) {
LOG_WARNING(Service_BTM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp
index 9925720a3..69acb3a8b 100644
--- a/src/core/hle/service/caps/caps_a.cpp
+++ b/src/core/hle/service/caps/caps_a.cpp
@@ -202,14 +202,14 @@ Result IAlbumAccessorService::TranslateResult(Result in_result) {
}
if ((in_result.raw & 0x3801ff) == ResultUnknown1024.raw) {
- if (in_result.description - 0x514 < 100) {
+ if (in_result.GetDescription() - 0x514 < 100) {
return ResultInvalidFileData;
}
- if (in_result.description - 0x5dc < 100) {
+ if (in_result.GetDescription() - 0x5dc < 100) {
return ResultInvalidFileData;
}
- if (in_result.description - 0x578 < 100) {
+ if (in_result.GetDescription() - 0x578 < 100) {
if (in_result == ResultFileCountLimit) {
return ResultUnknown22;
}
@@ -244,9 +244,10 @@ Result IAlbumAccessorService::TranslateResult(Result in_result) {
return ResultUnknown1024;
}
- if (in_result.module == ErrorModule::FS) {
- if ((in_result.description >> 0xc < 0x7d) || (in_result.description - 1000 < 2000) ||
- (((in_result.description - 3000) >> 3) < 0x271)) {
+ if (in_result.GetModule() == ErrorModule::FS) {
+ if ((in_result.GetDescription() >> 0xc < 0x7d) ||
+ (in_result.GetDescription() - 1000 < 2000) ||
+ (((in_result.GetDescription() - 3000) >> 3) < 0x271)) {
// TODO: Translate FS error
return in_result;
}
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index 31da86074..dfcac1ffd 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -73,8 +73,8 @@ static void GenerateErrorReport(Core::System& system, Result error_code, const F
"Program entry point: 0x{:16X}\n"
"\n",
Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw,
- 2000 + static_cast<u32>(error_code.module.Value()),
- static_cast<u32>(error_code.description.Value()), info.set_flags, info.program_entry_point);
+ 2000 + static_cast<u32>(error_code.GetModule()),
+ static_cast<u32>(error_code.GetDescription()), info.set_flags, info.program_entry_point);
if (info.backtrace_size != 0x0) {
crash_report += "Registers:\n";
for (size_t i = 0; i < info.registers.size(); i++) {
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index ca6d8d607..ae230afc0 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -12,18 +12,17 @@
#include "core/file_sys/card_image.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/errors.h"
-#include "core/file_sys/mode.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/sdmc_factory.h"
-#include "core/file_sys/vfs.h"
-#include "core/file_sys/vfs_offset.h"
+#include "core/file_sys/vfs/vfs.h"
+#include "core/file_sys/vfs/vfs_offset.h"
#include "core/hle/service/filesystem/filesystem.h"
-#include "core/hle/service/filesystem/fsp_ldr.h"
-#include "core/hle/service/filesystem/fsp_pr.h"
-#include "core/hle/service/filesystem/fsp_srv.h"
+#include "core/hle/service/filesystem/fsp/fsp_ldr.h"
+#include "core/hle/service/filesystem/fsp/fsp_pr.h"
+#include "core/hle/service/filesystem/fsp/fsp_srv.h"
#include "core/hle/service/filesystem/romfs_controller.h"
#include "core/hle/service/filesystem/save_data_controller.h"
#include "core/hle/service/server_manager.h"
@@ -53,12 +52,12 @@ Result VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size
std::string path(Common::FS::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir == nullptr) {
- return FileSys::ERROR_PATH_NOT_FOUND;
+ return FileSys::ResultPathNotFound;
}
- FileSys::EntryType entry_type{};
+ FileSys::DirectoryEntryType entry_type{};
if (GetEntryType(&entry_type, path) == ResultSuccess) {
- return FileSys::ERROR_PATH_ALREADY_EXISTS;
+ return FileSys::ResultPathAlreadyExists;
}
auto file = dir->CreateFile(Common::FS::GetFilename(path));
@@ -82,7 +81,7 @@ Result VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir == nullptr || dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
- return FileSys::ERROR_PATH_NOT_FOUND;
+ return FileSys::ResultPathNotFound;
}
if (!dir->DeleteFile(Common::FS::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
@@ -153,12 +152,12 @@ Result VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
// Use more-optimized vfs implementation rename.
if (src == nullptr) {
- return FileSys::ERROR_PATH_NOT_FOUND;
+ return FileSys::ResultPathNotFound;
}
if (dst && Common::FS::Exists(dst->GetFullPath())) {
LOG_ERROR(Service_FS, "File at new_path={} already exists", dst->GetFullPath());
- return FileSys::ERROR_PATH_ALREADY_EXISTS;
+ return FileSys::ResultPathAlreadyExists;
}
if (!src->Rename(Common::FS::GetFilename(dest_path))) {
@@ -195,7 +194,7 @@ Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
// Use more-optimized vfs implementation rename.
if (src == nullptr)
- return FileSys::ERROR_PATH_NOT_FOUND;
+ return FileSys::ResultPathNotFound;
if (!src->Rename(Common::FS::GetFilename(dest_path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultUnknown;
@@ -214,7 +213,8 @@ Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
}
Result VfsDirectoryServiceWrapper::OpenFile(FileSys::VirtualFile* out_file,
- const std::string& path_, FileSys::Mode mode) const {
+ const std::string& path_,
+ FileSys::OpenMode mode) const {
const std::string path(Common::FS::SanitizePath(path_));
std::string_view npath = path;
while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) {
@@ -223,10 +223,10 @@ Result VfsDirectoryServiceWrapper::OpenFile(FileSys::VirtualFile* out_file,
auto file = backing->GetFileRelative(npath);
if (file == nullptr) {
- return FileSys::ERROR_PATH_NOT_FOUND;
+ return FileSys::ResultPathNotFound;
}
- if (mode == FileSys::Mode::Append) {
+ if (mode == FileSys::OpenMode::AllowAppend) {
*out_file = std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize());
} else {
*out_file = file;
@@ -241,50 +241,50 @@ Result VfsDirectoryServiceWrapper::OpenDirectory(FileSys::VirtualDir* out_direct
auto dir = GetDirectoryRelativeWrapped(backing, path);
if (dir == nullptr) {
// TODO(DarkLordZach): Find a better error code for this
- return FileSys::ERROR_PATH_NOT_FOUND;
+ return FileSys::ResultPathNotFound;
}
*out_directory = dir;
return ResultSuccess;
}
-Result VfsDirectoryServiceWrapper::GetEntryType(FileSys::EntryType* out_entry_type,
+Result VfsDirectoryServiceWrapper::GetEntryType(FileSys::DirectoryEntryType* out_entry_type,
const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir == nullptr) {
- return FileSys::ERROR_PATH_NOT_FOUND;
+ return FileSys::ResultPathNotFound;
}
auto filename = Common::FS::GetFilename(path);
// TODO(Subv): Some games use the '/' path, find out what this means.
if (filename.empty()) {
- *out_entry_type = FileSys::EntryType::Directory;
+ *out_entry_type = FileSys::DirectoryEntryType::Directory;
return ResultSuccess;
}
if (dir->GetFile(filename) != nullptr) {
- *out_entry_type = FileSys::EntryType::File;
+ *out_entry_type = FileSys::DirectoryEntryType::File;
return ResultSuccess;
}
if (dir->GetSubdirectory(filename) != nullptr) {
- *out_entry_type = FileSys::EntryType::Directory;
+ *out_entry_type = FileSys::DirectoryEntryType::Directory;
return ResultSuccess;
}
- return FileSys::ERROR_PATH_NOT_FOUND;
+ return FileSys::ResultPathNotFound;
}
Result VfsDirectoryServiceWrapper::GetFileTimeStampRaw(
FileSys::FileTimeStampRaw* out_file_time_stamp_raw, const std::string& path) const {
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir == nullptr) {
- return FileSys::ERROR_PATH_NOT_FOUND;
+ return FileSys::ResultPathNotFound;
}
- FileSys::EntryType entry_type;
+ FileSys::DirectoryEntryType entry_type;
if (GetEntryType(&entry_type, path) != ResultSuccess) {
- return FileSys::ERROR_PATH_NOT_FOUND;
+ return FileSys::ResultPathNotFound;
}
*out_file_time_stamp_raw = dir->GetFileTimeStamp(Common::FS::GetFilename(path));
@@ -317,7 +317,7 @@ Result FileSystemController::OpenProcess(
const auto it = registrations.find(process_id);
if (it == registrations.end()) {
- return FileSys::ERROR_ENTITY_NOT_FOUND;
+ return FileSys::ResultTargetNotFound;
}
*out_program_id = it->second.program_id;
@@ -347,7 +347,7 @@ std::shared_ptr<SaveDataController> FileSystemController::OpenSaveDataController
std::shared_ptr<FileSys::SaveDataFactory> FileSystemController::CreateSaveDataFactory(
ProgramId program_id) {
using YuzuPath = Common::FS::YuzuPath;
- const auto rw_mode = FileSys::Mode::ReadWrite;
+ const auto rw_mode = FileSys::OpenMode::ReadWrite;
auto vfs = system.GetFilesystem();
const auto nand_directory =
@@ -360,12 +360,12 @@ Result FileSystemController::OpenSDMC(FileSys::VirtualDir* out_sdmc) const {
LOG_TRACE(Service_FS, "Opening SDMC");
if (sdmc_factory == nullptr) {
- return FileSys::ERROR_SD_CARD_NOT_FOUND;
+ return FileSys::ResultPortSdCardNoDevice;
}
auto sdmc = sdmc_factory->Open();
if (sdmc == nullptr) {
- return FileSys::ERROR_SD_CARD_NOT_FOUND;
+ return FileSys::ResultPortSdCardNoDevice;
}
*out_sdmc = sdmc;
@@ -377,12 +377,12 @@ Result FileSystemController::OpenBISPartition(FileSys::VirtualDir* out_bis_parti
LOG_TRACE(Service_FS, "Opening BIS Partition with id={:08X}", id);
if (bis_factory == nullptr) {
- return FileSys::ERROR_ENTITY_NOT_FOUND;
+ return FileSys::ResultTargetNotFound;
}
auto part = bis_factory->OpenPartition(id);
if (part == nullptr) {
- return FileSys::ERROR_INVALID_ARGUMENT;
+ return FileSys::ResultInvalidArgument;
}
*out_bis_partition = part;
@@ -394,12 +394,12 @@ Result FileSystemController::OpenBISPartitionStorage(
LOG_TRACE(Service_FS, "Opening BIS Partition Storage with id={:08X}", id);
if (bis_factory == nullptr) {
- return FileSys::ERROR_ENTITY_NOT_FOUND;
+ return FileSys::ResultTargetNotFound;
}
auto part = bis_factory->OpenPartitionStorage(id, system.GetFilesystem());
if (part == nullptr) {
- return FileSys::ERROR_INVALID_ARGUMENT;
+ return FileSys::ResultInvalidArgument;
}
*out_bis_partition_storage = part;
@@ -686,15 +686,15 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
using YuzuPath = Common::FS::YuzuPath;
const auto sdmc_dir_path = Common::FS::GetYuzuPath(YuzuPath::SDMCDir);
const auto sdmc_load_dir_path = sdmc_dir_path / "atmosphere/contents";
- const auto rw_mode = FileSys::Mode::ReadWrite;
+ const auto rw_mode = FileSys::OpenMode::ReadWrite;
auto nand_directory =
vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::NANDDir), rw_mode);
auto sd_directory = vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_dir_path), rw_mode);
- auto load_directory =
- vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::LoadDir), FileSys::Mode::Read);
- auto sd_load_directory =
- vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_load_dir_path), FileSys::Mode::Read);
+ auto load_directory = vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::LoadDir),
+ FileSys::OpenMode::Read);
+ auto sd_load_directory = vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_load_dir_path),
+ FileSys::OpenMode::Read);
auto dump_directory =
vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::DumpDir), rw_mode);
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 @@
#pragma once
#include <memory>
+#include <mutex>
#include "common/common_types.h"
-#include "core/file_sys/directory.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/fs_directory.h"
+#include "core/file_sys/fs_filesystem.h"
+#include "core/file_sys/vfs/vfs.h"
#include "core/hle/result.h"
namespace Core {
@@ -26,7 +28,6 @@ class XCI;
enum class BisPartitionId : u32;
enum class ContentRecordType : u8;
-enum class Mode : u32;
enum class SaveDataSpaceId : u8;
enum class SaveDataType : u8;
enum class StorageId : u8;
@@ -57,13 +58,6 @@ enum class ImageDirectoryId : u32 {
SdCard,
};
-enum class OpenDirectoryMode : u64 {
- Directory = (1 << 0),
- File = (1 << 1),
- All = Directory | File
-};
-DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode);
-
using ProcessId = u64;
using ProgramId = u64;
@@ -237,7 +231,7 @@ public:
* @return Opened file, or error code
*/
Result OpenFile(FileSys::VirtualFile* out_file, const std::string& path,
- FileSys::Mode mode) const;
+ FileSys::OpenMode mode) const;
/**
* Open a directory specified by its path
@@ -250,7 +244,7 @@ public:
* Get the type of the specified path
* @return The type of the specified path or error code
*/
- Result GetEntryType(FileSys::EntryType* out_entry_type, const std::string& path) const;
+ Result GetEntryType(FileSys::DirectoryEntryType* out_entry_type, const std::string& path) const;
/**
* Get the timestamp of the specified path
diff --git a/src/core/hle/service/filesystem/fsp/fs_i_directory.cpp b/src/core/hle/service/filesystem/fsp/fs_i_directory.cpp
new file mode 100644
index 000000000..39690018b
--- /dev/null
+++ b/src/core/hle/service/filesystem/fsp/fs_i_directory.cpp
@@ -0,0 +1,84 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/file_sys/fs_filesystem.h"
+#include "core/file_sys/savedata_factory.h"
+#include "core/hle/service/filesystem/fsp/fs_i_directory.h"
+#include "core/hle/service/ipc_helpers.h"
+
+namespace Service::FileSystem {
+
+template <typename T>
+static void BuildEntryIndex(std::vector<FileSys::DirectoryEntry>& entries,
+ const std::vector<T>& new_data, FileSys::DirectoryEntryType type) {
+ entries.reserve(entries.size() + new_data.size());
+
+ for (const auto& new_entry : new_data) {
+ auto name = new_entry->GetName();
+
+ if (type == FileSys::DirectoryEntryType::File &&
+ name == FileSys::GetSaveDataSizeFileName()) {
+ continue;
+ }
+
+ entries.emplace_back(name, static_cast<s8>(type),
+ type == FileSys::DirectoryEntryType::Directory ? 0
+ : new_entry->GetSize());
+ }
+}
+
+IDirectory::IDirectory(Core::System& system_, FileSys::VirtualDir backend_,
+ FileSys::OpenDirectoryMode mode)
+ : ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) {
+ static const FunctionInfo functions[] = {
+ {0, &IDirectory::Read, "Read"},
+ {1, &IDirectory::GetEntryCount, "GetEntryCount"},
+ };
+ RegisterHandlers(functions);
+
+ // TODO(DarkLordZach): Verify that this is the correct behavior.
+ // Build entry index now to save time later.
+ if (True(mode & FileSys::OpenDirectoryMode::Directory)) {
+ BuildEntryIndex(entries, backend->GetSubdirectories(),
+ FileSys::DirectoryEntryType::Directory);
+ }
+ if (True(mode & FileSys::OpenDirectoryMode::File)) {
+ BuildEntryIndex(entries, backend->GetFiles(), FileSys::DirectoryEntryType::File);
+ }
+}
+
+void IDirectory::Read(HLERequestContext& ctx) {
+ LOG_DEBUG(Service_FS, "called.");
+
+ // Calculate how many entries we can fit in the output buffer
+ const u64 count_entries = ctx.GetWriteBufferNumElements<FileSys::DirectoryEntry>();
+
+ // Cap at total number of entries.
+ const u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index);
+
+ // Determine data start and end
+ const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index);
+ const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries);
+ const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
+
+ next_entry_index += actual_entries;
+
+ // Write the data to memory
+ ctx.WriteBuffer(begin, range_size);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(ResultSuccess);
+ rb.Push(actual_entries);
+}
+
+void IDirectory::GetEntryCount(HLERequestContext& ctx) {
+ LOG_DEBUG(Service_FS, "called");
+
+ u64 count = entries.size() - next_entry_index;
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(ResultSuccess);
+ rb.Push(count);
+}
+
+} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp/fs_i_directory.h b/src/core/hle/service/filesystem/fsp/fs_i_directory.h
new file mode 100644
index 000000000..793ecfcd7
--- /dev/null
+++ b/src/core/hle/service/filesystem/fsp/fs_i_directory.h
@@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/file_sys/vfs/vfs.h"
+#include "core/hle/service/filesystem/filesystem.h"
+#include "core/hle/service/service.h"
+
+namespace FileSys {
+struct DirectoryEntry;
+}
+
+namespace Service::FileSystem {
+
+class IDirectory final : public ServiceFramework<IDirectory> {
+public:
+ explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_,
+ FileSys::OpenDirectoryMode mode);
+
+private:
+ FileSys::VirtualDir backend;
+ std::vector<FileSys::DirectoryEntry> entries;
+ u64 next_entry_index = 0;
+
+ void Read(HLERequestContext& ctx);
+ void GetEntryCount(HLERequestContext& ctx);
+};
+
+} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp/fs_i_file.cpp b/src/core/hle/service/filesystem/fsp/fs_i_file.cpp
new file mode 100644
index 000000000..9a18f6ec5
--- /dev/null
+++ b/src/core/hle/service/filesystem/fsp/fs_i_file.cpp
@@ -0,0 +1,127 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/file_sys/errors.h"
+#include "core/hle/service/filesystem/fsp/fs_i_file.h"
+#include "core/hle/service/ipc_helpers.h"
+
+namespace Service::FileSystem {
+
+IFile::IFile(Core::System& system_, FileSys::VirtualFile backend_)
+ : ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) {
+ static const FunctionInfo functions[] = {
+ {0, &IFile::Read, "Read"},
+ {1, &IFile::Write, "Write"},
+ {2, &IFile::Flush, "Flush"},
+ {3, &IFile::SetSize, "SetSize"},
+ {4, &IFile::GetSize, "GetSize"},
+ {5, nullptr, "OperateRange"},
+ {6, nullptr, "OperateRangeWithBuffer"},
+ };
+ RegisterHandlers(functions);
+}
+
+void IFile::Read(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const u64 option = rp.Pop<u64>();
+ const s64 offset = rp.Pop<s64>();
+ const s64 length = rp.Pop<s64>();
+
+ LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length);
+
+ // Error checking
+ if (length < 0) {
+ LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(FileSys::ResultInvalidSize);
+ return;
+ }
+ if (offset < 0) {
+ LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(FileSys::ResultInvalidOffset);
+ return;
+ }
+
+ // Read the data from the Storage backend
+ std::vector<u8> output = backend->ReadBytes(length, offset);
+
+ // Write the data to memory
+ ctx.WriteBuffer(output);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(ResultSuccess);
+ rb.Push(static_cast<u64>(output.size()));
+}
+
+void IFile::Write(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const u64 option = rp.Pop<u64>();
+ const s64 offset = rp.Pop<s64>();
+ const s64 length = rp.Pop<s64>();
+
+ LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length);
+
+ // Error checking
+ if (length < 0) {
+ LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(FileSys::ResultInvalidSize);
+ return;
+ }
+ if (offset < 0) {
+ LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(FileSys::ResultInvalidOffset);
+ return;
+ }
+
+ const auto data = ctx.ReadBuffer();
+
+ ASSERT_MSG(static_cast<s64>(data.size()) <= length,
+ "Attempting to write more data than requested (requested={:016X}, actual={:016X}).",
+ length, data.size());
+
+ // Write the data to the Storage backend
+ const auto write_size =
+ static_cast<std::size_t>(std::distance(data.begin(), data.begin() + length));
+ const std::size_t written = backend->Write(data.data(), write_size, offset);
+
+ ASSERT_MSG(static_cast<s64>(written) == length,
+ "Could not write all bytes to file (requested={:016X}, actual={:016X}).", length,
+ written);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IFile::Flush(HLERequestContext& ctx) {
+ LOG_DEBUG(Service_FS, "called");
+
+ // Exists for SDK compatibiltity -- No need to flush file.
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IFile::SetSize(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const u64 size = rp.Pop<u64>();
+ LOG_DEBUG(Service_FS, "called, size={}", size);
+
+ backend->Resize(size);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IFile::GetSize(HLERequestContext& ctx) {
+ const u64 size = backend->GetSize();
+ LOG_DEBUG(Service_FS, "called, size={}", size);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(ResultSuccess);
+ rb.Push<u64>(size);
+}
+
+} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp/fs_i_file.h b/src/core/hle/service/filesystem/fsp/fs_i_file.h
new file mode 100644
index 000000000..5e5430c67
--- /dev/null
+++ b/src/core/hle/service/filesystem/fsp/fs_i_file.h
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/filesystem/filesystem.h"
+#include "core/hle/service/service.h"
+
+namespace Service::FileSystem {
+
+class IFile final : public ServiceFramework<IFile> {
+public:
+ explicit IFile(Core::System& system_, FileSys::VirtualFile backend_);
+
+private:
+ FileSys::VirtualFile backend;
+
+ void Read(HLERequestContext& ctx);
+ void Write(HLERequestContext& ctx);
+ void Flush(HLERequestContext& ctx);
+ void SetSize(HLERequestContext& ctx);
+ void GetSize(HLERequestContext& ctx);
+};
+
+} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp
new file mode 100644
index 000000000..efa394dd1
--- /dev/null
+++ b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp
@@ -0,0 +1,262 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/string_util.h"
+#include "core/hle/service/filesystem/fsp/fs_i_directory.h"
+#include "core/hle/service/filesystem/fsp/fs_i_file.h"
+#include "core/hle/service/filesystem/fsp/fs_i_filesystem.h"
+#include "core/hle/service/ipc_helpers.h"
+
+namespace Service::FileSystem {
+
+IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_)
+ : ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move(
+ size_)} {
+ static const FunctionInfo functions[] = {
+ {0, &IFileSystem::CreateFile, "CreateFile"},
+ {1, &IFileSystem::DeleteFile, "DeleteFile"},
+ {2, &IFileSystem::CreateDirectory, "CreateDirectory"},
+ {3, &IFileSystem::DeleteDirectory, "DeleteDirectory"},
+ {4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"},
+ {5, &IFileSystem::RenameFile, "RenameFile"},
+ {6, nullptr, "RenameDirectory"},
+ {7, &IFileSystem::GetEntryType, "GetEntryType"},
+ {8, &IFileSystem::OpenFile, "OpenFile"},
+ {9, &IFileSystem::OpenDirectory, "OpenDirectory"},
+ {10, &IFileSystem::Commit, "Commit"},
+ {11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"},
+ {12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"},
+ {13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"},
+ {14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"},
+ {15, nullptr, "QueryEntry"},
+ {16, &IFileSystem::GetFileSystemAttribute, "GetFileSystemAttribute"},
+ };
+ RegisterHandlers(functions);
+}
+
+void IFileSystem::CreateFile(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const auto file_buffer = ctx.ReadBuffer();
+ const std::string name = Common::StringFromBuffer(file_buffer);
+
+ const u64 file_mode = rp.Pop<u64>();
+ const u32 file_size = rp.Pop<u32>();
+
+ LOG_DEBUG(Service_FS, "called. file={}, mode=0x{:X}, size=0x{:08X}", name, file_mode,
+ file_size);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(backend.CreateFile(name, file_size));
+}
+
+void IFileSystem::DeleteFile(HLERequestContext& ctx) {
+ const auto file_buffer = ctx.ReadBuffer();
+ const std::string name = Common::StringFromBuffer(file_buffer);
+
+ LOG_DEBUG(Service_FS, "called. file={}", name);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(backend.DeleteFile(name));
+}
+
+void IFileSystem::CreateDirectory(HLERequestContext& ctx) {
+ const auto file_buffer = ctx.ReadBuffer();
+ const std::string name = Common::StringFromBuffer(file_buffer);
+
+ LOG_DEBUG(Service_FS, "called. directory={}", name);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(backend.CreateDirectory(name));
+}
+
+void IFileSystem::DeleteDirectory(HLERequestContext& ctx) {
+ const auto file_buffer = ctx.ReadBuffer();
+ const std::string name = Common::StringFromBuffer(file_buffer);
+
+ LOG_DEBUG(Service_FS, "called. directory={}", name);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(backend.DeleteDirectory(name));
+}
+
+void IFileSystem::DeleteDirectoryRecursively(HLERequestContext& ctx) {
+ const auto file_buffer = ctx.ReadBuffer();
+ const std::string name = Common::StringFromBuffer(file_buffer);
+
+ LOG_DEBUG(Service_FS, "called. directory={}", name);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(backend.DeleteDirectoryRecursively(name));
+}
+
+void IFileSystem::CleanDirectoryRecursively(HLERequestContext& ctx) {
+ const auto file_buffer = ctx.ReadBuffer();
+ const std::string name = Common::StringFromBuffer(file_buffer);
+
+ LOG_DEBUG(Service_FS, "called. Directory: {}", name);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(backend.CleanDirectoryRecursively(name));
+}
+
+void IFileSystem::RenameFile(HLERequestContext& ctx) {
+ const std::string src_name = Common::StringFromBuffer(ctx.ReadBuffer(0));
+ const std::string dst_name = Common::StringFromBuffer(ctx.ReadBuffer(1));
+
+ LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(backend.RenameFile(src_name, dst_name));
+}
+
+void IFileSystem::OpenFile(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const auto file_buffer = ctx.ReadBuffer();
+ const std::string name = Common::StringFromBuffer(file_buffer);
+
+ const auto mode = static_cast<FileSys::OpenMode>(rp.Pop<u32>());
+
+ LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, mode);
+
+ FileSys::VirtualFile vfs_file{};
+ auto result = backend.OpenFile(&vfs_file, name, mode);
+ if (result != ResultSuccess) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ auto file = std::make_shared<IFile>(system, vfs_file);
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IFile>(std::move(file));
+}
+
+void IFileSystem::OpenDirectory(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const auto file_buffer = ctx.ReadBuffer();
+ const std::string name = Common::StringFromBuffer(file_buffer);
+ const auto mode = rp.PopRaw<FileSys::OpenDirectoryMode>();
+
+ LOG_DEBUG(Service_FS, "called. directory={}, mode={}", name, mode);
+
+ FileSys::VirtualDir vfs_dir{};
+ auto result = backend.OpenDirectory(&vfs_dir, name);
+ if (result != ResultSuccess) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ auto directory = std::make_shared<IDirectory>(system, vfs_dir, mode);
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IDirectory>(std::move(directory));
+}
+
+void IFileSystem::GetEntryType(HLERequestContext& ctx) {
+ const auto file_buffer = ctx.ReadBuffer();
+ const std::string name = Common::StringFromBuffer(file_buffer);
+
+ LOG_DEBUG(Service_FS, "called. file={}", name);
+
+ FileSys::DirectoryEntryType vfs_entry_type{};
+ auto result = backend.GetEntryType(&vfs_entry_type, name);
+ if (result != ResultSuccess) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u32>(static_cast<u32>(vfs_entry_type));
+}
+
+void IFileSystem::Commit(HLERequestContext& ctx) {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IFileSystem::GetFreeSpaceSize(HLERequestContext& ctx) {
+ LOG_DEBUG(Service_FS, "called");
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(ResultSuccess);
+ rb.Push(size.get_free_size());
+}
+
+void IFileSystem::GetTotalSpaceSize(HLERequestContext& ctx) {
+ LOG_DEBUG(Service_FS, "called");
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(ResultSuccess);
+ rb.Push(size.get_total_size());
+}
+
+void IFileSystem::GetFileTimeStampRaw(HLERequestContext& ctx) {
+ const auto file_buffer = ctx.ReadBuffer();
+ const std::string name = Common::StringFromBuffer(file_buffer);
+
+ LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name);
+
+ FileSys::FileTimeStampRaw vfs_timestamp{};
+ auto result = backend.GetFileTimeStampRaw(&vfs_timestamp, name);
+ if (result != ResultSuccess) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 10};
+ rb.Push(ResultSuccess);
+ rb.PushRaw(vfs_timestamp);
+}
+
+void IFileSystem::GetFileSystemAttribute(HLERequestContext& ctx) {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+
+ struct FileSystemAttribute {
+ u8 dir_entry_name_length_max_defined;
+ u8 file_entry_name_length_max_defined;
+ u8 dir_path_name_length_max_defined;
+ u8 file_path_name_length_max_defined;
+ INSERT_PADDING_BYTES_NOINIT(0x5);
+ u8 utf16_dir_entry_name_length_max_defined;
+ u8 utf16_file_entry_name_length_max_defined;
+ u8 utf16_dir_path_name_length_max_defined;
+ u8 utf16_file_path_name_length_max_defined;
+ INSERT_PADDING_BYTES_NOINIT(0x18);
+ s32 dir_entry_name_length_max;
+ s32 file_entry_name_length_max;
+ s32 dir_path_name_length_max;
+ s32 file_path_name_length_max;
+ INSERT_PADDING_WORDS_NOINIT(0x5);
+ s32 utf16_dir_entry_name_length_max;
+ s32 utf16_file_entry_name_length_max;
+ s32 utf16_dir_path_name_length_max;
+ s32 utf16_file_path_name_length_max;
+ INSERT_PADDING_WORDS_NOINIT(0x18);
+ INSERT_PADDING_WORDS_NOINIT(0x1);
+ };
+ static_assert(sizeof(FileSystemAttribute) == 0xc0, "FileSystemAttribute has incorrect size");
+
+ FileSystemAttribute savedata_attribute{};
+ savedata_attribute.dir_entry_name_length_max_defined = true;
+ savedata_attribute.file_entry_name_length_max_defined = true;
+ savedata_attribute.dir_entry_name_length_max = 0x40;
+ savedata_attribute.file_entry_name_length_max = 0x40;
+
+ IPC::ResponseBuilder rb{ctx, 50};
+ rb.Push(ResultSuccess);
+ rb.PushRaw(savedata_attribute);
+}
+
+} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp/fs_i_filesystem.h b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.h
new file mode 100644
index 000000000..b06b3ef0e
--- /dev/null
+++ b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.h
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/file_sys/vfs/vfs.h"
+#include "core/hle/service/filesystem/filesystem.h"
+#include "core/hle/service/filesystem/fsp/fsp_util.h"
+#include "core/hle/service/service.h"
+
+namespace Service::FileSystem {
+
+class IFileSystem final : public ServiceFramework<IFileSystem> {
+public:
+ explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_);
+
+ void CreateFile(HLERequestContext& ctx);
+ void DeleteFile(HLERequestContext& ctx);
+ void CreateDirectory(HLERequestContext& ctx);
+ void DeleteDirectory(HLERequestContext& ctx);
+ void DeleteDirectoryRecursively(HLERequestContext& ctx);
+ void CleanDirectoryRecursively(HLERequestContext& ctx);
+ void RenameFile(HLERequestContext& ctx);
+ void OpenFile(HLERequestContext& ctx);
+ void OpenDirectory(HLERequestContext& ctx);
+ void GetEntryType(HLERequestContext& ctx);
+ void Commit(HLERequestContext& ctx);
+ void GetFreeSpaceSize(HLERequestContext& ctx);
+ void GetTotalSpaceSize(HLERequestContext& ctx);
+ void GetFileTimeStampRaw(HLERequestContext& ctx);
+ void GetFileSystemAttribute(HLERequestContext& ctx);
+
+private:
+ VfsDirectoryServiceWrapper backend;
+ SizeGetter size;
+};
+
+} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp/fs_i_storage.cpp b/src/core/hle/service/filesystem/fsp/fs_i_storage.cpp
new file mode 100644
index 000000000..98223c1f9
--- /dev/null
+++ b/src/core/hle/service/filesystem/fsp/fs_i_storage.cpp
@@ -0,0 +1,62 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/file_sys/errors.h"
+#include "core/hle/service/filesystem/fsp/fs_i_storage.h"
+#include "core/hle/service/ipc_helpers.h"
+
+namespace Service::FileSystem {
+
+IStorage::IStorage(Core::System& system_, FileSys::VirtualFile backend_)
+ : ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) {
+ static const FunctionInfo functions[] = {
+ {0, &IStorage::Read, "Read"},
+ {1, nullptr, "Write"},
+ {2, nullptr, "Flush"},
+ {3, nullptr, "SetSize"},
+ {4, &IStorage::GetSize, "GetSize"},
+ {5, nullptr, "OperateRange"},
+ };
+ RegisterHandlers(functions);
+}
+
+void IStorage::Read(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const s64 offset = rp.Pop<s64>();
+ const s64 length = rp.Pop<s64>();
+
+ LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length);
+
+ // Error checking
+ if (length < 0) {
+ LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(FileSys::ResultInvalidSize);
+ return;
+ }
+ if (offset < 0) {
+ LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(FileSys::ResultInvalidOffset);
+ return;
+ }
+
+ // Read the data from the Storage backend
+ std::vector<u8> output = backend->ReadBytes(length, offset);
+ // Write the data to memory
+ ctx.WriteBuffer(output);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IStorage::GetSize(HLERequestContext& ctx) {
+ const u64 size = backend->GetSize();
+ LOG_DEBUG(Service_FS, "called, size={}", size);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(ResultSuccess);
+ rb.Push<u64>(size);
+}
+
+} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp/fs_i_storage.h b/src/core/hle/service/filesystem/fsp/fs_i_storage.h
new file mode 100644
index 000000000..cb5bebcc9
--- /dev/null
+++ b/src/core/hle/service/filesystem/fsp/fs_i_storage.h
@@ -0,0 +1,23 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/file_sys/vfs/vfs.h"
+#include "core/hle/service/filesystem/filesystem.h"
+#include "core/hle/service/service.h"
+
+namespace Service::FileSystem {
+
+class IStorage final : public ServiceFramework<IStorage> {
+public:
+ explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_);
+
+private:
+ FileSys::VirtualFile backend;
+
+ void Read(HLERequestContext& ctx);
+ void GetSize(HLERequestContext& ctx);
+};
+
+} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp_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 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include "core/hle/service/filesystem/fsp_ldr.h"
+#include "core/hle/service/filesystem/fsp/fsp_ldr.h"
namespace Service::FileSystem {
diff --git a/src/core/hle/service/filesystem/fsp_ldr.h b/src/core/hle/service/filesystem/fsp/fsp_ldr.h
index 358739a87..358739a87 100644
--- a/src/core/hle/service/filesystem/fsp_ldr.h
+++ b/src/core/hle/service/filesystem/fsp/fsp_ldr.h
diff --git a/src/core/hle/service/filesystem/fsp_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 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include "core/hle/service/filesystem/fsp_pr.h"
+#include "core/hle/service/filesystem/fsp/fsp_pr.h"
namespace Service::FileSystem {
diff --git a/src/core/hle/service/filesystem/fsp_pr.h b/src/core/hle/service/filesystem/fsp/fsp_pr.h
index bd4e0a730..bd4e0a730 100644
--- a/src/core/hle/service/filesystem/fsp_pr.h
+++ b/src/core/hle/service/filesystem/fsp/fsp_pr.h
diff --git a/src/core/hle/service/filesystem/fsp_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 @@
#include "common/settings.h"
#include "common/string_util.h"
#include "core/core.h"
-#include "core/file_sys/directory.h"
#include "core/file_sys/errors.h"
-#include "core/file_sys/mode.h"
+#include "core/file_sys/fs_directory.h"
+#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/system_archive/system_archive.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
#include "core/hle/result.h"
#include "core/hle/service/filesystem/filesystem.h"
-#include "core/hle/service/filesystem/fsp_srv.h"
+#include "core/hle/service/filesystem/fsp/fs_i_filesystem.h"
+#include "core/hle/service/filesystem/fsp/fs_i_storage.h"
+#include "core/hle/service/filesystem/fsp/fsp_srv.h"
#include "core/hle/service/filesystem/romfs_controller.h"
#include "core/hle/service/filesystem/save_data_controller.h"
#include "core/hle/service/hle_ipc.h"
@@ -34,19 +36,6 @@
#include "core/reporter.h"
namespace Service::FileSystem {
-
-struct SizeGetter {
- std::function<u64()> get_free_size;
- std::function<u64()> get_total_size;
-
- static SizeGetter FromStorageId(const FileSystemController& fsc, FileSys::StorageId id) {
- return {
- [&fsc, id] { return fsc.GetFreeSpaceSize(id); },
- [&fsc, id] { return fsc.GetTotalSpaceSize(id); },
- };
- }
-};
-
enum class FileSystemType : u8 {
Invalid0 = 0,
Invalid1 = 1,
@@ -58,525 +47,6 @@ enum class FileSystemType : u8 {
ApplicationPackage = 7,
};
-class IStorage final : public ServiceFramework<IStorage> {
-public:
- explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_)
- : ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) {
- static const FunctionInfo functions[] = {
- {0, &IStorage::Read, "Read"},
- {1, nullptr, "Write"},
- {2, nullptr, "Flush"},
- {3, nullptr, "SetSize"},
- {4, &IStorage::GetSize, "GetSize"},
- {5, nullptr, "OperateRange"},
- };
- RegisterHandlers(functions);
- }
-
-private:
- FileSys::VirtualFile backend;
-
- void Read(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const s64 offset = rp.Pop<s64>();
- const s64 length = rp.Pop<s64>();
-
- LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length);
-
- // Error checking
- if (length < 0) {
- LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(FileSys::ERROR_INVALID_SIZE);
- return;
- }
- if (offset < 0) {
- LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(FileSys::ERROR_INVALID_OFFSET);
- return;
- }
-
- // Read the data from the Storage backend
- std::vector<u8> output = backend->ReadBytes(length, offset);
- // Write the data to memory
- ctx.WriteBuffer(output);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void GetSize(HLERequestContext& ctx) {
- const u64 size = backend->GetSize();
- LOG_DEBUG(Service_FS, "called, size={}", size);
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push<u64>(size);
- }
-};
-
-class IFile final : public ServiceFramework<IFile> {
-public:
- explicit IFile(Core::System& system_, FileSys::VirtualFile backend_)
- : ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) {
- static const FunctionInfo functions[] = {
- {0, &IFile::Read, "Read"},
- {1, &IFile::Write, "Write"},
- {2, &IFile::Flush, "Flush"},
- {3, &IFile::SetSize, "SetSize"},
- {4, &IFile::GetSize, "GetSize"},
- {5, nullptr, "OperateRange"},
- {6, nullptr, "OperateRangeWithBuffer"},
- };
- RegisterHandlers(functions);
- }
-
-private:
- FileSys::VirtualFile backend;
-
- void Read(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 option = rp.Pop<u64>();
- const s64 offset = rp.Pop<s64>();
- const s64 length = rp.Pop<s64>();
-
- LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset,
- length);
-
- // Error checking
- if (length < 0) {
- LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(FileSys::ERROR_INVALID_SIZE);
- return;
- }
- if (offset < 0) {
- LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(FileSys::ERROR_INVALID_OFFSET);
- return;
- }
-
- // Read the data from the Storage backend
- std::vector<u8> output = backend->ReadBytes(length, offset);
-
- // Write the data to memory
- ctx.WriteBuffer(output);
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push(static_cast<u64>(output.size()));
- }
-
- void Write(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 option = rp.Pop<u64>();
- const s64 offset = rp.Pop<s64>();
- const s64 length = rp.Pop<s64>();
-
- LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset,
- length);
-
- // Error checking
- if (length < 0) {
- LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(FileSys::ERROR_INVALID_SIZE);
- return;
- }
- if (offset < 0) {
- LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(FileSys::ERROR_INVALID_OFFSET);
- return;
- }
-
- const auto data = ctx.ReadBuffer();
-
- ASSERT_MSG(
- static_cast<s64>(data.size()) <= length,
- "Attempting to write more data than requested (requested={:016X}, actual={:016X}).",
- length, data.size());
-
- // Write the data to the Storage backend
- const auto write_size =
- static_cast<std::size_t>(std::distance(data.begin(), data.begin() + length));
- const std::size_t written = backend->Write(data.data(), write_size, offset);
-
- ASSERT_MSG(static_cast<s64>(written) == length,
- "Could not write all bytes to file (requested={:016X}, actual={:016X}).", length,
- written);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void Flush(HLERequestContext& ctx) {
- LOG_DEBUG(Service_FS, "called");
-
- // Exists for SDK compatibiltity -- No need to flush file.
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void SetSize(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 size = rp.Pop<u64>();
- LOG_DEBUG(Service_FS, "called, size={}", size);
-
- backend->Resize(size);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void GetSize(HLERequestContext& ctx) {
- const u64 size = backend->GetSize();
- LOG_DEBUG(Service_FS, "called, size={}", size);
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push<u64>(size);
- }
-};
-
-template <typename T>
-static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vector<T>& new_data,
- FileSys::EntryType type) {
- entries.reserve(entries.size() + new_data.size());
-
- for (const auto& new_entry : new_data) {
- auto name = new_entry->GetName();
-
- if (type == FileSys::EntryType::File && name == FileSys::GetSaveDataSizeFileName()) {
- continue;
- }
-
- entries.emplace_back(name, type,
- type == FileSys::EntryType::Directory ? 0 : new_entry->GetSize());
- }
-}
-
-class IDirectory final : public ServiceFramework<IDirectory> {
-public:
- explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_, OpenDirectoryMode mode)
- : ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) {
- static const FunctionInfo functions[] = {
- {0, &IDirectory::Read, "Read"},
- {1, &IDirectory::GetEntryCount, "GetEntryCount"},
- };
- RegisterHandlers(functions);
-
- // TODO(DarkLordZach): Verify that this is the correct behavior.
- // Build entry index now to save time later.
- if (True(mode & OpenDirectoryMode::Directory)) {
- BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::EntryType::Directory);
- }
- if (True(mode & OpenDirectoryMode::File)) {
- BuildEntryIndex(entries, backend->GetFiles(), FileSys::EntryType::File);
- }
- }
-
-private:
- FileSys::VirtualDir backend;
- std::vector<FileSys::Entry> entries;
- u64 next_entry_index = 0;
-
- void Read(HLERequestContext& ctx) {
- LOG_DEBUG(Service_FS, "called.");
-
- // Calculate how many entries we can fit in the output buffer
- const u64 count_entries = ctx.GetWriteBufferNumElements<FileSys::Entry>();
-
- // Cap at total number of entries.
- const u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index);
-
- // Determine data start and end
- const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index);
- const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries);
- const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
-
- next_entry_index += actual_entries;
-
- // Write the data to memory
- ctx.WriteBuffer(begin, range_size);
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push(actual_entries);
- }
-
- void GetEntryCount(HLERequestContext& ctx) {
- LOG_DEBUG(Service_FS, "called");
-
- u64 count = entries.size() - next_entry_index;
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push(count);
- }
-};
-
-class IFileSystem final : public ServiceFramework<IFileSystem> {
-public:
- explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_)
- : ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move(
- size_)} {
- static const FunctionInfo functions[] = {
- {0, &IFileSystem::CreateFile, "CreateFile"},
- {1, &IFileSystem::DeleteFile, "DeleteFile"},
- {2, &IFileSystem::CreateDirectory, "CreateDirectory"},
- {3, &IFileSystem::DeleteDirectory, "DeleteDirectory"},
- {4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"},
- {5, &IFileSystem::RenameFile, "RenameFile"},
- {6, nullptr, "RenameDirectory"},
- {7, &IFileSystem::GetEntryType, "GetEntryType"},
- {8, &IFileSystem::OpenFile, "OpenFile"},
- {9, &IFileSystem::OpenDirectory, "OpenDirectory"},
- {10, &IFileSystem::Commit, "Commit"},
- {11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"},
- {12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"},
- {13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"},
- {14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"},
- {15, nullptr, "QueryEntry"},
- {16, &IFileSystem::GetFileSystemAttribute, "GetFileSystemAttribute"},
- };
- RegisterHandlers(functions);
- }
-
- void CreateFile(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- const auto file_buffer = ctx.ReadBuffer();
- const std::string name = Common::StringFromBuffer(file_buffer);
-
- const u64 file_mode = rp.Pop<u64>();
- const u32 file_size = rp.Pop<u32>();
-
- LOG_DEBUG(Service_FS, "called. file={}, mode=0x{:X}, size=0x{:08X}", name, file_mode,
- file_size);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(backend.CreateFile(name, file_size));
- }
-
- void DeleteFile(HLERequestContext& ctx) {
- const auto file_buffer = ctx.ReadBuffer();
- const std::string name = Common::StringFromBuffer(file_buffer);
-
- LOG_DEBUG(Service_FS, "called. file={}", name);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(backend.DeleteFile(name));
- }
-
- void CreateDirectory(HLERequestContext& ctx) {
- const auto file_buffer = ctx.ReadBuffer();
- const std::string name = Common::StringFromBuffer(file_buffer);
-
- LOG_DEBUG(Service_FS, "called. directory={}", name);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(backend.CreateDirectory(name));
- }
-
- void DeleteDirectory(HLERequestContext& ctx) {
- const auto file_buffer = ctx.ReadBuffer();
- const std::string name = Common::StringFromBuffer(file_buffer);
-
- LOG_DEBUG(Service_FS, "called. directory={}", name);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(backend.DeleteDirectory(name));
- }
-
- void DeleteDirectoryRecursively(HLERequestContext& ctx) {
- const auto file_buffer = ctx.ReadBuffer();
- const std::string name = Common::StringFromBuffer(file_buffer);
-
- LOG_DEBUG(Service_FS, "called. directory={}", name);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(backend.DeleteDirectoryRecursively(name));
- }
-
- void CleanDirectoryRecursively(HLERequestContext& ctx) {
- const auto file_buffer = ctx.ReadBuffer();
- const std::string name = Common::StringFromBuffer(file_buffer);
-
- LOG_DEBUG(Service_FS, "called. Directory: {}", name);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(backend.CleanDirectoryRecursively(name));
- }
-
- void RenameFile(HLERequestContext& ctx) {
- const std::string src_name = Common::StringFromBuffer(ctx.ReadBuffer(0));
- const std::string dst_name = Common::StringFromBuffer(ctx.ReadBuffer(1));
-
- LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(backend.RenameFile(src_name, dst_name));
- }
-
- void OpenFile(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- const auto file_buffer = ctx.ReadBuffer();
- const std::string name = Common::StringFromBuffer(file_buffer);
-
- const auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>());
-
- LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, mode);
-
- FileSys::VirtualFile vfs_file{};
- auto result = backend.OpenFile(&vfs_file, name, mode);
- if (result != ResultSuccess) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
- return;
- }
-
- auto file = std::make_shared<IFile>(system, vfs_file);
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IFile>(std::move(file));
- }
-
- void OpenDirectory(HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
-
- const auto file_buffer = ctx.ReadBuffer();
- const std::string name = Common::StringFromBuffer(file_buffer);
- const auto mode = rp.PopRaw<OpenDirectoryMode>();
-
- LOG_DEBUG(Service_FS, "called. directory={}, mode={}", name, mode);
-
- FileSys::VirtualDir vfs_dir{};
- auto result = backend.OpenDirectory(&vfs_dir, name);
- if (result != ResultSuccess) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
- return;
- }
-
- auto directory = std::make_shared<IDirectory>(system, vfs_dir, mode);
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IDirectory>(std::move(directory));
- }
-
- void GetEntryType(HLERequestContext& ctx) {
- const auto file_buffer = ctx.ReadBuffer();
- const std::string name = Common::StringFromBuffer(file_buffer);
-
- LOG_DEBUG(Service_FS, "called. file={}", name);
-
- FileSys::EntryType vfs_entry_type{};
- auto result = backend.GetEntryType(&vfs_entry_type, name);
- if (result != ResultSuccess) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
- return;
- }
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(static_cast<u32>(vfs_entry_type));
- }
-
- void Commit(HLERequestContext& ctx) {
- LOG_WARNING(Service_FS, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void GetFreeSpaceSize(HLERequestContext& ctx) {
- LOG_DEBUG(Service_FS, "called");
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push(size.get_free_size());
- }
-
- void GetTotalSpaceSize(HLERequestContext& ctx) {
- LOG_DEBUG(Service_FS, "called");
-
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push(size.get_total_size());
- }
-
- void GetFileTimeStampRaw(HLERequestContext& ctx) {
- const auto file_buffer = ctx.ReadBuffer();
- const std::string name = Common::StringFromBuffer(file_buffer);
-
- LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name);
-
- FileSys::FileTimeStampRaw vfs_timestamp{};
- auto result = backend.GetFileTimeStampRaw(&vfs_timestamp, name);
- if (result != ResultSuccess) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
- return;
- }
-
- IPC::ResponseBuilder rb{ctx, 10};
- rb.Push(ResultSuccess);
- rb.PushRaw(vfs_timestamp);
- }
-
- void GetFileSystemAttribute(HLERequestContext& ctx) {
- LOG_WARNING(Service_FS, "(STUBBED) called");
-
- struct FileSystemAttribute {
- u8 dir_entry_name_length_max_defined;
- u8 file_entry_name_length_max_defined;
- u8 dir_path_name_length_max_defined;
- u8 file_path_name_length_max_defined;
- INSERT_PADDING_BYTES_NOINIT(0x5);
- u8 utf16_dir_entry_name_length_max_defined;
- u8 utf16_file_entry_name_length_max_defined;
- u8 utf16_dir_path_name_length_max_defined;
- u8 utf16_file_path_name_length_max_defined;
- INSERT_PADDING_BYTES_NOINIT(0x18);
- s32 dir_entry_name_length_max;
- s32 file_entry_name_length_max;
- s32 dir_path_name_length_max;
- s32 file_path_name_length_max;
- INSERT_PADDING_WORDS_NOINIT(0x5);
- s32 utf16_dir_entry_name_length_max;
- s32 utf16_file_entry_name_length_max;
- s32 utf16_dir_path_name_length_max;
- s32 utf16_file_path_name_length_max;
- INSERT_PADDING_WORDS_NOINIT(0x18);
- INSERT_PADDING_WORDS_NOINIT(0x1);
- };
- static_assert(sizeof(FileSystemAttribute) == 0xc0,
- "FileSystemAttribute has incorrect size");
-
- FileSystemAttribute savedata_attribute{};
- savedata_attribute.dir_entry_name_length_max_defined = true;
- savedata_attribute.file_entry_name_length_max_defined = true;
- savedata_attribute.dir_entry_name_length_max = 0x40;
- savedata_attribute.file_entry_name_length_max = 0x40;
-
- IPC::ResponseBuilder rb{ctx, 50};
- rb.Push(ResultSuccess);
- rb.PushRaw(savedata_attribute);
- }
-
-private:
- VfsDirectoryServiceWrapper backend;
- SizeGetter size;
-};
-
class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
public:
explicit ISaveDataInfoReader(Core::System& system_,
@@ -960,7 +430,7 @@ void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) {
save_data_controller->OpenSaveData(&dir, parameters.space_id, parameters.attribute);
if (result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
- rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
+ rb.Push(FileSys::ResultTargetNotFound);
return;
}
@@ -1127,7 +597,7 @@ void FSP_SRV::OpenPatchDataStorageByCurrentProcess(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called with storage_id={:02X}, title_id={:016X}", storage_id, title_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
+ rb.Push(FileSys::ResultTargetNotFound);
}
void 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 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/service/filesystem/filesystem.h"
+
+namespace Service::FileSystem {
+
+struct SizeGetter {
+ std::function<u64()> get_free_size;
+ std::function<u64()> get_total_size;
+
+ static SizeGetter FromStorageId(const FileSystemController& fsc, FileSys::StorageId id) {
+ return {
+ [&fsc, id] { return fsc.GetFreeSpaceSize(id); },
+ [&fsc, id] { return fsc.GetTotalSpaceSize(id); },
+ };
+ }
+};
+
+} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/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 @@
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/romfs_factory.h"
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
namespace Service::FileSystem {
diff --git a/src/core/hle/service/filesystem/save_data_controller.cpp b/src/core/hle/service/filesystem/save_data_controller.cpp
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,
auto save_data = factory->Create(space, attribute);
if (save_data == nullptr) {
- return FileSys::ERROR_ENTITY_NOT_FOUND;
+ return FileSys::ResultTargetNotFound;
}
*out_save_data = save_data;
@@ -56,7 +56,7 @@ Result SaveDataController::OpenSaveData(FileSys::VirtualDir* out_save_data,
const FileSys::SaveDataAttribute& attribute) {
auto save_data = factory->Open(space, attribute);
if (save_data == nullptr) {
- return FileSys::ERROR_ENTITY_NOT_FOUND;
+ return FileSys::ResultTargetNotFound;
}
*out_save_data = save_data;
@@ -67,7 +67,7 @@ Result SaveDataController::OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_
FileSys::SaveDataSpaceId space) {
auto save_data_space = factory->GetSaveDataSpaceDirectory(space);
if (save_data_space == nullptr) {
- return FileSys::ERROR_ENTITY_NOT_FOUND;
+ return FileSys::ResultTargetNotFound;
}
*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 @@
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/savedata_factory.h"
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
namespace Service::FileSystem {
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 @@
#include "common/settings.h"
#include "common/time_zone.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/service/glue/time/manager.h"
#include "core/hle/service/glue/time/time_zone_binary.h"
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 @@
#include <string>
#include "common/common_types.h"
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/service/glue/time/file_timestamp_worker.h"
#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
#include "core/hle/service/glue/time/worker.h"
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 @@
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/system_archive/system_archive.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/time/time_zone_binary.h"
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) {
server_manager->RegisterNamedService(
"hid:dbg", std::make_shared<IHidDebugServer>(system, resource_manager));
server_manager->RegisterNamedService(
- "hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager));
+ "hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings));
server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system));
diff --git a/src/core/hle/service/hid/hid_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
const auto parameters{rp.PopRaw<Parameters>()};
- LOG_INFO(Service_HID, "called, is_enabled={}, npad_id={}, applet_resource_user_id={}",
- parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id);
+ LOG_DEBUG(Service_HID, "called, is_enabled={}, npad_id={}, applet_resource_user_id={}",
+ parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id);
if (!IsNpadIdValid(parameters.npad_id)) {
IPC::ResponseBuilder rb{ctx, 3};
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 @@
#include "core/hle/service/hid/hid_system_server.h"
#include "core/hle/service/ipc_helpers.h"
+#include "core/hle/service/set/settings_types.h"
#include "hid_core/hid_result.h"
#include "hid_core/resource_manager.h"
+#include "hid_core/resources/hid_firmware_settings.h"
#include "hid_core/resources/npad/npad.h"
#include "hid_core/resources/npad/npad_types.h"
#include "hid_core/resources/npad/npad_vibration.h"
@@ -13,9 +15,10 @@
namespace Service::HID {
-IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource)
+IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
+ std::shared_ptr<HidFirmwareSettings> settings)
: ServiceFramework{system_, "hid:sys"}, service_context{system_, service_name},
- resource_manager{resource} {
+ resource_manager{resource}, firmware_settings{settings} {
// clang-format off
static const FunctionInfo functions[] = {
{31, nullptr, "SendKeyboardLockKeyEvent"},
@@ -25,7 +28,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
{131, nullptr, "ActivateSleepButton"},
{141, nullptr, "AcquireCaptureButtonEventHandle"},
{151, nullptr, "ActivateCaptureButton"},
- {161, nullptr, "GetPlatformConfig"},
+ {161, &IHidSystemServer::GetPlatformConfig, "GetPlatformConfig"},
{210, nullptr, "AcquireNfcDeviceUpdateEventHandle"},
{211, nullptr, "GetNpadsWithNfc"},
{212, nullptr, "AcquireNfcActivateEventHandle"},
@@ -80,7 +83,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
{520, nullptr, "EnableHandheldHids"},
{521, nullptr, "DisableHandheldHids"},
{522, nullptr, "SetJoyConRailEnabled"},
- {523, nullptr, "IsJoyConRailEnabled"},
+ {523, &IHidSystemServer::IsJoyConRailEnabled, "IsJoyConRailEnabled"},
{524, nullptr, "IsHandheldHidsEnabled"},
{525, &IHidSystemServer::IsJoyConAttachedOnAllRail, "IsJoyConAttachedOnAllRail"},
{540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"},
@@ -123,7 +126,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
{831, nullptr, "SetNotificationLedPatternWithTimeout"},
{832, nullptr, "PrepareHidsForNotificationWake"},
{850, &IHidSystemServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
- {851, nullptr, "EnableUsbFullKeyController"},
+ {851, &IHidSystemServer::EnableUsbFullKeyController, "EnableUsbFullKeyController"},
{852, nullptr, "IsUsbConnected"},
{870, &IHidSystemServer::IsHandheldButtonPressedOnConsoleMode, "IsHandheldButtonPressedOnConsoleMode"},
{900, nullptr, "ActivateInputDetector"},
@@ -148,7 +151,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
{1120, &IHidSystemServer::SetFirmwareHotfixUpdateSkipEnabled, "SetFirmwareHotfixUpdateSkipEnabled"},
{1130, &IHidSystemServer::InitializeUsbFirmwareUpdate, "InitializeUsbFirmwareUpdate"},
{1131, &IHidSystemServer::FinalizeUsbFirmwareUpdate, "FinalizeUsbFirmwareUpdate"},
- {1132, nullptr, "CheckUsbFirmwareUpdateRequired"},
+ {1132, &IHidSystemServer::CheckUsbFirmwareUpdateRequired, "CheckUsbFirmwareUpdateRequired"},
{1133, nullptr, "StartUsbFirmwareUpdate"},
{1134, nullptr, "GetUsbFirmwareUpdateState"},
{1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"},
@@ -239,6 +242,16 @@ IHidSystemServer::~IHidSystemServer() {
service_context.CloseEvent(unique_pad_connection_event);
};
+void IHidSystemServer::GetPlatformConfig(HLERequestContext& ctx) {
+ const auto platform_config = firmware_settings->GetPlatformConfig();
+
+ LOG_INFO(Service_HID, "called, platform_config={}", platform_config.raw);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushRaw(platform_config);
+}
+
void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
@@ -674,6 +687,16 @@ void IHidSystemServer::EndPermitVibrationSession(HLERequestContext& ctx) {
rb.Push(result);
}
+void IHidSystemServer::IsJoyConRailEnabled(HLERequestContext& ctx) {
+ const bool is_attached = true;
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, is_attached={}", is_attached);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(is_attached);
+}
+
void IHidSystemServer::IsJoyConAttachedOnAllRail(HLERequestContext& ctx) {
const bool is_attached = true;
@@ -727,7 +750,7 @@ void IHidSystemServer::AcquireUniquePadConnectionEventHandle(HLERequestContext&
}
void IHidSystemServer::GetUniquePadIds(HLERequestContext& ctx) {
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ LOG_DEBUG(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
@@ -752,6 +775,16 @@ void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
rb.Push(is_enabled);
}
+void IHidSystemServer::EnableUsbFullKeyController(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto is_enabled{rp.Pop<bool>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
void IHidSystemServer::IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx) {
const bool button_pressed = false;
@@ -798,6 +831,13 @@ void IHidSystemServer::FinalizeUsbFirmwareUpdate(HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
+void IHidSystemServer::CheckUsbFirmwareUpdateRequired(HLERequestContext& ctx) {
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
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;
namespace Service::HID {
class ResourceManager;
+class HidFirmwareSettings;
class IHidSystemServer final : public ServiceFramework<IHidSystemServer> {
public:
- explicit IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource);
+ explicit IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
+ std::shared_ptr<HidFirmwareSettings> settings);
~IHidSystemServer() override;
private:
+ void GetPlatformConfig(HLERequestContext& ctx);
void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx);
void EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx);
void DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx);
@@ -50,6 +53,7 @@ private:
void GetVibrationMasterVolume(HLERequestContext& ctx);
void BeginPermitVibrationSession(HLERequestContext& ctx);
void EndPermitVibrationSession(HLERequestContext& ctx);
+ void IsJoyConRailEnabled(HLERequestContext& ctx);
void IsJoyConAttachedOnAllRail(HLERequestContext& ctx);
void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx);
void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx);
@@ -58,12 +62,14 @@ private:
void GetUniquePadIds(HLERequestContext& ctx);
void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx);
void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
+ void EnableUsbFullKeyController(HLERequestContext& ctx);
void IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx);
void InitializeFirmwareUpdate(HLERequestContext& ctx);
void CheckFirmwareUpdateRequired(HLERequestContext& ctx);
void SetFirmwareHotfixUpdateSkipEnabled(HLERequestContext& ctx);
void InitializeUsbFirmwareUpdate(HLERequestContext& ctx);
void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx);
+ void CheckUsbFirmwareUpdateRequired(HLERequestContext& ctx);
void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx);
void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
void SetForceHandheldStyleVibration(HLERequestContext& ctx);
@@ -77,6 +83,7 @@ private:
Kernel::KEvent* unique_pad_connection_event;
KernelHelpers::ServiceContext service_context;
std::shared_ptr<ResourceManager> resource_manager;
+ std::shared_ptr<HidFirmwareSettings> firmware_settings;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/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 {
return result;
}
- if (result.module != ErrorModule::NFC) {
+ if (result.GetModule() != ErrorModule::NFC) {
return result;
}
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 @@
#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/glue_manager.h"
#include "core/hle/service/ipc_helpers.h"
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() {
settings.primary_album_storage = PrimaryAlbumStorage::SdCard;
settings.battery_percentage_flag = true;
settings.chinese_traditional_input_method = ChineseTraditionalInputMethod::Unknown0;
+ settings.vibration_master_volume = 1.0f;
return settings;
}
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 {
};
static_assert(sizeof(NotificationFlag) == 4, "NotificationFlag is an invalid size");
+struct PlatformConfig {
+ union {
+ u32 raw{};
+ BitField<0, 1, u32> has_rail_interface;
+ BitField<1, 1, u32> has_sio_mcu;
+ };
+};
+static_assert(sizeof(PlatformConfig) == 0x4, "PlatformConfig is an invalid size");
+
/// This is nn::settings::system::TvFlag
struct TvFlag {
union {
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&
const auto ver_file = romfs->GetFile("file");
if (ver_file == nullptr) {
return early_exit_failure("The system version archive didn't contain the file 'file'.",
- FileSys::ERROR_INVALID_ARGUMENT);
+ FileSys::ResultInvalidArgument);
}
auto data = ver_file->ReadAllBytes();
if (data.size() != sizeof(FirmwareVersionFormat)) {
return early_exit_failure("The system version file 'file' was not the correct size.",
- FileSys::ERROR_OUT_OF_BOUNDS);
+ FileSys::ResultOutOfRange);
}
std::memcpy(&out_firmware, data.data(), sizeof(FirmwareVersionFormat));
@@ -123,8 +123,8 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
{30, &ISystemSettingsServer::SetNotificationSettings, "SetNotificationSettings"},
{31, &ISystemSettingsServer::GetAccountNotificationSettings, "GetAccountNotificationSettings"},
{32, &ISystemSettingsServer::SetAccountNotificationSettings, "SetAccountNotificationSettings"},
- {35, nullptr, "GetVibrationMasterVolume"},
- {36, nullptr, "SetVibrationMasterVolume"},
+ {35, &ISystemSettingsServer::GetVibrationMasterVolume, "GetVibrationMasterVolume"},
+ {36, &ISystemSettingsServer::SetVibrationMasterVolume, "SetVibrationMasterVolume"},
{37, &ISystemSettingsServer::GetSettingsItemValueSize, "GetSettingsItemValueSize"},
{38, &ISystemSettingsServer::GetSettingsItemValue, "GetSettingsItemValue"},
{39, &ISystemSettingsServer::GetTvSettings, "GetTvSettings"},
@@ -133,10 +133,10 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
{42, nullptr, "SetEdid"},
{43, nullptr, "GetAudioOutputMode"},
{44, nullptr, "SetAudioOutputMode"},
- {45, nullptr, "IsForceMuteOnHeadphoneRemoved"},
- {46, nullptr, "SetForceMuteOnHeadphoneRemoved"},
+ {45, &ISystemSettingsServer::IsForceMuteOnHeadphoneRemoved, "IsForceMuteOnHeadphoneRemoved"},
+ {46, &ISystemSettingsServer::SetForceMuteOnHeadphoneRemoved, "SetForceMuteOnHeadphoneRemoved"},
{47, &ISystemSettingsServer::GetQuestFlag, "GetQuestFlag"},
- {48, nullptr, "SetQuestFlag"},
+ {48, &ISystemSettingsServer::SetQuestFlag, "SetQuestFlag"},
{49, nullptr, "GetDataDeletionSettings"},
{50, nullptr, "SetDataDeletionSettings"},
{51, nullptr, "GetInitialSystemAppletProgramId"},
@@ -152,7 +152,7 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
{61, &ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled, "SetUserSystemClockAutomaticCorrectionEnabled"},
{62, &ISystemSettingsServer::GetDebugModeFlag, "GetDebugModeFlag"},
{63, &ISystemSettingsServer::GetPrimaryAlbumStorage, "GetPrimaryAlbumStorage"},
- {64, nullptr, "SetPrimaryAlbumStorage"},
+ {64, &ISystemSettingsServer::SetPrimaryAlbumStorage, "SetPrimaryAlbumStorage"},
{65, nullptr, "GetUsb30EnableFlag"},
{66, nullptr, "SetUsb30EnableFlag"},
{67, nullptr, "GetBatteryLot"},
@@ -467,7 +467,7 @@ void ISystemSettingsServer::GetExternalSteadyClockSourceId(HLERequestContext& ct
LOG_INFO(Service_SET, "called");
Common::UUID id{};
- auto res = GetExternalSteadyClockSourceId(id);
+ const auto res = GetExternalSteadyClockSourceId(id);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(Common::UUID) / sizeof(u32)};
rb.Push(res);
@@ -478,9 +478,9 @@ void ISystemSettingsServer::SetExternalSteadyClockSourceId(HLERequestContext& ct
LOG_INFO(Service_SET, "called");
IPC::RequestParser rp{ctx};
- auto id{rp.PopRaw<Common::UUID>()};
+ const auto id{rp.PopRaw<Common::UUID>()};
- auto res = SetExternalSteadyClockSourceId(id);
+ const auto res = SetExternalSteadyClockSourceId(id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
@@ -490,7 +490,7 @@ void ISystemSettingsServer::GetUserSystemClockContext(HLERequestContext& ctx) {
LOG_INFO(Service_SET, "called");
Service::PSC::Time::SystemClockContext context{};
- auto res = GetUserSystemClockContext(context);
+ const auto res = GetUserSystemClockContext(context);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)};
rb.Push(res);
@@ -501,9 +501,9 @@ void ISystemSettingsServer::SetUserSystemClockContext(HLERequestContext& ctx) {
LOG_INFO(Service_SET, "called");
IPC::RequestParser rp{ctx};
- auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
+ const auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
- auto res = SetUserSystemClockContext(context);
+ const auto res = SetUserSystemClockContext(context);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
@@ -652,6 +652,29 @@ void ISystemSettingsServer::SetAccountNotificationSettings(HLERequestContext& ct
rb.Push(ResultSuccess);
}
+void ISystemSettingsServer::GetVibrationMasterVolume(HLERequestContext& ctx) {
+ f32 vibration_master_volume = {};
+ const auto result = GetVibrationMasterVolume(vibration_master_volume);
+
+ LOG_INFO(Service_SET, "called, master_volume={}", vibration_master_volume);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(result);
+ rb.Push(vibration_master_volume);
+}
+
+void ISystemSettingsServer::SetVibrationMasterVolume(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto vibration_master_volume = rp.PopRaw<f32>();
+
+ LOG_INFO(Service_SET, "called, elements={}", m_system_settings.vibration_master_volume);
+
+ const auto result = SetVibrationMasterVolume(vibration_master_volume);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
// FIXME: implement support for the real system_settings.ini
template <typename T>
@@ -683,6 +706,8 @@ static Settings GetSettings() {
ret["time"]["standard_user_clock_initial_year"] = ToBytes(s32{2023});
// HID
+ ret["hid"]["has_rail_interface"] = ToBytes(bool{true});
+ ret["hid"]["has_sio_mcu"] = ToBytes(bool{true});
ret["hid_debug"]["enables_debugpad"] = ToBytes(bool{true});
ret["hid_debug"]["manages_devices"] = ToBytes(bool{true});
ret["hid_debug"]["manages_touch_ic_i2c"] = ToBytes(bool{true});
@@ -703,6 +728,9 @@ static Settings GetSettings() {
// Settings
ret["settings_debug"]["is_debug_mode_enabled"] = ToBytes(bool{false});
+ // Error
+ ret["err"]["applet_auto_close"] = ToBytes(bool{false});
+
return ret;
}
@@ -789,15 +817,25 @@ void ISystemSettingsServer::SetTvSettings(HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
-void ISystemSettingsServer::GetDebugModeFlag(HLERequestContext& ctx) {
- bool is_debug_mode_enabled = false;
- GetSettingsItemValue<bool>(is_debug_mode_enabled, "settings_debug", "is_debug_mode_enabled");
-
- LOG_DEBUG(Service_SET, "called, is_debug_mode_enabled={}", is_debug_mode_enabled);
+void ISystemSettingsServer::IsForceMuteOnHeadphoneRemoved(HLERequestContext& ctx) {
+ LOG_INFO(Service_SET, "called, force_mute_on_headphone_removed={}",
+ m_system_settings.force_mute_on_headphone_removed);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(is_debug_mode_enabled);
+ rb.PushRaw(m_system_settings.force_mute_on_headphone_removed);
+}
+
+void ISystemSettingsServer::SetForceMuteOnHeadphoneRemoved(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ m_system_settings.force_mute_on_headphone_removed = rp.PopRaw<bool>();
+ SetSaveNeeded();
+
+ LOG_INFO(Service_SET, "called, force_mute_on_headphone_removed={}",
+ m_system_settings.force_mute_on_headphone_removed);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
}
void ISystemSettingsServer::GetQuestFlag(HLERequestContext& ctx) {
@@ -808,11 +846,22 @@ void ISystemSettingsServer::GetQuestFlag(HLERequestContext& ctx) {
rb.PushEnum(m_system_settings.quest_flag);
}
+void ISystemSettingsServer::SetQuestFlag(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ m_system_settings.quest_flag = rp.PopEnum<QuestFlag>();
+ SetSaveNeeded();
+
+ LOG_INFO(Service_SET, "called, quest_flag={}", m_system_settings.quest_flag);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
void ISystemSettingsServer::GetDeviceTimeZoneLocationName(HLERequestContext& ctx) {
LOG_INFO(Service_SET, "called");
Service::PSC::Time::LocationName name{};
- auto res = GetDeviceTimeZoneLocationName(name);
+ const auto res = GetDeviceTimeZoneLocationName(name);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::LocationName) / sizeof(u32)};
rb.Push(res);
@@ -825,7 +874,7 @@ void ISystemSettingsServer::SetDeviceTimeZoneLocationName(HLERequestContext& ctx
IPC::RequestParser rp{ctx};
auto name{rp.PopRaw<Service::PSC::Time::LocationName>()};
- auto res = SetDeviceTimeZoneLocationName(name);
+ const auto res = SetDeviceTimeZoneLocationName(name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
@@ -846,7 +895,7 @@ void ISystemSettingsServer::GetNetworkSystemClockContext(HLERequestContext& ctx)
LOG_INFO(Service_SET, "called");
Service::PSC::Time::SystemClockContext context{};
- auto res = GetNetworkSystemClockContext(context);
+ const auto res = GetNetworkSystemClockContext(context);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)};
rb.Push(res);
@@ -857,9 +906,9 @@ void ISystemSettingsServer::SetNetworkSystemClockContext(HLERequestContext& ctx)
LOG_INFO(Service_SET, "called");
IPC::RequestParser rp{ctx};
- auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
+ const auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
- auto res = SetNetworkSystemClockContext(context);
+ const auto res = SetNetworkSystemClockContext(context);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
@@ -869,7 +918,7 @@ void ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled(HLEReque
LOG_INFO(Service_SET, "called");
bool enabled{};
- auto res = IsUserSystemClockAutomaticCorrectionEnabled(enabled);
+ const auto res = IsUserSystemClockAutomaticCorrectionEnabled(enabled);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
@@ -882,12 +931,23 @@ void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled(HLERequ
IPC::RequestParser rp{ctx};
auto enabled{rp.Pop<bool>()};
- auto res = SetUserSystemClockAutomaticCorrectionEnabled(enabled);
+ const auto res = SetUserSystemClockAutomaticCorrectionEnabled(enabled);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
+void ISystemSettingsServer::GetDebugModeFlag(HLERequestContext& ctx) {
+ bool is_debug_mode_enabled = false;
+ GetSettingsItemValue<bool>(is_debug_mode_enabled, "settings_debug", "is_debug_mode_enabled");
+
+ LOG_DEBUG(Service_SET, "called, is_debug_mode_enabled={}", is_debug_mode_enabled);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(is_debug_mode_enabled);
+}
+
void ISystemSettingsServer::GetPrimaryAlbumStorage(HLERequestContext& ctx) {
LOG_INFO(Service_SET, "called, primary_album_storage={}",
m_system_settings.primary_album_storage);
@@ -897,6 +957,18 @@ void ISystemSettingsServer::GetPrimaryAlbumStorage(HLERequestContext& ctx) {
rb.PushEnum(m_system_settings.primary_album_storage);
}
+void ISystemSettingsServer::SetPrimaryAlbumStorage(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ m_system_settings.primary_album_storage = rp.PopEnum<PrimaryAlbumStorage>();
+ SetSaveNeeded();
+
+ LOG_INFO(Service_SET, "called, primary_album_storage={}",
+ m_system_settings.primary_album_storage);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
void ISystemSettingsServer::GetNfcEnableFlag(HLERequestContext& ctx) {
LOG_INFO(Service_SET, "called, nfc_enable_flag={}", m_system_settings.nfc_enable_flag);
@@ -1075,7 +1147,7 @@ void ISystemSettingsServer::SetExternalSteadyClockInternalOffset(HLERequestConte
IPC::RequestParser rp{ctx};
auto offset{rp.Pop<s64>()};
- auto res = SetExternalSteadyClockInternalOffset(offset);
+ const auto res = SetExternalSteadyClockInternalOffset(offset);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
@@ -1085,7 +1157,7 @@ void ISystemSettingsServer::GetExternalSteadyClockInternalOffset(HLERequestConte
LOG_DEBUG(Service_SET, "called.");
s64 offset{};
- auto res = GetExternalSteadyClockInternalOffset(offset);
+ const auto res = GetExternalSteadyClockInternalOffset(offset);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res);
@@ -1143,7 +1215,7 @@ void ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(HLERequestConte
LOG_INFO(Service_SET, "called");
Service::PSC::Time::SteadyClockTimePoint time_point{};
- auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point);
+ const auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res);
@@ -1156,7 +1228,7 @@ void ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(HLERequestConte
IPC::RequestParser rp{ctx};
auto time_point{rp.PopRaw<Service::PSC::Time::SteadyClockTimePoint>()};
- auto res = SetDeviceTimeZoneLocationUpdatedTime(time_point);
+ const auto res = SetDeviceTimeZoneLocationUpdatedTime(time_point);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
@@ -1167,7 +1239,7 @@ void ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime(
LOG_INFO(Service_SET, "called");
Service::PSC::Time::SteadyClockTimePoint time_point{};
- auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
+ const auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res);
@@ -1179,9 +1251,9 @@ void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime(
LOG_INFO(Service_SET, "called");
IPC::RequestParser rp{ctx};
- auto time_point{rp.PopRaw<Service::PSC::Time::SteadyClockTimePoint>()};
+ const auto time_point{rp.PopRaw<Service::PSC::Time::SteadyClockTimePoint>()};
- auto res = SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
+ const auto res = SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
@@ -1307,57 +1379,68 @@ Result ISystemSettingsServer::GetSettingsItemValue(std::vector<u8>& out_value,
R_SUCCEED();
}
-Result ISystemSettingsServer::GetExternalSteadyClockSourceId(Common::UUID& out_id) {
+Result ISystemSettingsServer::GetVibrationMasterVolume(f32& out_volume) const {
+ out_volume = m_system_settings.vibration_master_volume;
+ R_SUCCEED();
+}
+
+Result ISystemSettingsServer::SetVibrationMasterVolume(f32 volume) {
+ m_system_settings.vibration_master_volume = volume;
+ SetSaveNeeded();
+ R_SUCCEED();
+}
+
+Result ISystemSettingsServer::GetExternalSteadyClockSourceId(Common::UUID& out_id) const {
out_id = m_private_settings.external_clock_source_id;
R_SUCCEED();
}
-Result ISystemSettingsServer::SetExternalSteadyClockSourceId(Common::UUID id) {
+Result ISystemSettingsServer::SetExternalSteadyClockSourceId(const Common::UUID& id) {
m_private_settings.external_clock_source_id = id;
SetSaveNeeded();
R_SUCCEED();
}
Result ISystemSettingsServer::GetUserSystemClockContext(
- Service::PSC::Time::SystemClockContext& out_context) {
+ Service::PSC::Time::SystemClockContext& out_context) const {
out_context = m_system_settings.user_system_clock_context;
R_SUCCEED();
}
Result ISystemSettingsServer::SetUserSystemClockContext(
- Service::PSC::Time::SystemClockContext& context) {
+ const Service::PSC::Time::SystemClockContext& context) {
m_system_settings.user_system_clock_context = context;
SetSaveNeeded();
R_SUCCEED();
}
Result ISystemSettingsServer::GetDeviceTimeZoneLocationName(
- Service::PSC::Time::LocationName& out_name) {
+ Service::PSC::Time::LocationName& out_name) const {
out_name = m_system_settings.device_time_zone_location_name;
R_SUCCEED();
}
Result ISystemSettingsServer::SetDeviceTimeZoneLocationName(
- Service::PSC::Time::LocationName& name) {
+ const Service::PSC::Time::LocationName& name) {
m_system_settings.device_time_zone_location_name = name;
SetSaveNeeded();
R_SUCCEED();
}
Result ISystemSettingsServer::GetNetworkSystemClockContext(
- Service::PSC::Time::SystemClockContext& out_context) {
+ Service::PSC::Time::SystemClockContext& out_context) const {
out_context = m_system_settings.network_system_clock_context;
R_SUCCEED();
}
Result ISystemSettingsServer::SetNetworkSystemClockContext(
- Service::PSC::Time::SystemClockContext& context) {
+ const Service::PSC::Time::SystemClockContext& context) {
m_system_settings.network_system_clock_context = context;
SetSaveNeeded();
R_SUCCEED();
}
-Result ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) {
+Result ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) const {
out_enabled = m_system_settings.user_system_clock_automatic_correction_enabled;
R_SUCCEED();
}
@@ -1374,32 +1457,32 @@ Result ISystemSettingsServer::SetExternalSteadyClockInternalOffset(s64 offset) {
R_SUCCEED();
}
-Result ISystemSettingsServer::GetExternalSteadyClockInternalOffset(s64& out_offset) {
+Result ISystemSettingsServer::GetExternalSteadyClockInternalOffset(s64& out_offset) const {
out_offset = m_private_settings.external_steady_clock_internal_offset;
R_SUCCEED();
}
Result ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(
- Service::PSC::Time::SteadyClockTimePoint& out_time_point) {
+ Service::PSC::Time::SteadyClockTimePoint& out_time_point) const {
out_time_point = m_system_settings.device_time_zone_location_updated_time;
R_SUCCEED();
}
Result ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(
- Service::PSC::Time::SteadyClockTimePoint& time_point) {
+ const Service::PSC::Time::SteadyClockTimePoint& time_point) {
m_system_settings.device_time_zone_location_updated_time = time_point;
SetSaveNeeded();
R_SUCCEED();
}
Result ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime(
- Service::PSC::Time::SteadyClockTimePoint& out_time_point) {
+ Service::PSC::Time::SteadyClockTimePoint& out_time_point) const {
out_time_point = m_system_settings.user_system_clock_automatic_correction_updated_time_point;
R_SUCCEED();
}
Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime(
- Service::PSC::Time::SteadyClockTimePoint out_time_point) {
+ const Service::PSC::Time::SteadyClockTimePoint& out_time_point) {
m_system_settings.user_system_clock_automatic_correction_updated_time_point = out_time_point;
SetSaveNeeded();
R_SUCCEED();
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:
return result;
}
- Result GetExternalSteadyClockSourceId(Common::UUID& out_id);
- Result SetExternalSteadyClockSourceId(Common::UUID id);
- Result GetUserSystemClockContext(Service::PSC::Time::SystemClockContext& out_context);
- Result SetUserSystemClockContext(Service::PSC::Time::SystemClockContext& context);
- Result GetDeviceTimeZoneLocationName(Service::PSC::Time::LocationName& out_name);
- Result SetDeviceTimeZoneLocationName(Service::PSC::Time::LocationName& name);
- Result GetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& out_context);
- Result SetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& context);
- Result IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled);
+ Result GetVibrationMasterVolume(f32& out_volume) const;
+ Result SetVibrationMasterVolume(f32 volume);
+ Result GetExternalSteadyClockSourceId(Common::UUID& out_id) const;
+ Result SetExternalSteadyClockSourceId(const Common::UUID& id);
+ Result GetUserSystemClockContext(Service::PSC::Time::SystemClockContext& out_context) const;
+ Result SetUserSystemClockContext(const Service::PSC::Time::SystemClockContext& context);
+ Result GetDeviceTimeZoneLocationName(Service::PSC::Time::LocationName& out_name) const;
+ Result SetDeviceTimeZoneLocationName(const Service::PSC::Time::LocationName& name);
+ Result GetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& out_context) const;
+ Result SetNetworkSystemClockContext(const Service::PSC::Time::SystemClockContext& context);
+ Result IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) const;
Result SetUserSystemClockAutomaticCorrectionEnabled(bool enabled);
Result SetExternalSteadyClockInternalOffset(s64 offset);
- Result GetExternalSteadyClockInternalOffset(s64& out_offset);
+ Result GetExternalSteadyClockInternalOffset(s64& out_offset) const;
Result GetDeviceTimeZoneLocationUpdatedTime(
- Service::PSC::Time::SteadyClockTimePoint& out_time_point);
+ Service::PSC::Time::SteadyClockTimePoint& out_time_point) const;
Result SetDeviceTimeZoneLocationUpdatedTime(
- Service::PSC::Time::SteadyClockTimePoint& time_point);
+ const Service::PSC::Time::SteadyClockTimePoint& time_point);
Result GetUserSystemClockAutomaticCorrectionUpdatedTime(
- Service::PSC::Time::SteadyClockTimePoint& out_time_point);
+ Service::PSC::Time::SteadyClockTimePoint& out_time_point) const;
Result SetUserSystemClockAutomaticCorrectionUpdatedTime(
- Service::PSC::Time::SteadyClockTimePoint time_point);
+ const Service::PSC::Time::SteadyClockTimePoint& time_point);
private:
void SetLanguageCode(HLERequestContext& ctx);
@@ -89,12 +91,17 @@ private:
void SetNotificationSettings(HLERequestContext& ctx);
void GetAccountNotificationSettings(HLERequestContext& ctx);
void SetAccountNotificationSettings(HLERequestContext& ctx);
+ void GetVibrationMasterVolume(HLERequestContext& ctx);
+ void SetVibrationMasterVolume(HLERequestContext& ctx);
void GetSettingsItemValueSize(HLERequestContext& ctx);
void GetSettingsItemValue(HLERequestContext& ctx);
void GetTvSettings(HLERequestContext& ctx);
void SetTvSettings(HLERequestContext& ctx);
+ void IsForceMuteOnHeadphoneRemoved(HLERequestContext& ctx);
+ void SetForceMuteOnHeadphoneRemoved(HLERequestContext& ctx);
void GetDebugModeFlag(HLERequestContext& ctx);
void GetQuestFlag(HLERequestContext& ctx);
+ void SetQuestFlag(HLERequestContext& ctx);
void GetDeviceTimeZoneLocationName(HLERequestContext& ctx);
void SetDeviceTimeZoneLocationName(HLERequestContext& ctx);
void SetRegionCode(HLERequestContext& ctx);
@@ -103,6 +110,7 @@ private:
void IsUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
void SetUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
void GetPrimaryAlbumStorage(HLERequestContext& ctx);
+ void SetPrimaryAlbumStorage(HLERequestContext& ctx);
void GetNfcEnableFlag(HLERequestContext& ctx);
void SetNfcEnableFlag(HLERequestContext& ctx);
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 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/file_sys/control_metadata.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs/vfs.h"
namespace Core {
class System;
diff --git a/src/core/loader/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 @@
#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/romfs_factory.h"
-#include "core/file_sys/vfs_offset.h"
+#include "core/file_sys/vfs/vfs_offset.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index dc3883528..1a0138697 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -68,8 +68,8 @@ json GetReportCommonData(u64 title_id, Result result, const std::string& timesta
auto out = json{
{"title_id", fmt::format("{:016X}", title_id)},
{"result_raw", fmt::format("{:08X}", result.raw)},
- {"result_module", fmt::format("{:08X}", static_cast<u32>(result.module.Value()))},
- {"result_description", fmt::format("{:08X}", result.description.Value())},
+ {"result_module", fmt::format("{:08X}", static_cast<u32>(result.GetModule()))},
+ {"result_description", fmt::format("{:08X}", result.GetDescription())},
{"timestamp", timestamp},
};
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 @@
#include "core/core.h"
#include "core/file_sys/common_funcs.h"
#include "core/file_sys/content_archive.h"
-#include "core/file_sys/mode.h"
+#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
@@ -159,7 +159,7 @@ inline InstallResult InstallNSP(Core::System& system, FileSys::VfsFilesystem& vf
};
std::shared_ptr<FileSys::NSP> nsp;
- FileSys::VirtualFile file = vfs.OpenFile(filename, FileSys::Mode::Read);
+ FileSys::VirtualFile file = vfs.OpenFile(filename, FileSys::OpenMode::Read);
if (boost::to_lower_copy(file->GetName()).ends_with(std::string("nsp"))) {
nsp = std::make_shared<FileSys::NSP>(file);
if (nsp->IsExtractedType()) {
@@ -224,7 +224,8 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem& vfs, const std::string&
return true;
};
- const auto nca = std::make_shared<FileSys::NCA>(vfs.OpenFile(filename, FileSys::Mode::Read));
+ const auto nca =
+ std::make_shared<FileSys::NCA>(vfs.OpenFile(filename, FileSys::OpenMode::Read));
const auto id = nca->GetStatus();
// Game updates necessary are missing base RomFS
@@ -345,8 +346,8 @@ inline std::vector<std::string> VerifyInstalledContents(
inline GameVerificationResult VerifyGameContents(
Core::System& system, const std::string& game_path,
const std::function<bool(size_t, size_t)>& callback) {
- const auto loader =
- Loader::GetLoader(system, system.GetFilesystem()->OpenFile(game_path, FileSys::Mode::Read));
+ const auto loader = Loader::GetLoader(
+ system, system.GetFilesystem()->OpenFile(game_path, FileSys::OpenMode::Read));
if (loader == nullptr) {
return GameVerificationResult::NotImplemented;
}
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() {
original_npad_type = npad_type;
}
- SetPollingMode(EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::Active);
+ // Disable special features before disconnecting
+ if (controller.right_polling_mode != Common::Input::PollingMode::Active) {
+ SetPollingMode(EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::Active);
+ }
+
Disconnect();
if (player.connected) {
Connect();
@@ -1241,7 +1245,12 @@ bool EmulatedController::SetVibration(DeviceIndex device_index, const VibrationV
return false;
}
- last_vibration_value = vibration;
+ // Skip duplicated vibrations
+ if (last_vibration_value[index] == vibration) {
+ return Settings::values.vibration_enabled.GetValue();
+ }
+
+ last_vibration_value[index] = vibration;
if (!Settings::values.vibration_enabled) {
return false;
@@ -1272,7 +1281,10 @@ bool EmulatedController::SetVibration(DeviceIndex device_index, const VibrationV
}
VibrationValue EmulatedController::GetActualVibrationValue(DeviceIndex device_index) const {
- return last_vibration_value;
+ if (device_index >= DeviceIndex::MaxDeviceIndex) {
+ return Core::HID::DEFAULT_VIBRATION_VALUE;
+ }
+ return last_vibration_value[static_cast<std::size_t>(device_index)];
}
bool 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:
f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
u32 turbo_button_state{0};
std::size_t nfc_handles{0};
- VibrationValue last_vibration_value{DEFAULT_VIBRATION_VALUE};
+ std::array<VibrationValue, 2> last_vibration_value{DEFAULT_VIBRATION_VALUE,
+ DEFAULT_VIBRATION_VALUE};
// Temporary values to avoid doing changes while the controller is in configuring mode
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 {
f32 low_frequency{};
f32 high_amplitude{};
f32 high_frequency{};
+ bool operator==(const VibrationValue& b) {
+ if (low_amplitude != b.low_amplitude || high_amplitude != b.high_amplitude) {
+ return false;
+ }
+ if (low_frequency != b.low_amplitude || high_frequency != b.high_frequency) {
+ return false;
+ }
+ return true;
+ }
};
static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size.");
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 @@
#include "core/core_timing.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/service/ipc_helpers.h"
+#include "core/hle/service/set/system_settings_server.h"
+#include "core/hle/service/sm/sm.h"
#include "hid_core/hid_core.h"
#include "hid_core/hid_util.h"
#include "hid_core/resource_manager.h"
@@ -180,7 +182,11 @@ void ResourceManager::InitializeHidCommonSampler() {
debug_pad->SetAppletResource(applet_resource, &shared_mutex);
digitizer->SetAppletResource(applet_resource, &shared_mutex);
keyboard->SetAppletResource(applet_resource, &shared_mutex);
- npad->SetNpadExternals(applet_resource, &shared_mutex, handheld_config);
+
+ const auto settings =
+ system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys");
+ npad->SetNpadExternals(applet_resource, &shared_mutex, handheld_config, settings);
+
six_axis->SetAppletResource(applet_resource, &shared_mutex);
mouse->SetAppletResource(applet_resource, &shared_mutex);
debug_mouse->SetAppletResource(applet_resource, &shared_mutex);
@@ -373,6 +379,10 @@ Result ResourceManager::SendVibrationValue(u64 aruid,
device = GetNSVibrationDevice(handle);
}
if (device != nullptr) {
+ // Prevent sending vibrations to an inactive vibration handle
+ if (!device->IsActive()) {
+ return ResultSuccess;
+ }
result = device->SendVibrationValue(value);
}
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() {
const auto npad_index = NpadIdTypeToIndex(npad_id_type);
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid_index);
- if (!data->flag.is_assigned) {
+ if (data == nullptr || !data->flag.is_assigned) {
continue;
}
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) {
m_set_sys->GetSettingsItemValue<bool>(is_touch_firmware_auto_update_disabled, "hid_debug",
"touch_firmware_auto_update_disabled");
+ bool has_rail_interface{};
+ bool has_sio_mcu{};
+ m_set_sys->GetSettingsItemValue<bool>(has_rail_interface, "hid", "has_rail_interface");
+ m_set_sys->GetSettingsItemValue<bool>(has_sio_mcu, "hid", "has_sio_mcu");
+ platform_config.has_rail_interface.Assign(has_rail_interface);
+ platform_config.has_sio_mcu.Assign(has_sio_mcu);
+
is_initialized = true;
}
@@ -103,4 +110,9 @@ HidFirmwareSettings::FeaturesPerId HidFirmwareSettings::FeaturesDisabledPerId()
return features_per_id_disabled;
}
+Set::PlatformConfig HidFirmwareSettings::GetPlatformConfig() {
+ LoadSettings(false);
+ return platform_config;
+}
+
} // 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 @@
#pragma once
#include "common/common_types.h"
+#include "core/hle/service/set/settings_types.h"
namespace Core {
class System;
@@ -39,6 +40,7 @@ public:
FirmwareSetting GetFirmwareUpdateFailure();
FeaturesPerId FeaturesDisabledPerId();
+ Set::PlatformConfig GetPlatformConfig();
private:
bool is_initialized{};
@@ -57,6 +59,7 @@ private:
bool is_touch_firmware_auto_update_disabled{};
FirmwareSetting is_firmware_update_failure{};
FeaturesPerId features_per_id_disabled{};
+ Set::PlatformConfig platform_config{};
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
};
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) {
void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource,
std::recursive_mutex* shared_mutex,
- std::shared_ptr<HandheldConfig> handheld_config) {
+ std::shared_ptr<HandheldConfig> handheld_config,
+ std::shared_ptr<Service::Set::ISystemSettingsServer> settings) {
applet_resource_holder.applet_resource = resource;
applet_resource_holder.shared_mutex = shared_mutex;
applet_resource_holder.shared_npad_resource = &npad_resource;
applet_resource_holder.handheld_config = handheld_config;
+ vibration_handler.SetSettingsService(settings);
+
for (auto& abstract_pad : abstracted_pads) {
abstract_pad.SetExternals(&applet_resource_holder, nullptr, nullptr, nullptr, nullptr,
&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 {
class ServiceContext;
} // namespace Service::KernelHelpers
+namespace Service::Set {
+class ISystemSettingsServer;
+}
+
union Result;
namespace Service::HID {
@@ -128,7 +132,8 @@ public:
void UnregisterAppletResourceUserId(u64 aruid);
void SetNpadExternals(std::shared_ptr<AppletResource> resource,
std::recursive_mutex* shared_mutex,
- std::shared_ptr<HandheldConfig> handheld_config);
+ std::shared_ptr<HandheldConfig> handheld_config,
+ std::shared_ptr<Service::Set::ISystemSettingsServer> settings);
AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
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 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
+#include "core/hle/service/set/system_settings_server.h"
#include "hid_core/hid_result.h"
#include "hid_core/resources/npad/npad_vibration.h"
@@ -13,10 +14,11 @@ NpadVibration::~NpadVibration() = default;
Result NpadVibration::Activate() {
std::scoped_lock lock{mutex};
- const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume();
- // if (master_volume < 0.0f || master_volume > 1.0f) {
- // return ResultVibrationStrengthOutOfRange;
- // }
+ f32 master_volume = 1.0f;
+ m_set_sys->GetVibrationMasterVolume(master_volume);
+ if (master_volume < 0.0f || master_volume > 1.0f) {
+ return ResultVibrationStrengthOutOfRange;
+ }
volume = master_volume;
return ResultSuccess;
@@ -26,6 +28,12 @@ Result NpadVibration::Deactivate() {
return ResultSuccess;
}
+Result NpadVibration::SetSettingsService(
+ std::shared_ptr<Service::Set::ISystemSettingsServer> settings) {
+ m_set_sys = settings;
+ return ResultSuccess;
+}
+
Result NpadVibration::SetVibrationMasterVolume(f32 master_volume) {
std::scoped_lock lock{mutex};
@@ -34,7 +42,7 @@ Result NpadVibration::SetVibrationMasterVolume(f32 master_volume) {
}
volume = master_volume;
- // nn::settings::system::SetVibrationMasterVolume(master_volume);
+ m_set_sys->SetVibrationMasterVolume(master_volume);
return ResultSuccess;
}
@@ -48,10 +56,11 @@ Result NpadVibration::GetVibrationVolume(f32& out_volume) const {
Result NpadVibration::GetVibrationMasterVolume(f32& out_volume) const {
std::scoped_lock lock{mutex};
- const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume();
- // if (master_volume < 0.0f || master_volume > 1.0f) {
- // return ResultVibrationStrengthOutOfRange;
- // }
+ f32 master_volume = 1.0f;
+ m_set_sys->GetVibrationMasterVolume(master_volume);
+ if (master_volume < 0.0f || master_volume > 1.0f) {
+ return ResultVibrationStrengthOutOfRange;
+ }
out_volume = master_volume;
return ResultSuccess;
@@ -67,10 +76,11 @@ Result NpadVibration::BeginPermitVibrationSession(u64 aruid) {
Result NpadVibration::EndPermitVibrationSession() {
std::scoped_lock lock{mutex};
- const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume();
- // if (master_volume < 0.0f || master_volume > 1.0f) {
- // return ResultVibrationStrengthOutOfRange;
- // }
+ f32 master_volume = 1.0f;
+ m_set_sys->GetVibrationMasterVolume(master_volume);
+ if (master_volume < 0.0f || master_volume > 1.0f) {
+ return ResultVibrationStrengthOutOfRange;
+ }
volume = master_volume;
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 @@
#include "common/common_types.h"
#include "core/hle/result.h"
+namespace Service::Set {
+class ISystemSettingsServer;
+}
+
namespace Service::HID {
class NpadVibration final {
@@ -18,6 +22,7 @@ public:
Result Activate();
Result Deactivate();
+ Result SetSettingsService(std::shared_ptr<Service::Set::ISystemSettingsServer> settings);
Result SetVibrationMasterVolume(f32 master_volume);
Result GetVibrationVolume(f32& out_volume) const;
Result GetVibrationMasterVolume(f32& out_volume) const;
@@ -31,6 +36,8 @@ private:
f32 volume{};
u64 session_aruid{};
mutable std::mutex mutex;
+
+ std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
};
} // 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() {
return ResultSuccess;
}
+bool NpadVibrationBase::IsActive() const {
+ return ref_counter > 0;
+}
+
bool NpadVibrationBase::IsVibrationMounted() const {
return is_mounted;
}
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:
virtual Result Activate();
virtual Result Deactivate();
+ bool IsActive() const;
bool IsVibrationMounted() const;
protected:
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 {
callback = std::move(finished);
emit MainWindowDisplayError(
tr("Error Code: %1-%2 (0x%3)")
- .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
- .arg(error.description, 4, 10, QChar::fromLatin1('0'))
+ .arg(static_cast<u32>(error.GetModule()) + 2000, 4, 10, QChar::fromLatin1('0'))
+ .arg(error.GetDescription(), 4, 10, QChar::fromLatin1('0'))
.arg(error.raw, 8, 16, QChar::fromLatin1('0')),
tr("An error has occurred.\nPlease try again or contact the developer of the software."));
}
@@ -38,8 +38,8 @@ void QtErrorDisplay::ShowErrorWithTimestamp(Result error, std::chrono::seconds t
const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count());
emit MainWindowDisplayError(
tr("Error Code: %1-%2 (0x%3)")
- .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
- .arg(error.description, 4, 10, QChar::fromLatin1('0'))
+ .arg(static_cast<u32>(error.GetModule()) + 2000, 4, 10, QChar::fromLatin1('0'))
+ .arg(error.GetDescription(), 4, 10, QChar::fromLatin1('0'))
.arg(error.raw, 8, 16, QChar::fromLatin1('0')),
tr("An error occurred on %1 at %2.\nPlease try again or contact the developer of the "
"software.")
@@ -53,8 +53,8 @@ void QtErrorDisplay::ShowCustomErrorText(Result error, std::string dialog_text,
callback = std::move(finished);
emit MainWindowDisplayError(
tr("Error Code: %1-%2 (0x%3)")
- .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
- .arg(error.description, 4, 10, QChar::fromLatin1('0'))
+ .arg(static_cast<u32>(error.GetModule()) + 2000, 4, 10, QChar::fromLatin1('0'))
+ .arg(error.GetDescription(), 4, 10, QChar::fromLatin1('0'))
.arg(error.raw, 8, 16, QChar::fromLatin1('0')),
tr("An error has occurred.\n\n%1\n\n%2")
.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 @@
#include <QList>
#include "configuration/shared_widget.h"
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
#include "frontend_common/config.h"
#include "vk_device_info.h"
#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 @@
#include <QList>
-#include "core/file_sys/vfs_types.h"
+#include "core/file_sys/vfs/vfs_types.h"
namespace Core {
class 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 @@
#include "core/file_sys/card_image.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
-#include "core/file_sys/mode.h"
+#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
@@ -347,7 +347,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
if (!is_dir &&
(HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
- const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read);
+ const auto file = vfs->OpenFile(physical_name, FileSys::OpenMode::Read);
if (!file) {
return true;
}
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 @@
#include "configuration/configure_per_game.h"
#include "configuration/configure_tas.h"
#include "core/file_sys/romfs_factory.h"
-#include "core/file_sys/vfs.h"
-#include "core/file_sys/vfs_real.h"
+#include "core/file_sys/vfs/vfs.h"
+#include "core/file_sys/vfs/vfs_real.h"
#include "core/frontend/applets/cabinet.h"
#include "core/frontend/applets/controller.h"
#include "core/frontend/applets/general_frontend.h"
@@ -56,7 +56,7 @@
// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows
// defines.
static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(
- const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::Mode mode) {
+ const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::OpenMode mode) {
return vfs->CreateDirectory(path, mode);
}
@@ -1880,7 +1880,7 @@ bool GMainWindow::SelectAndSetCurrentUser(
void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) {
// Ensure all NCAs are registered before launching the game
- const auto file = vfs->OpenFile(filepath, FileSys::Mode::Read);
+ const auto file = vfs->OpenFile(filepath, FileSys::OpenMode::Read);
if (!file) {
return;
}
@@ -2274,7 +2274,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
open_target = tr("Save Data");
const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
auto vfs_nand_dir =
- vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read);
+ vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read);
if (has_user_save) {
// User save data
@@ -2653,7 +2653,7 @@ void GMainWindow::RemoveCustomConfiguration(u64 program_id, const std::string& g
void GMainWindow::RemoveCacheStorage(u64 program_id) {
const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
auto vfs_nand_dir =
- vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read);
+ vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read);
const auto cache_storage_path = FileSys::SaveDataFactory::GetFullPath(
{}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::CacheStorage,
@@ -2673,7 +2673,8 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
"cancelled the operation."));
};
- const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read));
+ const auto loader =
+ Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::OpenMode::Read));
if (loader == nullptr) {
failed();
return;
@@ -2717,7 +2718,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), installed};
auto romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, packed_update_raw, false);
- const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::Mode::ReadWrite);
+ const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::OpenMode::ReadWrite);
if (out == nullptr) {
failed();
@@ -3015,7 +3016,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga
system->GetContentProvider()};
const auto control = pm.GetControlMetadata();
const auto loader =
- Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read));
+ Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::OpenMode::Read));
game_title = fmt::format("{:016X}", program_id);
if (control.first != nullptr) {
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 @@
#include "core/cpu_manager.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/registered_cache.h"
-#include "core/file_sys/vfs_real.h"
+#include "core/file_sys/vfs/vfs_real.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "core/telemetry_session.h"