summaryrefslogtreecommitdiffstats
path: root/src/core/file_sys
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/file_sys')
-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)55
-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
85 files changed, 2390 insertions, 217 deletions
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..627d5d251 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>
@@ -28,16 +28,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 +72,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 +96,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 +143,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 +155,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 +182,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 +191,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 +264,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_) {}
@@ -298,11 +296,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 +329,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 +351,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 +371,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 +455,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 +506,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;