From 0f9288e38d80c6c63a545934557501fae40d3d83 Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Tue, 16 Jan 2024 06:23:01 +0100 Subject: vfs: Move vfs files to their own directory --- src/core/file_sys/bis_factory.cpp | 2 +- src/core/file_sys/bis_factory.h | 2 +- src/core/file_sys/card_image.cpp | 4 +- src/core/file_sys/card_image.h | 2 +- src/core/file_sys/content_archive.cpp | 2 +- src/core/file_sys/content_archive.h | 2 +- src/core/file_sys/control_metadata.cpp | 2 +- src/core/file_sys/control_metadata.h | 2 +- src/core/file_sys/fsmitm_romfsbuild.cpp | 4 +- src/core/file_sys/fsmitm_romfsbuild.h | 2 +- src/core/file_sys/fssystem/fs_i_storage.h | 2 +- .../fssystem_aes_ctr_counter_extended_storage.cpp | 2 +- .../file_sys/fssystem/fssystem_aes_ctr_storage.h | 2 +- src/core/file_sys/fssystem/fssystem_bucket_tree.h | 2 +- .../fssystem/fssystem_compressed_storage.h | 2 +- ...hierarchical_integrity_verification_storage.cpp | 2 +- ...m_hierarchical_integrity_verification_storage.h | 2 +- .../fssystem_hierarchical_sha256_storage.h | 2 +- .../file_sys/fssystem/fssystem_indirect_storage.h | 4 +- .../fssystem/fssystem_integrity_romfs_storage.h | 2 +- .../fssystem/fssystem_nca_file_system_driver.cpp | 4 +- .../fssystem/fssystem_nca_file_system_driver.h | 2 +- src/core/file_sys/fssystem/fssystem_nca_reader.cpp | 2 +- src/core/file_sys/ips_layer.cpp | 2 +- src/core/file_sys/ips_layer.h | 2 +- src/core/file_sys/kernel_executable.cpp | 2 +- src/core/file_sys/kernel_executable.h | 2 +- src/core/file_sys/nca_metadata.cpp | 2 +- src/core/file_sys/nca_metadata.h | 2 +- src/core/file_sys/partition_filesystem.cpp | 2 +- src/core/file_sys/partition_filesystem.h | 2 +- src/core/file_sys/patch_manager.cpp | 6 +- src/core/file_sys/patch_manager.h | 2 +- src/core/file_sys/program_metadata.cpp | 2 +- src/core/file_sys/program_metadata.h | 2 +- src/core/file_sys/registered_cache.cpp | 2 +- src/core/file_sys/registered_cache.h | 2 +- src/core/file_sys/romfs.cpp | 10 +- src/core/file_sys/romfs.h | 2 +- src/core/file_sys/romfs_factory.h | 2 +- src/core/file_sys/savedata_factory.cpp | 2 +- src/core/file_sys/savedata_factory.h | 2 +- src/core/file_sys/sdmc_factory.cpp | 2 +- src/core/file_sys/sdmc_factory.h | 2 +- src/core/file_sys/submission_package.h | 2 +- src/core/file_sys/system_archive/mii_model.cpp | 2 +- src/core/file_sys/system_archive/mii_model.h | 2 +- src/core/file_sys/system_archive/ng_word.cpp | 2 +- src/core/file_sys/system_archive/ng_word.h | 2 +- src/core/file_sys/system_archive/shared_font.cpp | 2 +- src/core/file_sys/system_archive/shared_font.h | 2 +- src/core/file_sys/system_archive/system_archive.h | 2 +- .../file_sys/system_archive/system_version.cpp | 2 +- src/core/file_sys/system_archive/system_version.h | 2 +- .../file_sys/system_archive/time_zone_binary.cpp | 2 +- .../file_sys/system_archive/time_zone_binary.h | 2 +- src/core/file_sys/vfs.cpp | 552 --------------------- src/core/file_sys/vfs.h | 327 ------------ src/core/file_sys/vfs/vfs.cpp | 552 +++++++++++++++++++++ src/core/file_sys/vfs/vfs.h | 327 ++++++++++++ src/core/file_sys/vfs/vfs_cached.cpp | 63 +++ src/core/file_sys/vfs/vfs_cached.h | 31 ++ src/core/file_sys/vfs/vfs_concat.cpp | 192 +++++++ src/core/file_sys/vfs/vfs_concat.h | 57 +++ src/core/file_sys/vfs/vfs_layered.cpp | 132 +++++ src/core/file_sys/vfs/vfs_layered.h | 46 ++ src/core/file_sys/vfs/vfs_offset.cpp | 98 ++++ src/core/file_sys/vfs/vfs_offset.h | 50 ++ src/core/file_sys/vfs/vfs_real.cpp | 528 ++++++++++++++++++++ src/core/file_sys/vfs/vfs_real.h | 145 ++++++ src/core/file_sys/vfs/vfs_static.h | 80 +++ src/core/file_sys/vfs/vfs_types.h | 29 ++ src/core/file_sys/vfs/vfs_vector.cpp | 133 +++++ src/core/file_sys/vfs/vfs_vector.h | 131 +++++ src/core/file_sys/vfs_cached.cpp | 63 --- src/core/file_sys/vfs_cached.h | 31 -- src/core/file_sys/vfs_concat.cpp | 192 ------- src/core/file_sys/vfs_concat.h | 57 --- src/core/file_sys/vfs_layered.cpp | 132 ----- src/core/file_sys/vfs_layered.h | 46 -- src/core/file_sys/vfs_offset.cpp | 98 ---- src/core/file_sys/vfs_offset.h | 50 -- src/core/file_sys/vfs_real.cpp | 528 -------------------- src/core/file_sys/vfs_real.h | 145 ------ src/core/file_sys/vfs_static.h | 80 --- src/core/file_sys/vfs_types.h | 29 -- src/core/file_sys/vfs_vector.cpp | 133 ----- src/core/file_sys/vfs_vector.h | 131 ----- src/core/file_sys/xts_archive.cpp | 2 +- src/core/file_sys/xts_archive.h | 2 +- 90 files changed, 2662 insertions(+), 2662 deletions(-) delete mode 100644 src/core/file_sys/vfs.cpp delete mode 100644 src/core/file_sys/vfs.h create mode 100644 src/core/file_sys/vfs/vfs.cpp create mode 100644 src/core/file_sys/vfs/vfs.h create mode 100644 src/core/file_sys/vfs/vfs_cached.cpp create mode 100644 src/core/file_sys/vfs/vfs_cached.h create mode 100644 src/core/file_sys/vfs/vfs_concat.cpp create mode 100644 src/core/file_sys/vfs/vfs_concat.h create mode 100644 src/core/file_sys/vfs/vfs_layered.cpp create mode 100644 src/core/file_sys/vfs/vfs_layered.h create mode 100644 src/core/file_sys/vfs/vfs_offset.cpp create mode 100644 src/core/file_sys/vfs/vfs_offset.h create mode 100644 src/core/file_sys/vfs/vfs_real.cpp create mode 100644 src/core/file_sys/vfs/vfs_real.h create mode 100644 src/core/file_sys/vfs/vfs_static.h create mode 100644 src/core/file_sys/vfs/vfs_types.h create mode 100644 src/core/file_sys/vfs/vfs_vector.cpp create mode 100644 src/core/file_sys/vfs/vfs_vector.h delete mode 100644 src/core/file_sys/vfs_cached.cpp delete mode 100644 src/core/file_sys/vfs_cached.h delete mode 100644 src/core/file_sys/vfs_concat.cpp delete mode 100644 src/core/file_sys/vfs_concat.h delete mode 100644 src/core/file_sys/vfs_layered.cpp delete mode 100644 src/core/file_sys/vfs_layered.h delete mode 100644 src/core/file_sys/vfs_offset.cpp delete mode 100644 src/core/file_sys/vfs_offset.h delete mode 100644 src/core/file_sys/vfs_real.cpp delete mode 100644 src/core/file_sys/vfs_real.h delete mode 100644 src/core/file_sys/vfs_static.h delete mode 100644 src/core/file_sys/vfs_types.h delete mode 100644 src/core/file_sys/vfs_vector.cpp delete mode 100644 src/core/file_sys/vfs_vector.h (limited to 'src/core/file_sys') diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index c750c0da7..f275f5fe4 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp @@ -6,7 +6,7 @@ #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 { 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 #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 #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/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 #include #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 #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/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 #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 #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 #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 #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 #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 -#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 #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 #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 -#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.cpp deleted file mode 100644 index b7105c8ff..000000000 --- a/src/core/file_sys/vfs.cpp +++ /dev/null @@ -1,552 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include "common/fs/path_util.h" -#include "core/file_sys/mode.h" -#include "core/file_sys/vfs.h" - -namespace FileSys { - -VfsFilesystem::VfsFilesystem(VirtualDir root_) : root(std::move(root_)) {} - -VfsFilesystem::~VfsFilesystem() = default; - -std::string VfsFilesystem::GetName() const { - return root->GetName(); -} - -bool VfsFilesystem::IsReadable() const { - return root->IsReadable(); -} - -bool VfsFilesystem::IsWritable() const { - return root->IsWritable(); -} - -VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const { - const auto path = Common::FS::SanitizePath(path_); - if (root->GetFileRelative(path) != nullptr) - return VfsEntryType::File; - if (root->GetDirectoryRelative(path) != nullptr) - return VfsEntryType::Directory; - - return VfsEntryType::None; -} - -VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) { - const auto path = Common::FS::SanitizePath(path_); - return root->GetFileRelative(path); -} - -VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) { - const auto path = Common::FS::SanitizePath(path_); - return root->CreateFileRelative(path); -} - -VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { - const auto old_path = Common::FS::SanitizePath(old_path_); - const auto new_path = Common::FS::SanitizePath(new_path_); - - // VfsDirectory impls are only required to implement copy across the current directory. - if (Common::FS::GetParentPath(old_path) == Common::FS::GetParentPath(new_path)) { - if (!root->Copy(Common::FS::GetFilename(old_path), Common::FS::GetFilename(new_path))) - return nullptr; - return OpenFile(new_path, Mode::ReadWrite); - } - - // Do it using RawCopy. Non-default impls are encouraged to optimize this. - const auto old_file = OpenFile(old_path, Mode::Read); - if (old_file == nullptr) - return nullptr; - auto new_file = OpenFile(new_path, Mode::Read); - if (new_file != nullptr) - return nullptr; - new_file = CreateFile(new_path, Mode::Write); - if (new_file == nullptr) - return nullptr; - if (!VfsRawCopy(old_file, new_file)) - return nullptr; - return new_file; -} - -VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view new_path) { - const auto sanitized_old_path = Common::FS::SanitizePath(old_path); - const auto sanitized_new_path = Common::FS::SanitizePath(new_path); - - // Again, non-default impls are highly encouraged to provide a more optimized version of this. - auto out = CopyFile(sanitized_old_path, sanitized_new_path); - if (out == nullptr) - return nullptr; - if (DeleteFile(sanitized_old_path)) - return out; - return nullptr; -} - -bool VfsFilesystem::DeleteFile(std::string_view path_) { - const auto path = Common::FS::SanitizePath(path_); - auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write); - if (parent == nullptr) - return false; - return parent->DeleteFile(Common::FS::GetFilename(path)); -} - -VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { - const auto path = Common::FS::SanitizePath(path_); - return root->GetDirectoryRelative(path); -} - -VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { - const auto path = Common::FS::SanitizePath(path_); - return root->CreateDirectoryRelative(path); -} - -VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) { - const auto old_path = Common::FS::SanitizePath(old_path_); - const auto new_path = Common::FS::SanitizePath(new_path_); - - // Non-default impls are highly encouraged to provide a more optimized version of this. - auto old_dir = OpenDirectory(old_path, Mode::Read); - if (old_dir == nullptr) - return nullptr; - auto new_dir = OpenDirectory(new_path, Mode::Read); - if (new_dir != nullptr) - return nullptr; - new_dir = CreateDirectory(new_path, Mode::Write); - if (new_dir == nullptr) - return nullptr; - - for (const auto& file : old_dir->GetFiles()) { - const auto x = CopyFile(old_path + '/' + file->GetName(), new_path + '/' + file->GetName()); - if (x == nullptr) - return nullptr; - } - - for (const auto& dir : old_dir->GetSubdirectories()) { - const auto x = - CopyDirectory(old_path + '/' + dir->GetName(), new_path + '/' + dir->GetName()); - if (x == nullptr) - return nullptr; - } - - return new_dir; -} - -VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_view new_path) { - const auto sanitized_old_path = Common::FS::SanitizePath(old_path); - const auto sanitized_new_path = Common::FS::SanitizePath(new_path); - - // Non-default impls are highly encouraged to provide a more optimized version of this. - auto out = CopyDirectory(sanitized_old_path, sanitized_new_path); - if (out == nullptr) - return nullptr; - if (DeleteDirectory(sanitized_old_path)) - return out; - return nullptr; -} - -bool VfsFilesystem::DeleteDirectory(std::string_view path_) { - const auto path = Common::FS::SanitizePath(path_); - auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write); - if (parent == nullptr) - return false; - return parent->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path)); -} - -VfsFile::~VfsFile() = default; - -std::string VfsFile::GetExtension() const { - return std::string(Common::FS::GetExtensionFromFilename(GetName())); -} - -VfsDirectory::~VfsDirectory() = default; - -std::optional VfsFile::ReadByte(std::size_t offset) const { - u8 out{}; - const std::size_t size = Read(&out, sizeof(u8), offset); - if (size == 1) { - return out; - } - - return std::nullopt; -} - -std::vector VfsFile::ReadBytes(std::size_t size, std::size_t offset) const { - std::vector out(size); - std::size_t read_size = Read(out.data(), size, offset); - out.resize(read_size); - return out; -} - -std::vector VfsFile::ReadAllBytes() const { - return ReadBytes(GetSize()); -} - -bool VfsFile::WriteByte(u8 data, std::size_t offset) { - return Write(&data, 1, offset) == 1; -} - -std::size_t VfsFile::WriteBytes(const std::vector& data, std::size_t offset) { - return Write(data.data(), data.size(), offset); -} - -std::string VfsFile::GetFullPath() const { - if (GetContainingDirectory() == nullptr) - return '/' + GetName(); - - return GetContainingDirectory()->GetFullPath() + '/' + GetName(); -} - -VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const { - auto vec = Common::FS::SplitPathComponents(path); - if (vec.empty()) { - return nullptr; - } - - if (vec.size() == 1) { - return GetFile(vec[0]); - } - - auto dir = GetSubdirectory(vec[0]); - for (std::size_t component = 1; component < vec.size() - 1; ++component) { - if (dir == nullptr) { - return nullptr; - } - - dir = dir->GetSubdirectory(vec[component]); - } - - if (dir == nullptr) { - return nullptr; - } - - return dir->GetFile(vec.back()); -} - -VirtualFile VfsDirectory::GetFileAbsolute(std::string_view path) const { - if (IsRoot()) { - return GetFileRelative(path); - } - - return GetParentDirectory()->GetFileAbsolute(path); -} - -VirtualDir VfsDirectory::GetDirectoryRelative(std::string_view path) const { - auto vec = Common::FS::SplitPathComponents(path); - if (vec.empty()) { - // TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently - // because of const-ness - return nullptr; - } - - auto dir = GetSubdirectory(vec[0]); - for (std::size_t component = 1; component < vec.size(); ++component) { - if (dir == nullptr) { - return nullptr; - } - - dir = dir->GetSubdirectory(vec[component]); - } - - return dir; -} - -VirtualDir VfsDirectory::GetDirectoryAbsolute(std::string_view path) const { - if (IsRoot()) { - return GetDirectoryRelative(path); - } - - return GetParentDirectory()->GetDirectoryAbsolute(path); -} - -VirtualFile VfsDirectory::GetFile(std::string_view name) const { - const auto& files = GetFiles(); - const auto iter = std::find_if(files.begin(), files.end(), - [&name](const auto& file1) { return name == file1->GetName(); }); - return iter == files.end() ? nullptr : *iter; -} - -FileTimeStampRaw VfsDirectory::GetFileTimeStamp([[maybe_unused]] std::string_view path) const { - return {}; -} - -VirtualDir VfsDirectory::GetSubdirectory(std::string_view name) const { - const auto& subs = GetSubdirectories(); - const auto iter = std::find_if(subs.begin(), subs.end(), - [&name](const auto& file1) { return name == file1->GetName(); }); - return iter == subs.end() ? nullptr : *iter; -} - -bool VfsDirectory::IsRoot() const { - return GetParentDirectory() == nullptr; -} - -std::size_t VfsDirectory::GetSize() const { - const auto& files = GetFiles(); - const auto sum_sizes = [](const auto& range) { - return std::accumulate(range.begin(), range.end(), 0ULL, - [](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); }); - }; - - const auto file_total = sum_sizes(files); - const auto& sub_dir = GetSubdirectories(); - const auto subdir_total = sum_sizes(sub_dir); - - return file_total + subdir_total; -} - -VirtualFile VfsDirectory::CreateFileRelative(std::string_view path) { - auto vec = Common::FS::SplitPathComponents(path); - if (vec.empty()) { - return nullptr; - } - - if (vec.size() == 1) { - return CreateFile(vec[0]); - } - - auto dir = GetSubdirectory(vec[0]); - if (dir == nullptr) { - dir = CreateSubdirectory(vec[0]); - if (dir == nullptr) { - return nullptr; - } - } - - return dir->CreateFileRelative(Common::FS::GetPathWithoutTop(path)); -} - -VirtualFile VfsDirectory::CreateFileAbsolute(std::string_view path) { - if (IsRoot()) { - return CreateFileRelative(path); - } - - return GetParentDirectory()->CreateFileAbsolute(path); -} - -VirtualDir VfsDirectory::CreateDirectoryRelative(std::string_view path) { - auto vec = Common::FS::SplitPathComponents(path); - if (vec.empty()) { - return nullptr; - } - - if (vec.size() == 1) { - return CreateSubdirectory(vec[0]); - } - - auto dir = GetSubdirectory(vec[0]); - if (dir == nullptr) { - dir = CreateSubdirectory(vec[0]); - if (dir == nullptr) { - return nullptr; - } - } - - return dir->CreateDirectoryRelative(Common::FS::GetPathWithoutTop(path)); -} - -VirtualDir VfsDirectory::CreateDirectoryAbsolute(std::string_view path) { - if (IsRoot()) { - return CreateDirectoryRelative(path); - } - - return GetParentDirectory()->CreateDirectoryAbsolute(path); -} - -bool VfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { - auto dir = GetSubdirectory(name); - if (dir == nullptr) { - return false; - } - - bool success = true; - for (const auto& file : dir->GetFiles()) { - if (!DeleteFile(file->GetName())) { - success = false; - } - } - - for (const auto& sdir : dir->GetSubdirectories()) { - if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) { - success = false; - } - } - - return success; -} - -bool VfsDirectory::CleanSubdirectoryRecursive(std::string_view name) { - auto dir = GetSubdirectory(name); - if (dir == nullptr) { - return false; - } - - bool success = true; - for (const auto& file : dir->GetFiles()) { - if (!dir->DeleteFile(file->GetName())) { - success = false; - } - } - - for (const auto& sdir : dir->GetSubdirectories()) { - if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) { - success = false; - } - } - - return success; -} - -bool VfsDirectory::Copy(std::string_view src, std::string_view dest) { - const auto f1 = GetFile(src); - auto f2 = CreateFile(dest); - if (f1 == nullptr || f2 == nullptr) { - return false; - } - - if (!f2->Resize(f1->GetSize())) { - DeleteFile(dest); - return false; - } - - return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize(); -} - -std::map> VfsDirectory::GetEntries() const { - std::map> out; - for (const auto& dir : GetSubdirectories()) - out.emplace(dir->GetName(), VfsEntryType::Directory); - for (const auto& file : GetFiles()) - out.emplace(file->GetName(), VfsEntryType::File); - return out; -} - -std::string VfsDirectory::GetFullPath() const { - if (IsRoot()) - return GetName(); - - return GetParentDirectory()->GetFullPath() + '/' + GetName(); -} - -bool ReadOnlyVfsDirectory::IsWritable() const { - return false; -} - -bool ReadOnlyVfsDirectory::IsReadable() const { - return true; -} - -VirtualDir ReadOnlyVfsDirectory::CreateSubdirectory(std::string_view name) { - return nullptr; -} - -VirtualFile ReadOnlyVfsDirectory::CreateFile(std::string_view name) { - return nullptr; -} - -VirtualFile ReadOnlyVfsDirectory::CreateFileAbsolute(std::string_view path) { - return nullptr; -} - -VirtualFile ReadOnlyVfsDirectory::CreateFileRelative(std::string_view path) { - return nullptr; -} - -VirtualDir ReadOnlyVfsDirectory::CreateDirectoryAbsolute(std::string_view path) { - return nullptr; -} - -VirtualDir ReadOnlyVfsDirectory::CreateDirectoryRelative(std::string_view path) { - return nullptr; -} - -bool ReadOnlyVfsDirectory::DeleteSubdirectory(std::string_view name) { - return false; -} - -bool ReadOnlyVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { - return false; -} - -bool ReadOnlyVfsDirectory::CleanSubdirectoryRecursive(std::string_view name) { - return false; -} - -bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) { - return false; -} - -bool ReadOnlyVfsDirectory::Rename(std::string_view name) { - return false; -} - -bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t block_size) { - if (file1->GetSize() != file2->GetSize()) - return false; - - std::vector f1_v(block_size); - std::vector f2_v(block_size); - for (std::size_t i = 0; i < file1->GetSize(); i += block_size) { - auto f1_vs = file1->Read(f1_v.data(), block_size, i); - auto f2_vs = file2->Read(f2_v.data(), block_size, i); - - if (f1_vs != f2_vs) - return false; - auto iters = std::mismatch(f1_v.begin(), f1_v.end(), f2_v.begin(), f2_v.end()); - if (iters.first != f1_v.end() && iters.second != f2_v.end()) - return false; - } - - return true; -} - -bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size) { - if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) - return false; - if (!dest->Resize(src->GetSize())) - return false; - - std::vector temp(std::min(block_size, src->GetSize())); - for (std::size_t i = 0; i < src->GetSize(); i += block_size) { - const auto read = std::min(block_size, src->GetSize() - i); - - if (src->Read(temp.data(), read, i) != read) { - return false; - } - - if (dest->Write(temp.data(), read, i) != read) { - return false; - } - } - - return true; -} - -bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size) { - if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) - return false; - - for (const auto& file : src->GetFiles()) { - const auto out = dest->CreateFile(file->GetName()); - if (!VfsRawCopy(file, out, block_size)) - return false; - } - - for (const auto& dir : src->GetSubdirectories()) { - const auto out = dest->CreateSubdirectory(dir->GetName()); - if (!VfsRawCopyD(dir, out, block_size)) - return false; - } - - return true; -} - -VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path) { - const auto res = rel->GetDirectoryRelative(path); - if (res == nullptr) - return rel->CreateDirectoryRelative(path); - return res; -} -} // namespace FileSys diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h deleted file mode 100644 index a7cd1cae3..000000000 --- a/src/core/file_sys/vfs.h +++ /dev/null @@ -1,327 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "core/file_sys/vfs_types.h" - -namespace FileSys { - -enum class Mode : u32; - -// An enumeration representing what can be at the end of a path in a VfsFilesystem -enum class VfsEntryType { - None, - File, - Directory, -}; - -// A class representing an abstract filesystem. A default implementation given the root VirtualDir -// is provided for convenience, but if the Vfs implementation has any additional state or -// functionality, they will need to override. -class VfsFilesystem { -public: - YUZU_NON_COPYABLE(VfsFilesystem); - YUZU_NON_MOVEABLE(VfsFilesystem); - - explicit VfsFilesystem(VirtualDir root); - virtual ~VfsFilesystem(); - - // Gets the friendly name for the filesystem. - virtual std::string GetName() const; - - // Return whether or not the user has read permissions on this filesystem. - virtual bool IsReadable() const; - // Return whether or not the user has write permission on this filesystem. - virtual bool IsWritable() const; - - // Determine if the entry at path is non-existent, a file, or a directory. - virtual VfsEntryType GetEntryType(std::string_view path) const; - - // Opens the file with path relative to root. If it doesn't exist, returns nullptr. - virtual VirtualFile OpenFile(std::string_view path, Mode perms); - // Creates a new, empty file at path - virtual VirtualFile CreateFile(std::string_view path, Mode perms); - // Copies the file from old_path to new_path, returning the new file on success and nullptr on - // failure. - virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path); - // Moves the file from old_path to new_path, returning the moved file on success and nullptr on - // failure. - virtual VirtualFile MoveFile(std::string_view old_path, std::string_view new_path); - // Deletes the file with path relative to root, returning true on success. - virtual bool DeleteFile(std::string_view path); - - // Opens the directory with path relative to root. If it doesn't exist, returns nullptr. - virtual VirtualDir OpenDirectory(std::string_view path, Mode perms); - // Creates a new, empty directory at path - virtual VirtualDir CreateDirectory(std::string_view path, Mode perms); - // Copies the directory from old_path to new_path, returning the new directory on success and - // nullptr on failure. - virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path); - // Moves the directory from old_path to new_path, returning the moved directory on success and - // nullptr on failure. - virtual VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path); - // Deletes the directory with path relative to root, returning true on success. - virtual bool DeleteDirectory(std::string_view path); - -protected: - // Root directory in default implementation. - VirtualDir root; -}; - -// A class representing a file in an abstract filesystem. -class VfsFile { -public: - YUZU_NON_COPYABLE(VfsFile); - YUZU_NON_MOVEABLE(VfsFile); - - VfsFile() = default; - virtual ~VfsFile(); - - // Retrieves the file name. - virtual std::string GetName() const = 0; - // Retrieves the extension of the file name. - virtual std::string GetExtension() const; - // Retrieves the size of the file. - virtual std::size_t GetSize() const = 0; - // Resizes the file to new_size. Returns whether or not the operation was successful. - virtual bool Resize(std::size_t new_size) = 0; - // Gets a pointer to the directory containing this file, returning nullptr if there is none. - virtual VirtualDir GetContainingDirectory() const = 0; - - // Returns whether or not the file can be written to. - virtual bool IsWritable() const = 0; - // Returns whether or not the file can be read from. - virtual bool IsReadable() const = 0; - - // The primary method of reading from the file. Reads length bytes into data starting at offset - // into file. Returns number of bytes successfully read. - virtual std::size_t Read(u8* data, std::size_t length, std::size_t offset = 0) const = 0; - // The primary method of writing to the file. Writes length bytes from data starting at offset - // into file. Returns number of bytes successfully written. - virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0; - - // Reads exactly one byte at the offset provided, returning std::nullopt on error. - virtual std::optional ReadByte(std::size_t offset = 0) const; - // Reads size bytes starting at offset in file into a vector. - virtual std::vector ReadBytes(std::size_t size, std::size_t offset = 0) const; - // Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(), - // 0)' - virtual std::vector ReadAllBytes() const; - - // Reads an array of type T, size number_elements starting at offset. - // Returns the number of bytes (sizeof(T)*number_elements) read successfully. - template - std::size_t ReadArray(T* data, std::size_t number_elements, std::size_t offset = 0) const { - static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); - - return Read(reinterpret_cast(data), number_elements * sizeof(T), offset); - } - - // Reads size bytes into the memory starting at data starting at offset into the file. - // Returns the number of bytes read successfully. - template - std::size_t ReadBytes(T* data, std::size_t size, std::size_t offset = 0) const { - static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); - return Read(reinterpret_cast(data), size, offset); - } - - // Reads one object of type T starting at offset in file. - // Returns the number of bytes read successfully (sizeof(T)). - template - std::size_t ReadObject(T* data, std::size_t offset = 0) const { - static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); - return Read(reinterpret_cast(data), sizeof(T), offset); - } - - // Writes exactly one byte to offset in file and returns whether or not the byte was written - // successfully. - virtual bool WriteByte(u8 data, std::size_t offset = 0); - // Writes a vector of bytes to offset in file and returns the number of bytes successfully - // written. - virtual std::size_t WriteBytes(const std::vector& data, std::size_t offset = 0); - - // Writes an array of type T, size number_elements to offset in file. - // Returns the number of bytes (sizeof(T)*number_elements) written successfully. - template - std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) { - static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); - return Write(reinterpret_cast(data), number_elements * sizeof(T), offset); - } - - // Writes size bytes starting at memory location data to offset in file. - // Returns the number of bytes written successfully. - template - std::size_t WriteBytes(const T* data, std::size_t size, std::size_t offset = 0) { - static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); - return Write(reinterpret_cast(data), size, offset); - } - - // Writes one object of type T to offset in file. - // Returns the number of bytes written successfully (sizeof(T)). - template - std::size_t WriteObject(const T& data, std::size_t offset = 0) { - static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); - return Write(reinterpret_cast(&data), sizeof(T), offset); - } - - // Renames the file to name. Returns whether or not the operation was successful. - virtual bool Rename(std::string_view name) = 0; - - // Returns the full path of this file as a string, recursively - virtual std::string GetFullPath() const; -}; - -// A class representing a directory in an abstract filesystem. -class VfsDirectory { -public: - YUZU_NON_COPYABLE(VfsDirectory); - YUZU_NON_MOVEABLE(VfsDirectory); - - VfsDirectory() = default; - virtual ~VfsDirectory(); - - // Retrieves the file located at path as if the current directory was root. Returns nullptr if - // not found. - virtual VirtualFile GetFileRelative(std::string_view path) const; - // Calls GetFileRelative(path) on the root of the current directory. - virtual VirtualFile GetFileAbsolute(std::string_view path) const; - - // Retrieves the directory located at path as if the current directory was root. Returns nullptr - // if not found. - virtual VirtualDir GetDirectoryRelative(std::string_view path) const; - // Calls GetDirectoryRelative(path) on the root of the current directory. - virtual VirtualDir GetDirectoryAbsolute(std::string_view path) const; - - // Returns a vector containing all of the files in this directory. - virtual std::vector GetFiles() const = 0; - // Returns the file with filename matching name. Returns nullptr if directory doesn't have a - // file with name. - virtual VirtualFile GetFile(std::string_view name) const; - - // Returns a struct containing the file's timestamp. - virtual FileTimeStampRaw GetFileTimeStamp(std::string_view path) const; - - // Returns a vector containing all of the subdirectories in this directory. - virtual std::vector GetSubdirectories() const = 0; - // Returns the directory with name matching name. Returns nullptr if directory doesn't have a - // directory with name. - virtual VirtualDir GetSubdirectory(std::string_view name) const; - - // Returns whether or not the directory can be written to. - virtual bool IsWritable() const = 0; - // Returns whether of not the directory can be read from. - virtual bool IsReadable() const = 0; - - // Returns whether or not the directory is the root of the current file tree. - virtual bool IsRoot() const; - - // Returns the name of the directory. - virtual std::string GetName() const = 0; - // Returns the total size of all files and subdirectories in this directory. - virtual std::size_t GetSize() const; - // Returns the parent directory of this directory. Returns nullptr if this directory is root or - // has no parent. - virtual VirtualDir GetParentDirectory() const = 0; - - // Creates a new subdirectory with name name. Returns a pointer to the new directory or nullptr - // if the operation failed. - virtual VirtualDir CreateSubdirectory(std::string_view name) = 0; - // Creates a new file with name name. Returns a pointer to the new file or nullptr if the - // operation failed. - virtual VirtualFile CreateFile(std::string_view name) = 0; - - // Creates a new file at the path relative to this directory. Also creates directories if - // they do not exist and is supported by this implementation. Returns nullptr on any failure. - virtual VirtualFile CreateFileRelative(std::string_view path); - - // Creates a new file at the path relative to root of this directory. Also creates directories - // if they do not exist and is supported by this implementation. Returns nullptr on any failure. - virtual VirtualFile CreateFileAbsolute(std::string_view path); - - // Creates a new directory at the path relative to this directory. Also creates directories if - // they do not exist and is supported by this implementation. Returns nullptr on any failure. - virtual VirtualDir CreateDirectoryRelative(std::string_view path); - - // Creates a new directory at the path relative to root of this directory. Also creates - // directories if they do not exist and is supported by this implementation. Returns nullptr on - // any failure. - virtual VirtualDir CreateDirectoryAbsolute(std::string_view path); - - // Deletes the subdirectory with the given name and returns true on success. - virtual bool DeleteSubdirectory(std::string_view name) = 0; - - // Deletes all subdirectories and files within the provided directory and then deletes - // the directory itself. Returns true on success. - virtual bool DeleteSubdirectoryRecursive(std::string_view name); - - // Deletes all subdirectories and files within the provided directory. - // Unlike DeleteSubdirectoryRecursive, this does not delete the provided directory. - virtual bool CleanSubdirectoryRecursive(std::string_view name); - - // Returns whether or not the file with name name was deleted successfully. - virtual bool DeleteFile(std::string_view name) = 0; - - // Returns whether or not this directory was renamed to name. - virtual bool Rename(std::string_view name) = 0; - - // Returns whether or not the file with name src was successfully copied to a new file with name - // dest. - virtual bool Copy(std::string_view src, std::string_view dest); - - // Gets all of the entries directly in the directory (files and dirs), returning a map between - // item name -> type. - virtual std::map> GetEntries() const; - - // Returns the full path of this directory as a string, recursively - virtual std::string GetFullPath() const; -}; - -// A convenience partial-implementation of VfsDirectory that stubs out methods that should only work -// if writable. This is to avoid redundant empty methods everywhere. -class ReadOnlyVfsDirectory : public VfsDirectory { -public: - bool IsWritable() const override; - bool IsReadable() const override; - VirtualDir CreateSubdirectory(std::string_view name) override; - VirtualFile CreateFile(std::string_view name) override; - VirtualFile CreateFileAbsolute(std::string_view path) override; - VirtualFile CreateFileRelative(std::string_view path) override; - VirtualDir CreateDirectoryAbsolute(std::string_view path) override; - VirtualDir CreateDirectoryRelative(std::string_view path) override; - bool DeleteSubdirectory(std::string_view name) override; - bool DeleteSubdirectoryRecursive(std::string_view name) override; - bool CleanSubdirectoryRecursive(std::string_view name) override; - bool DeleteFile(std::string_view name) override; - bool Rename(std::string_view name) override; -}; - -// Compare the two files, byte-for-byte, in increments specified by block_size -bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, - std::size_t block_size = 0x1000); - -// A method that copies the raw data between two different implementations of VirtualFile. If you -// are using the same implementation, it is probably better to use the Copy method in the parent -// directory of src/dest. -bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size = 0x1000); - -// A method that performs a similar function to VfsRawCopy above, but instead copies entire -// directories. It suffers the same performance penalties as above and an implementation-specific -// Copy should always be preferred. -bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size = 0x1000); - -// Checks if the directory at path relative to rel exists. If it does, returns that. If it does not -// it attempts to create it and returns the new dir or nullptr on failure. -VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path); - -} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs.cpp b/src/core/file_sys/vfs/vfs.cpp new file mode 100644 index 000000000..b88a5f91d --- /dev/null +++ b/src/core/file_sys/vfs/vfs.cpp @@ -0,0 +1,552 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include "common/fs/path_util.h" +#include "core/file_sys/mode.h" +#include "core/file_sys/vfs/vfs.h" + +namespace FileSys { + +VfsFilesystem::VfsFilesystem(VirtualDir root_) : root(std::move(root_)) {} + +VfsFilesystem::~VfsFilesystem() = default; + +std::string VfsFilesystem::GetName() const { + return root->GetName(); +} + +bool VfsFilesystem::IsReadable() const { + return root->IsReadable(); +} + +bool VfsFilesystem::IsWritable() const { + return root->IsWritable(); +} + +VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const { + const auto path = Common::FS::SanitizePath(path_); + if (root->GetFileRelative(path) != nullptr) + return VfsEntryType::File; + if (root->GetDirectoryRelative(path) != nullptr) + return VfsEntryType::Directory; + + return VfsEntryType::None; +} + +VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) { + const auto path = Common::FS::SanitizePath(path_); + return root->GetFileRelative(path); +} + +VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) { + const auto path = Common::FS::SanitizePath(path_); + return root->CreateFileRelative(path); +} + +VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { + const auto old_path = Common::FS::SanitizePath(old_path_); + const auto new_path = Common::FS::SanitizePath(new_path_); + + // VfsDirectory impls are only required to implement copy across the current directory. + if (Common::FS::GetParentPath(old_path) == Common::FS::GetParentPath(new_path)) { + if (!root->Copy(Common::FS::GetFilename(old_path), Common::FS::GetFilename(new_path))) + return nullptr; + return OpenFile(new_path, Mode::ReadWrite); + } + + // Do it using RawCopy. Non-default impls are encouraged to optimize this. + const auto old_file = OpenFile(old_path, Mode::Read); + if (old_file == nullptr) + return nullptr; + auto new_file = OpenFile(new_path, Mode::Read); + if (new_file != nullptr) + return nullptr; + new_file = CreateFile(new_path, Mode::Write); + if (new_file == nullptr) + return nullptr; + if (!VfsRawCopy(old_file, new_file)) + return nullptr; + return new_file; +} + +VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view new_path) { + const auto sanitized_old_path = Common::FS::SanitizePath(old_path); + const auto sanitized_new_path = Common::FS::SanitizePath(new_path); + + // Again, non-default impls are highly encouraged to provide a more optimized version of this. + auto out = CopyFile(sanitized_old_path, sanitized_new_path); + if (out == nullptr) + return nullptr; + if (DeleteFile(sanitized_old_path)) + return out; + return nullptr; +} + +bool VfsFilesystem::DeleteFile(std::string_view path_) { + const auto path = Common::FS::SanitizePath(path_); + auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write); + if (parent == nullptr) + return false; + return parent->DeleteFile(Common::FS::GetFilename(path)); +} + +VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { + const auto path = Common::FS::SanitizePath(path_); + return root->GetDirectoryRelative(path); +} + +VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { + const auto path = Common::FS::SanitizePath(path_); + return root->CreateDirectoryRelative(path); +} + +VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) { + const auto old_path = Common::FS::SanitizePath(old_path_); + const auto new_path = Common::FS::SanitizePath(new_path_); + + // Non-default impls are highly encouraged to provide a more optimized version of this. + auto old_dir = OpenDirectory(old_path, Mode::Read); + if (old_dir == nullptr) + return nullptr; + auto new_dir = OpenDirectory(new_path, Mode::Read); + if (new_dir != nullptr) + return nullptr; + new_dir = CreateDirectory(new_path, Mode::Write); + if (new_dir == nullptr) + return nullptr; + + for (const auto& file : old_dir->GetFiles()) { + const auto x = CopyFile(old_path + '/' + file->GetName(), new_path + '/' + file->GetName()); + if (x == nullptr) + return nullptr; + } + + for (const auto& dir : old_dir->GetSubdirectories()) { + const auto x = + CopyDirectory(old_path + '/' + dir->GetName(), new_path + '/' + dir->GetName()); + if (x == nullptr) + return nullptr; + } + + return new_dir; +} + +VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_view new_path) { + const auto sanitized_old_path = Common::FS::SanitizePath(old_path); + const auto sanitized_new_path = Common::FS::SanitizePath(new_path); + + // Non-default impls are highly encouraged to provide a more optimized version of this. + auto out = CopyDirectory(sanitized_old_path, sanitized_new_path); + if (out == nullptr) + return nullptr; + if (DeleteDirectory(sanitized_old_path)) + return out; + return nullptr; +} + +bool VfsFilesystem::DeleteDirectory(std::string_view path_) { + const auto path = Common::FS::SanitizePath(path_); + auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write); + if (parent == nullptr) + return false; + return parent->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path)); +} + +VfsFile::~VfsFile() = default; + +std::string VfsFile::GetExtension() const { + return std::string(Common::FS::GetExtensionFromFilename(GetName())); +} + +VfsDirectory::~VfsDirectory() = default; + +std::optional VfsFile::ReadByte(std::size_t offset) const { + u8 out{}; + const std::size_t size = Read(&out, sizeof(u8), offset); + if (size == 1) { + return out; + } + + return std::nullopt; +} + +std::vector VfsFile::ReadBytes(std::size_t size, std::size_t offset) const { + std::vector out(size); + std::size_t read_size = Read(out.data(), size, offset); + out.resize(read_size); + return out; +} + +std::vector VfsFile::ReadAllBytes() const { + return ReadBytes(GetSize()); +} + +bool VfsFile::WriteByte(u8 data, std::size_t offset) { + return Write(&data, 1, offset) == 1; +} + +std::size_t VfsFile::WriteBytes(const std::vector& data, std::size_t offset) { + return Write(data.data(), data.size(), offset); +} + +std::string VfsFile::GetFullPath() const { + if (GetContainingDirectory() == nullptr) + return '/' + GetName(); + + return GetContainingDirectory()->GetFullPath() + '/' + GetName(); +} + +VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const { + auto vec = Common::FS::SplitPathComponents(path); + if (vec.empty()) { + return nullptr; + } + + if (vec.size() == 1) { + return GetFile(vec[0]); + } + + auto dir = GetSubdirectory(vec[0]); + for (std::size_t component = 1; component < vec.size() - 1; ++component) { + if (dir == nullptr) { + return nullptr; + } + + dir = dir->GetSubdirectory(vec[component]); + } + + if (dir == nullptr) { + return nullptr; + } + + return dir->GetFile(vec.back()); +} + +VirtualFile VfsDirectory::GetFileAbsolute(std::string_view path) const { + if (IsRoot()) { + return GetFileRelative(path); + } + + return GetParentDirectory()->GetFileAbsolute(path); +} + +VirtualDir VfsDirectory::GetDirectoryRelative(std::string_view path) const { + auto vec = Common::FS::SplitPathComponents(path); + if (vec.empty()) { + // TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently + // because of const-ness + return nullptr; + } + + auto dir = GetSubdirectory(vec[0]); + for (std::size_t component = 1; component < vec.size(); ++component) { + if (dir == nullptr) { + return nullptr; + } + + dir = dir->GetSubdirectory(vec[component]); + } + + return dir; +} + +VirtualDir VfsDirectory::GetDirectoryAbsolute(std::string_view path) const { + if (IsRoot()) { + return GetDirectoryRelative(path); + } + + return GetParentDirectory()->GetDirectoryAbsolute(path); +} + +VirtualFile VfsDirectory::GetFile(std::string_view name) const { + const auto& files = GetFiles(); + const auto iter = std::find_if(files.begin(), files.end(), + [&name](const auto& file1) { return name == file1->GetName(); }); + return iter == files.end() ? nullptr : *iter; +} + +FileTimeStampRaw VfsDirectory::GetFileTimeStamp([[maybe_unused]] std::string_view path) const { + return {}; +} + +VirtualDir VfsDirectory::GetSubdirectory(std::string_view name) const { + const auto& subs = GetSubdirectories(); + const auto iter = std::find_if(subs.begin(), subs.end(), + [&name](const auto& file1) { return name == file1->GetName(); }); + return iter == subs.end() ? nullptr : *iter; +} + +bool VfsDirectory::IsRoot() const { + return GetParentDirectory() == nullptr; +} + +std::size_t VfsDirectory::GetSize() const { + const auto& files = GetFiles(); + const auto sum_sizes = [](const auto& range) { + return std::accumulate(range.begin(), range.end(), 0ULL, + [](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); }); + }; + + const auto file_total = sum_sizes(files); + const auto& sub_dir = GetSubdirectories(); + const auto subdir_total = sum_sizes(sub_dir); + + return file_total + subdir_total; +} + +VirtualFile VfsDirectory::CreateFileRelative(std::string_view path) { + auto vec = Common::FS::SplitPathComponents(path); + if (vec.empty()) { + return nullptr; + } + + if (vec.size() == 1) { + return CreateFile(vec[0]); + } + + auto dir = GetSubdirectory(vec[0]); + if (dir == nullptr) { + dir = CreateSubdirectory(vec[0]); + if (dir == nullptr) { + return nullptr; + } + } + + return dir->CreateFileRelative(Common::FS::GetPathWithoutTop(path)); +} + +VirtualFile VfsDirectory::CreateFileAbsolute(std::string_view path) { + if (IsRoot()) { + return CreateFileRelative(path); + } + + return GetParentDirectory()->CreateFileAbsolute(path); +} + +VirtualDir VfsDirectory::CreateDirectoryRelative(std::string_view path) { + auto vec = Common::FS::SplitPathComponents(path); + if (vec.empty()) { + return nullptr; + } + + if (vec.size() == 1) { + return CreateSubdirectory(vec[0]); + } + + auto dir = GetSubdirectory(vec[0]); + if (dir == nullptr) { + dir = CreateSubdirectory(vec[0]); + if (dir == nullptr) { + return nullptr; + } + } + + return dir->CreateDirectoryRelative(Common::FS::GetPathWithoutTop(path)); +} + +VirtualDir VfsDirectory::CreateDirectoryAbsolute(std::string_view path) { + if (IsRoot()) { + return CreateDirectoryRelative(path); + } + + return GetParentDirectory()->CreateDirectoryAbsolute(path); +} + +bool VfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { + auto dir = GetSubdirectory(name); + if (dir == nullptr) { + return false; + } + + bool success = true; + for (const auto& file : dir->GetFiles()) { + if (!DeleteFile(file->GetName())) { + success = false; + } + } + + for (const auto& sdir : dir->GetSubdirectories()) { + if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) { + success = false; + } + } + + return success; +} + +bool VfsDirectory::CleanSubdirectoryRecursive(std::string_view name) { + auto dir = GetSubdirectory(name); + if (dir == nullptr) { + return false; + } + + bool success = true; + for (const auto& file : dir->GetFiles()) { + if (!dir->DeleteFile(file->GetName())) { + success = false; + } + } + + for (const auto& sdir : dir->GetSubdirectories()) { + if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) { + success = false; + } + } + + return success; +} + +bool VfsDirectory::Copy(std::string_view src, std::string_view dest) { + const auto f1 = GetFile(src); + auto f2 = CreateFile(dest); + if (f1 == nullptr || f2 == nullptr) { + return false; + } + + if (!f2->Resize(f1->GetSize())) { + DeleteFile(dest); + return false; + } + + return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize(); +} + +std::map> VfsDirectory::GetEntries() const { + std::map> out; + for (const auto& dir : GetSubdirectories()) + out.emplace(dir->GetName(), VfsEntryType::Directory); + for (const auto& file : GetFiles()) + out.emplace(file->GetName(), VfsEntryType::File); + return out; +} + +std::string VfsDirectory::GetFullPath() const { + if (IsRoot()) + return GetName(); + + return GetParentDirectory()->GetFullPath() + '/' + GetName(); +} + +bool ReadOnlyVfsDirectory::IsWritable() const { + return false; +} + +bool ReadOnlyVfsDirectory::IsReadable() const { + return true; +} + +VirtualDir ReadOnlyVfsDirectory::CreateSubdirectory(std::string_view name) { + return nullptr; +} + +VirtualFile ReadOnlyVfsDirectory::CreateFile(std::string_view name) { + return nullptr; +} + +VirtualFile ReadOnlyVfsDirectory::CreateFileAbsolute(std::string_view path) { + return nullptr; +} + +VirtualFile ReadOnlyVfsDirectory::CreateFileRelative(std::string_view path) { + return nullptr; +} + +VirtualDir ReadOnlyVfsDirectory::CreateDirectoryAbsolute(std::string_view path) { + return nullptr; +} + +VirtualDir ReadOnlyVfsDirectory::CreateDirectoryRelative(std::string_view path) { + return nullptr; +} + +bool ReadOnlyVfsDirectory::DeleteSubdirectory(std::string_view name) { + return false; +} + +bool ReadOnlyVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { + return false; +} + +bool ReadOnlyVfsDirectory::CleanSubdirectoryRecursive(std::string_view name) { + return false; +} + +bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) { + return false; +} + +bool ReadOnlyVfsDirectory::Rename(std::string_view name) { + return false; +} + +bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t block_size) { + if (file1->GetSize() != file2->GetSize()) + return false; + + std::vector f1_v(block_size); + std::vector f2_v(block_size); + for (std::size_t i = 0; i < file1->GetSize(); i += block_size) { + auto f1_vs = file1->Read(f1_v.data(), block_size, i); + auto f2_vs = file2->Read(f2_v.data(), block_size, i); + + if (f1_vs != f2_vs) + return false; + auto iters = std::mismatch(f1_v.begin(), f1_v.end(), f2_v.begin(), f2_v.end()); + if (iters.first != f1_v.end() && iters.second != f2_v.end()) + return false; + } + + return true; +} + +bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size) { + if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) + return false; + if (!dest->Resize(src->GetSize())) + return false; + + std::vector temp(std::min(block_size, src->GetSize())); + for (std::size_t i = 0; i < src->GetSize(); i += block_size) { + const auto read = std::min(block_size, src->GetSize() - i); + + if (src->Read(temp.data(), read, i) != read) { + return false; + } + + if (dest->Write(temp.data(), read, i) != read) { + return false; + } + } + + return true; +} + +bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size) { + if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) + return false; + + for (const auto& file : src->GetFiles()) { + const auto out = dest->CreateFile(file->GetName()); + if (!VfsRawCopy(file, out, block_size)) + return false; + } + + for (const auto& dir : src->GetSubdirectories()) { + const auto out = dest->CreateSubdirectory(dir->GetName()); + if (!VfsRawCopyD(dir, out, block_size)) + return false; + } + + return true; +} + +VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path) { + const auto res = rel->GetDirectoryRelative(path); + if (res == nullptr) + return rel->CreateDirectoryRelative(path); + return res; +} +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs.h b/src/core/file_sys/vfs/vfs.h new file mode 100644 index 000000000..6830244e3 --- /dev/null +++ b/src/core/file_sys/vfs/vfs.h @@ -0,0 +1,327 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "common/common_funcs.h" +#include "common/common_types.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, + File, + Directory, +}; + +// A class representing an abstract filesystem. A default implementation given the root VirtualDir +// is provided for convenience, but if the Vfs implementation has any additional state or +// functionality, they will need to override. +class VfsFilesystem { +public: + YUZU_NON_COPYABLE(VfsFilesystem); + YUZU_NON_MOVEABLE(VfsFilesystem); + + explicit VfsFilesystem(VirtualDir root); + virtual ~VfsFilesystem(); + + // Gets the friendly name for the filesystem. + virtual std::string GetName() const; + + // Return whether or not the user has read permissions on this filesystem. + virtual bool IsReadable() const; + // Return whether or not the user has write permission on this filesystem. + virtual bool IsWritable() const; + + // Determine if the entry at path is non-existent, a file, or a directory. + virtual VfsEntryType GetEntryType(std::string_view path) const; + + // Opens the file with path relative to root. If it doesn't exist, returns nullptr. + virtual VirtualFile OpenFile(std::string_view path, Mode perms); + // Creates a new, empty file at path + virtual VirtualFile CreateFile(std::string_view path, Mode perms); + // Copies the file from old_path to new_path, returning the new file on success and nullptr on + // failure. + virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path); + // Moves the file from old_path to new_path, returning the moved file on success and nullptr on + // failure. + virtual VirtualFile MoveFile(std::string_view old_path, std::string_view new_path); + // Deletes the file with path relative to root, returning true on success. + virtual bool DeleteFile(std::string_view path); + + // Opens the directory with path relative to root. If it doesn't exist, returns nullptr. + virtual VirtualDir OpenDirectory(std::string_view path, Mode perms); + // Creates a new, empty directory at path + virtual VirtualDir CreateDirectory(std::string_view path, Mode perms); + // Copies the directory from old_path to new_path, returning the new directory on success and + // nullptr on failure. + virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path); + // Moves the directory from old_path to new_path, returning the moved directory on success and + // nullptr on failure. + virtual VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path); + // Deletes the directory with path relative to root, returning true on success. + virtual bool DeleteDirectory(std::string_view path); + +protected: + // Root directory in default implementation. + VirtualDir root; +}; + +// A class representing a file in an abstract filesystem. +class VfsFile { +public: + YUZU_NON_COPYABLE(VfsFile); + YUZU_NON_MOVEABLE(VfsFile); + + VfsFile() = default; + virtual ~VfsFile(); + + // Retrieves the file name. + virtual std::string GetName() const = 0; + // Retrieves the extension of the file name. + virtual std::string GetExtension() const; + // Retrieves the size of the file. + virtual std::size_t GetSize() const = 0; + // Resizes the file to new_size. Returns whether or not the operation was successful. + virtual bool Resize(std::size_t new_size) = 0; + // Gets a pointer to the directory containing this file, returning nullptr if there is none. + virtual VirtualDir GetContainingDirectory() const = 0; + + // Returns whether or not the file can be written to. + virtual bool IsWritable() const = 0; + // Returns whether or not the file can be read from. + virtual bool IsReadable() const = 0; + + // The primary method of reading from the file. Reads length bytes into data starting at offset + // into file. Returns number of bytes successfully read. + virtual std::size_t Read(u8* data, std::size_t length, std::size_t offset = 0) const = 0; + // The primary method of writing to the file. Writes length bytes from data starting at offset + // into file. Returns number of bytes successfully written. + virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0; + + // Reads exactly one byte at the offset provided, returning std::nullopt on error. + virtual std::optional ReadByte(std::size_t offset = 0) const; + // Reads size bytes starting at offset in file into a vector. + virtual std::vector ReadBytes(std::size_t size, std::size_t offset = 0) const; + // Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(), + // 0)' + virtual std::vector ReadAllBytes() const; + + // Reads an array of type T, size number_elements starting at offset. + // Returns the number of bytes (sizeof(T)*number_elements) read successfully. + template + std::size_t ReadArray(T* data, std::size_t number_elements, std::size_t offset = 0) const { + static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); + + return Read(reinterpret_cast(data), number_elements * sizeof(T), offset); + } + + // Reads size bytes into the memory starting at data starting at offset into the file. + // Returns the number of bytes read successfully. + template + std::size_t ReadBytes(T* data, std::size_t size, std::size_t offset = 0) const { + static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); + return Read(reinterpret_cast(data), size, offset); + } + + // Reads one object of type T starting at offset in file. + // Returns the number of bytes read successfully (sizeof(T)). + template + std::size_t ReadObject(T* data, std::size_t offset = 0) const { + static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); + return Read(reinterpret_cast(data), sizeof(T), offset); + } + + // Writes exactly one byte to offset in file and returns whether or not the byte was written + // successfully. + virtual bool WriteByte(u8 data, std::size_t offset = 0); + // Writes a vector of bytes to offset in file and returns the number of bytes successfully + // written. + virtual std::size_t WriteBytes(const std::vector& data, std::size_t offset = 0); + + // Writes an array of type T, size number_elements to offset in file. + // Returns the number of bytes (sizeof(T)*number_elements) written successfully. + template + std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) { + static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); + return Write(reinterpret_cast(data), number_elements * sizeof(T), offset); + } + + // Writes size bytes starting at memory location data to offset in file. + // Returns the number of bytes written successfully. + template + std::size_t WriteBytes(const T* data, std::size_t size, std::size_t offset = 0) { + static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); + return Write(reinterpret_cast(data), size, offset); + } + + // Writes one object of type T to offset in file. + // Returns the number of bytes written successfully (sizeof(T)). + template + std::size_t WriteObject(const T& data, std::size_t offset = 0) { + static_assert(std::is_trivially_copyable_v, "Data type must be trivially copyable."); + return Write(reinterpret_cast(&data), sizeof(T), offset); + } + + // Renames the file to name. Returns whether or not the operation was successful. + virtual bool Rename(std::string_view name) = 0; + + // Returns the full path of this file as a string, recursively + virtual std::string GetFullPath() const; +}; + +// A class representing a directory in an abstract filesystem. +class VfsDirectory { +public: + YUZU_NON_COPYABLE(VfsDirectory); + YUZU_NON_MOVEABLE(VfsDirectory); + + VfsDirectory() = default; + virtual ~VfsDirectory(); + + // Retrieves the file located at path as if the current directory was root. Returns nullptr if + // not found. + virtual VirtualFile GetFileRelative(std::string_view path) const; + // Calls GetFileRelative(path) on the root of the current directory. + virtual VirtualFile GetFileAbsolute(std::string_view path) const; + + // Retrieves the directory located at path as if the current directory was root. Returns nullptr + // if not found. + virtual VirtualDir GetDirectoryRelative(std::string_view path) const; + // Calls GetDirectoryRelative(path) on the root of the current directory. + virtual VirtualDir GetDirectoryAbsolute(std::string_view path) const; + + // Returns a vector containing all of the files in this directory. + virtual std::vector GetFiles() const = 0; + // Returns the file with filename matching name. Returns nullptr if directory doesn't have a + // file with name. + virtual VirtualFile GetFile(std::string_view name) const; + + // Returns a struct containing the file's timestamp. + virtual FileTimeStampRaw GetFileTimeStamp(std::string_view path) const; + + // Returns a vector containing all of the subdirectories in this directory. + virtual std::vector GetSubdirectories() const = 0; + // Returns the directory with name matching name. Returns nullptr if directory doesn't have a + // directory with name. + virtual VirtualDir GetSubdirectory(std::string_view name) const; + + // Returns whether or not the directory can be written to. + virtual bool IsWritable() const = 0; + // Returns whether of not the directory can be read from. + virtual bool IsReadable() const = 0; + + // Returns whether or not the directory is the root of the current file tree. + virtual bool IsRoot() const; + + // Returns the name of the directory. + virtual std::string GetName() const = 0; + // Returns the total size of all files and subdirectories in this directory. + virtual std::size_t GetSize() const; + // Returns the parent directory of this directory. Returns nullptr if this directory is root or + // has no parent. + virtual VirtualDir GetParentDirectory() const = 0; + + // Creates a new subdirectory with name name. Returns a pointer to the new directory or nullptr + // if the operation failed. + virtual VirtualDir CreateSubdirectory(std::string_view name) = 0; + // Creates a new file with name name. Returns a pointer to the new file or nullptr if the + // operation failed. + virtual VirtualFile CreateFile(std::string_view name) = 0; + + // Creates a new file at the path relative to this directory. Also creates directories if + // they do not exist and is supported by this implementation. Returns nullptr on any failure. + virtual VirtualFile CreateFileRelative(std::string_view path); + + // Creates a new file at the path relative to root of this directory. Also creates directories + // if they do not exist and is supported by this implementation. Returns nullptr on any failure. + virtual VirtualFile CreateFileAbsolute(std::string_view path); + + // Creates a new directory at the path relative to this directory. Also creates directories if + // they do not exist and is supported by this implementation. Returns nullptr on any failure. + virtual VirtualDir CreateDirectoryRelative(std::string_view path); + + // Creates a new directory at the path relative to root of this directory. Also creates + // directories if they do not exist and is supported by this implementation. Returns nullptr on + // any failure. + virtual VirtualDir CreateDirectoryAbsolute(std::string_view path); + + // Deletes the subdirectory with the given name and returns true on success. + virtual bool DeleteSubdirectory(std::string_view name) = 0; + + // Deletes all subdirectories and files within the provided directory and then deletes + // the directory itself. Returns true on success. + virtual bool DeleteSubdirectoryRecursive(std::string_view name); + + // Deletes all subdirectories and files within the provided directory. + // Unlike DeleteSubdirectoryRecursive, this does not delete the provided directory. + virtual bool CleanSubdirectoryRecursive(std::string_view name); + + // Returns whether or not the file with name name was deleted successfully. + virtual bool DeleteFile(std::string_view name) = 0; + + // Returns whether or not this directory was renamed to name. + virtual bool Rename(std::string_view name) = 0; + + // Returns whether or not the file with name src was successfully copied to a new file with name + // dest. + virtual bool Copy(std::string_view src, std::string_view dest); + + // Gets all of the entries directly in the directory (files and dirs), returning a map between + // item name -> type. + virtual std::map> GetEntries() const; + + // Returns the full path of this directory as a string, recursively + virtual std::string GetFullPath() const; +}; + +// A convenience partial-implementation of VfsDirectory that stubs out methods that should only work +// if writable. This is to avoid redundant empty methods everywhere. +class ReadOnlyVfsDirectory : public VfsDirectory { +public: + bool IsWritable() const override; + bool IsReadable() const override; + VirtualDir CreateSubdirectory(std::string_view name) override; + VirtualFile CreateFile(std::string_view name) override; + VirtualFile CreateFileAbsolute(std::string_view path) override; + VirtualFile CreateFileRelative(std::string_view path) override; + VirtualDir CreateDirectoryAbsolute(std::string_view path) override; + VirtualDir CreateDirectoryRelative(std::string_view path) override; + bool DeleteSubdirectory(std::string_view name) override; + bool DeleteSubdirectoryRecursive(std::string_view name) override; + bool CleanSubdirectoryRecursive(std::string_view name) override; + bool DeleteFile(std::string_view name) override; + bool Rename(std::string_view name) override; +}; + +// Compare the two files, byte-for-byte, in increments specified by block_size +bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, + std::size_t block_size = 0x1000); + +// A method that copies the raw data between two different implementations of VirtualFile. If you +// are using the same implementation, it is probably better to use the Copy method in the parent +// directory of src/dest. +bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size = 0x1000); + +// A method that performs a similar function to VfsRawCopy above, but instead copies entire +// directories. It suffers the same performance penalties as above and an implementation-specific +// Copy should always be preferred. +bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size = 0x1000); + +// Checks if the directory at path relative to rel exists. If it does, returns that. If it does not +// it attempts to create it and returns the new dir or nullptr on failure. +VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path); + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_cached.cpp b/src/core/file_sys/vfs/vfs_cached.cpp new file mode 100644 index 000000000..01cd0f1e0 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_cached.cpp @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/file_sys/vfs/vfs_cached.h" +#include "core/file_sys/vfs/vfs_types.h" + +namespace FileSys { + +CachedVfsDirectory::CachedVfsDirectory(VirtualDir&& source_dir) + : name(source_dir->GetName()), parent(source_dir->GetParentDirectory()) { + for (auto& dir : source_dir->GetSubdirectories()) { + dirs.emplace(dir->GetName(), std::make_shared(std::move(dir))); + } + for (auto& file : source_dir->GetFiles()) { + files.emplace(file->GetName(), std::move(file)); + } +} + +CachedVfsDirectory::~CachedVfsDirectory() = default; + +VirtualFile CachedVfsDirectory::GetFile(std::string_view file_name) const { + auto it = files.find(file_name); + if (it != files.end()) { + return it->second; + } + + return nullptr; +} + +VirtualDir CachedVfsDirectory::GetSubdirectory(std::string_view dir_name) const { + auto it = dirs.find(dir_name); + if (it != dirs.end()) { + return it->second; + } + + return nullptr; +} + +std::vector CachedVfsDirectory::GetFiles() const { + std::vector out; + for (auto& [file_name, file] : files) { + out.push_back(file); + } + return out; +} + +std::vector CachedVfsDirectory::GetSubdirectories() const { + std::vector out; + for (auto& [dir_name, dir] : dirs) { + out.push_back(dir); + } + return out; +} + +std::string CachedVfsDirectory::GetName() const { + return name; +} + +VirtualDir CachedVfsDirectory::GetParentDirectory() const { + return parent; +} + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_cached.h b/src/core/file_sys/vfs/vfs_cached.h new file mode 100644 index 000000000..47dff7224 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_cached.h @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include "core/file_sys/vfs/vfs.h" + +namespace FileSys { + +class CachedVfsDirectory : public ReadOnlyVfsDirectory { +public: + CachedVfsDirectory(VirtualDir&& source_directory); + + ~CachedVfsDirectory() override; + VirtualFile GetFile(std::string_view file_name) const override; + VirtualDir GetSubdirectory(std::string_view dir_name) const override; + std::vector GetFiles() const override; + std::vector GetSubdirectories() const override; + std::string GetName() const override; + VirtualDir GetParentDirectory() const override; + +private: + std::string name; + VirtualDir parent; + std::map> dirs; + std::map> files; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_concat.cpp b/src/core/file_sys/vfs/vfs_concat.cpp new file mode 100644 index 000000000..b5cc9a9e9 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_concat.cpp @@ -0,0 +1,192 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +#include "common/assert.h" +#include "core/file_sys/vfs/vfs_concat.h" +#include "core/file_sys/vfs/vfs_static.h" + +namespace FileSys { + +ConcatenatedVfsFile::ConcatenatedVfsFile(std::string&& name_, ConcatenationMap&& concatenation_map_) + : concatenation_map(std::move(concatenation_map_)), name(std::move(name_)) { + DEBUG_ASSERT(this->VerifyContinuity()); +} + +bool ConcatenatedVfsFile::VerifyContinuity() const { + u64 last_offset = 0; + for (auto& entry : concatenation_map) { + if (entry.offset != last_offset) { + return false; + } + + last_offset = entry.offset + entry.file->GetSize(); + } + + return true; +} + +ConcatenatedVfsFile::~ConcatenatedVfsFile() = default; + +VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::string&& name, + std::vector&& files) { + // Fold trivial cases. + if (files.empty()) { + return nullptr; + } + if (files.size() == 1) { + return files.front(); + } + + // Make the concatenation map from the input. + std::vector concatenation_map; + concatenation_map.reserve(files.size()); + u64 last_offset = 0; + + for (auto& file : files) { + const auto size = file->GetSize(); + + concatenation_map.emplace_back(ConcatenationEntry{ + .offset = last_offset, + .file = std::move(file), + }); + + last_offset += size; + } + + return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map))); +} + +VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile( + u8 filler_byte, std::string&& name, std::vector>&& files) { + // Fold trivial cases. + if (files.empty()) { + return nullptr; + } + if (files.size() == 1) { + return files.begin()->second; + } + + // Make the concatenation map from the input. + std::vector concatenation_map; + + concatenation_map.reserve(files.size()); + u64 last_offset = 0; + + // Iteration of a multimap is ordered, so offset will be strictly non-decreasing. + for (auto& [offset, file] : files) { + const auto size = file->GetSize(); + + if (offset > last_offset) { + concatenation_map.emplace_back(ConcatenationEntry{ + .offset = last_offset, + .file = std::make_shared(filler_byte, offset - last_offset), + }); + } + + concatenation_map.emplace_back(ConcatenationEntry{ + .offset = offset, + .file = std::move(file), + }); + + last_offset = offset + size; + } + + return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map))); +} + +std::string ConcatenatedVfsFile::GetName() const { + if (concatenation_map.empty()) { + return ""; + } + if (!name.empty()) { + return name; + } + return concatenation_map.front().file->GetName(); +} + +std::size_t ConcatenatedVfsFile::GetSize() const { + if (concatenation_map.empty()) { + return 0; + } + return concatenation_map.back().offset + concatenation_map.back().file->GetSize(); +} + +bool ConcatenatedVfsFile::Resize(std::size_t new_size) { + return false; +} + +VirtualDir ConcatenatedVfsFile::GetContainingDirectory() const { + if (concatenation_map.empty()) { + return nullptr; + } + return concatenation_map.front().file->GetContainingDirectory(); +} + +bool ConcatenatedVfsFile::IsWritable() const { + return false; +} + +bool ConcatenatedVfsFile::IsReadable() const { + return true; +} + +std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { + const ConcatenationEntry key{ + .offset = offset, + .file = nullptr, + }; + + // Read nothing if the map is empty. + if (concatenation_map.empty()) { + return 0; + } + + // Binary search to find the iterator to the first position we can check. + // It must exist, since we are not empty and are comparing unsigned integers. + auto it = std::prev(std::upper_bound(concatenation_map.begin(), concatenation_map.end(), key)); + u64 cur_length = length; + u64 cur_offset = offset; + + while (cur_length > 0 && it != concatenation_map.end()) { + // Check if we can read the file at this position. + const auto& file = it->file; + const u64 map_offset = it->offset; + const u64 file_size = file->GetSize(); + + if (cur_offset > map_offset + file_size) { + // Entirely out of bounds read. + break; + } + + // Read the file at this position. + const u64 file_seek = cur_offset - map_offset; + const u64 intended_read_size = std::min(cur_length, file_size - file_seek); + const u64 actual_read_size = + file->Read(data + (cur_offset - offset), intended_read_size, file_seek); + + // Update tracking. + cur_offset += actual_read_size; + cur_length -= actual_read_size; + it++; + + // If we encountered a short read, we're done. + if (actual_read_size < intended_read_size) { + break; + } + } + + return cur_offset - offset; +} + +std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { + return 0; +} + +bool ConcatenatedVfsFile::Rename(std::string_view new_name) { + return false; +} + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_concat.h b/src/core/file_sys/vfs/vfs_concat.h new file mode 100644 index 000000000..6d12af762 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_concat.h @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include "core/file_sys/vfs/vfs.h" + +namespace FileSys { + +// Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently +// read-only. +class ConcatenatedVfsFile : public VfsFile { +private: + struct ConcatenationEntry { + u64 offset; + VirtualFile file; + + auto operator<=>(const ConcatenationEntry& other) const { + return this->offset <=> other.offset; + } + }; + using ConcatenationMap = std::vector; + + explicit ConcatenatedVfsFile(std::string&& name, + std::vector&& concatenation_map); + bool VerifyContinuity() const; + +public: + ~ConcatenatedVfsFile() override; + + /// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases. + static VirtualFile MakeConcatenatedFile(std::string&& name, std::vector&& files); + + /// Convenience function that turns a map of offsets to files into a concatenated file, filling + /// gaps with a given filler byte. + static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::string&& name, + std::vector>&& files); + + std::string GetName() const override; + std::size_t GetSize() const override; + bool Resize(std::size_t new_size) override; + VirtualDir GetContainingDirectory() const override; + bool IsWritable() const override; + bool IsReadable() const override; + std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; + std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; + bool Rename(std::string_view new_name) override; + +private: + ConcatenationMap concatenation_map; + std::string name; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_layered.cpp b/src/core/file_sys/vfs/vfs_layered.cpp new file mode 100644 index 000000000..47b2a3c78 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_layered.cpp @@ -0,0 +1,132 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include "core/file_sys/vfs/vfs_layered.h" + +namespace FileSys { + +LayeredVfsDirectory::LayeredVfsDirectory(std::vector dirs_, std::string name_) + : dirs(std::move(dirs_)), name(std::move(name_)) {} + +LayeredVfsDirectory::~LayeredVfsDirectory() = default; + +VirtualDir LayeredVfsDirectory::MakeLayeredDirectory(std::vector dirs, + std::string name) { + if (dirs.empty()) + return nullptr; + if (dirs.size() == 1) + return dirs[0]; + + return VirtualDir(new LayeredVfsDirectory(std::move(dirs), std::move(name))); +} + +VirtualFile LayeredVfsDirectory::GetFileRelative(std::string_view path) const { + for (const auto& layer : dirs) { + const auto file = layer->GetFileRelative(path); + if (file != nullptr) + return file; + } + + return nullptr; +} + +VirtualDir LayeredVfsDirectory::GetDirectoryRelative(std::string_view path) const { + std::vector out; + for (const auto& layer : dirs) { + auto dir = layer->GetDirectoryRelative(path); + if (dir != nullptr) { + out.emplace_back(std::move(dir)); + } + } + + return MakeLayeredDirectory(std::move(out)); +} + +VirtualFile LayeredVfsDirectory::GetFile(std::string_view file_name) const { + return GetFileRelative(file_name); +} + +VirtualDir LayeredVfsDirectory::GetSubdirectory(std::string_view subdir_name) const { + return GetDirectoryRelative(subdir_name); +} + +std::string LayeredVfsDirectory::GetFullPath() const { + return dirs[0]->GetFullPath(); +} + +std::vector LayeredVfsDirectory::GetFiles() const { + std::vector out; + std::unordered_set out_names; + + for (const auto& layer : dirs) { + for (auto& file : layer->GetFiles()) { + const auto [it, is_new] = out_names.emplace(file->GetName()); + if (is_new) { + out.emplace_back(std::move(file)); + } + } + } + + return out; +} + +std::vector LayeredVfsDirectory::GetSubdirectories() const { + std::vector out; + std::unordered_set out_names; + + for (const auto& layer : dirs) { + for (const auto& sd : layer->GetSubdirectories()) { + out_names.emplace(sd->GetName()); + } + } + + out.reserve(out_names.size()); + for (const auto& subdir : out_names) { + out.emplace_back(GetSubdirectory(subdir)); + } + + return out; +} + +bool LayeredVfsDirectory::IsWritable() const { + return false; +} + +bool LayeredVfsDirectory::IsReadable() const { + return true; +} + +std::string LayeredVfsDirectory::GetName() const { + return name.empty() ? dirs[0]->GetName() : name; +} + +VirtualDir LayeredVfsDirectory::GetParentDirectory() const { + return dirs[0]->GetParentDirectory(); +} + +VirtualDir LayeredVfsDirectory::CreateSubdirectory(std::string_view subdir_name) { + return nullptr; +} + +VirtualFile LayeredVfsDirectory::CreateFile(std::string_view file_name) { + return nullptr; +} + +bool LayeredVfsDirectory::DeleteSubdirectory(std::string_view subdir_name) { + return false; +} + +bool LayeredVfsDirectory::DeleteFile(std::string_view file_name) { + return false; +} + +bool LayeredVfsDirectory::Rename(std::string_view new_name) { + name = new_name; + return true; +} + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_layered.h b/src/core/file_sys/vfs/vfs_layered.h new file mode 100644 index 000000000..0027ffa9a --- /dev/null +++ b/src/core/file_sys/vfs/vfs_layered.h @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "core/file_sys/vfs/vfs.h" + +namespace FileSys { + +// Class that stacks multiple VfsDirectories on top of each other, attempting to read from the first +// one and falling back to the one after. The highest priority directory (overwrites all others) +// should be element 0 in the dirs vector. +class LayeredVfsDirectory : public VfsDirectory { + explicit LayeredVfsDirectory(std::vector dirs_, std::string name_); + +public: + ~LayeredVfsDirectory() override; + + /// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases. + static VirtualDir MakeLayeredDirectory(std::vector dirs, std::string name = ""); + + VirtualFile GetFileRelative(std::string_view path) const override; + VirtualDir GetDirectoryRelative(std::string_view path) const override; + VirtualFile GetFile(std::string_view file_name) const override; + VirtualDir GetSubdirectory(std::string_view subdir_name) const override; + std::string GetFullPath() const override; + + std::vector GetFiles() const override; + std::vector GetSubdirectories() const override; + bool IsWritable() const override; + bool IsReadable() const override; + std::string GetName() const override; + VirtualDir GetParentDirectory() const override; + VirtualDir CreateSubdirectory(std::string_view subdir_name) override; + VirtualFile CreateFile(std::string_view file_name) override; + bool DeleteSubdirectory(std::string_view subdir_name) override; + bool DeleteFile(std::string_view file_name) override; + bool Rename(std::string_view new_name) override; + +private: + std::vector dirs; + std::string name; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_offset.cpp b/src/core/file_sys/vfs/vfs_offset.cpp new file mode 100644 index 000000000..1a37d2670 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_offset.cpp @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +#include "core/file_sys/vfs/vfs_offset.h" + +namespace FileSys { + +OffsetVfsFile::OffsetVfsFile(VirtualFile file_, std::size_t size_, std::size_t offset_, + std::string name_, VirtualDir parent_) + : file(file_), offset(offset_), size(size_), name(std::move(name_)), + parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {} + +OffsetVfsFile::~OffsetVfsFile() = default; + +std::string OffsetVfsFile::GetName() const { + return name.empty() ? file->GetName() : name; +} + +std::size_t OffsetVfsFile::GetSize() const { + return size; +} + +bool OffsetVfsFile::Resize(std::size_t new_size) { + if (offset + new_size < file->GetSize()) { + size = new_size; + } else { + auto res = file->Resize(offset + new_size); + if (!res) + return false; + size = new_size; + } + + return true; +} + +VirtualDir OffsetVfsFile::GetContainingDirectory() const { + return parent; +} + +bool OffsetVfsFile::IsWritable() const { + return file->IsWritable(); +} + +bool OffsetVfsFile::IsReadable() const { + return file->IsReadable(); +} + +std::size_t OffsetVfsFile::Read(u8* data, std::size_t length, std::size_t r_offset) const { + return file->Read(data, TrimToFit(length, r_offset), offset + r_offset); +} + +std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t r_offset) { + return file->Write(data, TrimToFit(length, r_offset), offset + r_offset); +} + +std::optional OffsetVfsFile::ReadByte(std::size_t r_offset) const { + if (r_offset >= size) { + return std::nullopt; + } + + return file->ReadByte(offset + r_offset); +} + +std::vector OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const { + return file->ReadBytes(TrimToFit(r_size, r_offset), offset + r_offset); +} + +std::vector OffsetVfsFile::ReadAllBytes() const { + return file->ReadBytes(size, offset); +} + +bool OffsetVfsFile::WriteByte(u8 data, std::size_t r_offset) { + if (r_offset < size) + return file->WriteByte(data, offset + r_offset); + + return false; +} + +std::size_t OffsetVfsFile::WriteBytes(const std::vector& data, std::size_t r_offset) { + return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset); +} + +bool OffsetVfsFile::Rename(std::string_view new_name) { + return file->Rename(new_name); +} + +std::size_t OffsetVfsFile::GetOffset() const { + return offset; +} + +std::size_t OffsetVfsFile::TrimToFit(std::size_t r_size, std::size_t r_offset) const { + return std::clamp(r_size, std::size_t{0}, size - r_offset); +} + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_offset.h b/src/core/file_sys/vfs/vfs_offset.h new file mode 100644 index 000000000..4abe41d8e --- /dev/null +++ b/src/core/file_sys/vfs/vfs_offset.h @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "core/file_sys/vfs/vfs.h" + +namespace FileSys { + +// An implementation of VfsFile that wraps around another VfsFile at a certain offset. +// Similar to seeking to an offset. +// If the file is writable, operations that would write past the end of the offset file will expand +// the size of this wrapper. +class OffsetVfsFile : public VfsFile { +public: + OffsetVfsFile(VirtualFile file, std::size_t size, std::size_t offset = 0, + std::string new_name = "", VirtualDir new_parent = nullptr); + ~OffsetVfsFile() override; + + std::string GetName() const override; + std::size_t GetSize() const override; + bool Resize(std::size_t new_size) override; + VirtualDir GetContainingDirectory() const override; + bool IsWritable() const override; + bool IsReadable() const override; + std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; + std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; + std::optional ReadByte(std::size_t offset) const override; + std::vector ReadBytes(std::size_t size, std::size_t offset) const override; + std::vector ReadAllBytes() const override; + bool WriteByte(u8 data, std::size_t offset) override; + std::size_t WriteBytes(const std::vector& data, std::size_t offset) override; + + bool Rename(std::string_view new_name) override; + + std::size_t GetOffset() const; + +private: + std::size_t TrimToFit(std::size_t r_size, std::size_t r_offset) const; + + VirtualFile file; + std::size_t offset; + std::size_t size; + std::string name; + VirtualDir parent; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_real.cpp b/src/core/file_sys/vfs/vfs_real.cpp new file mode 100644 index 000000000..1e6d8163b --- /dev/null +++ b/src/core/file_sys/vfs/vfs_real.cpp @@ -0,0 +1,528 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include "common/assert.h" +#include "common/fs/file.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" +#include "common/logging/log.h" +#include "core/file_sys/vfs/vfs.h" +#include "core/file_sys/vfs/vfs_real.h" + +// For FileTimeStampRaw +#include + +#ifdef _MSC_VER +#define stat _stat64 +#endif + +namespace FileSys { + +namespace FS = Common::FS; + +namespace { + +constexpr size_t MaxOpenFiles = 512; + +constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(Mode mode) { + switch (mode) { + case Mode::Read: + return FS::FileAccessMode::Read; + case Mode::Write: + case Mode::ReadWrite: + case Mode::Append: + case Mode::ReadAppend: + case Mode::WriteAppend: + case Mode::All: + return FS::FileAccessMode::ReadWrite; + default: + return {}; + } +} + +} // Anonymous namespace + +RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {} +RealVfsFilesystem::~RealVfsFilesystem() = default; + +std::string RealVfsFilesystem::GetName() const { + return "Real"; +} + +bool RealVfsFilesystem::IsReadable() const { + return true; +} + +bool RealVfsFilesystem::IsWritable() const { + return true; +} + +VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const { + const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); + if (!FS::Exists(path)) { + return VfsEntryType::None; + } + if (FS::IsDir(path)) { + return VfsEntryType::Directory; + } + + return VfsEntryType::File; +} + +VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::optional size, + Mode perms) { + const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); + std::scoped_lock lk{list_lock}; + + if (auto it = cache.find(path); it != cache.end()) { + if (auto file = it->second.lock(); file) { + return file; + } + } + + if (!size && !FS::IsFile(path)) { + return nullptr; + } + + auto reference = std::make_unique(); + this->InsertReferenceIntoListLocked(*reference); + + auto file = std::shared_ptr( + new RealVfsFile(*this, std::move(reference), path, perms, size)); + cache[path] = file; + + return file; +} + +VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { + return OpenFileFromEntry(path_, {}, perms); +} + +VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { + const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); + { + std::scoped_lock lk{list_lock}; + cache.erase(path); + } + + // Current usages of CreateFile expect to delete the contents of an existing file. + if (FS::IsFile(path)) { + FS::IOFile temp{path, FS::FileAccessMode::Write, FS::FileType::BinaryFile}; + + if (!temp.IsOpen()) { + return nullptr; + } + + temp.Close(); + + return OpenFile(path, perms); + } + + if (!FS::NewFile(path)) { + return nullptr; + } + + return OpenFile(path, perms); +} + +VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { + // Unused + return nullptr; +} + +VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { + const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); + const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); + { + std::scoped_lock lk{list_lock}; + cache.erase(old_path); + cache.erase(new_path); + } + if (!FS::RenameFile(old_path, new_path)) { + return nullptr; + } + return OpenFile(new_path, Mode::ReadWrite); +} + +bool RealVfsFilesystem::DeleteFile(std::string_view path_) { + const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); + { + std::scoped_lock lk{list_lock}; + cache.erase(path); + } + return FS::RemoveFile(path); +} + +VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { + const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); + return std::shared_ptr(new RealVfsDirectory(*this, path, perms)); +} + +VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { + const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); + if (!FS::CreateDirs(path)) { + return nullptr; + } + return std::shared_ptr(new RealVfsDirectory(*this, path, perms)); +} + +VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_, + std::string_view new_path_) { + // Unused + return nullptr; +} + +VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, + std::string_view new_path_) { + const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); + const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); + + if (!FS::RenameDir(old_path, new_path)) { + return nullptr; + } + return OpenDirectory(new_path, Mode::ReadWrite); +} + +bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { + const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); + return FS::RemoveDirRecursively(path); +} + +std::unique_lock RealVfsFilesystem::RefreshReference(const std::string& path, + Mode perms, + FileReference& reference) { + std::unique_lock lk{list_lock}; + + // Temporarily remove from list. + this->RemoveReferenceFromListLocked(reference); + + // Restore file if needed. + if (!reference.file) { + this->EvictSingleReferenceLocked(); + + reference.file = + FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile); + if (reference.file) { + num_open_files++; + } + } + + // Reinsert into list. + this->InsertReferenceIntoListLocked(reference); + + return lk; +} + +void RealVfsFilesystem::DropReference(std::unique_ptr&& reference) { + std::scoped_lock lk{list_lock}; + + // Remove from list. + this->RemoveReferenceFromListLocked(*reference); + + // Close the file. + if (reference->file) { + reference->file.reset(); + num_open_files--; + } +} + +void RealVfsFilesystem::EvictSingleReferenceLocked() { + if (num_open_files < MaxOpenFiles || open_references.empty()) { + return; + } + + // Get and remove from list. + auto& reference = open_references.back(); + this->RemoveReferenceFromListLocked(reference); + + // Close the file. + if (reference.file) { + reference.file.reset(); + num_open_files--; + } + + // Reinsert into closed list. + this->InsertReferenceIntoListLocked(reference); +} + +void RealVfsFilesystem::InsertReferenceIntoListLocked(FileReference& reference) { + if (reference.file) { + open_references.push_front(reference); + } else { + closed_references.push_front(reference); + } +} + +void RealVfsFilesystem::RemoveReferenceFromListLocked(FileReference& reference) { + if (reference.file) { + open_references.erase(open_references.iterator_to(reference)); + } else { + closed_references.erase(closed_references.iterator_to(reference)); + } +} + +RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr reference_, + const std::string& path_, Mode perms_, std::optional size_) + : base(base_), reference(std::move(reference_)), path(path_), + parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponentsCopy(path_)), + size(size_), perms(perms_) {} + +RealVfsFile::~RealVfsFile() { + base.DropReference(std::move(reference)); +} + +std::string RealVfsFile::GetName() const { + return path_components.empty() ? "" : std::string(path_components.back()); +} + +std::size_t RealVfsFile::GetSize() const { + if (size) { + return *size; + } + auto lk = base.RefreshReference(path, perms, *reference); + return reference->file ? reference->file->GetSize() : 0; +} + +bool RealVfsFile::Resize(std::size_t new_size) { + size.reset(); + auto lk = base.RefreshReference(path, perms, *reference); + return reference->file ? reference->file->SetSize(new_size) : false; +} + +VirtualDir RealVfsFile::GetContainingDirectory() const { + return base.OpenDirectory(parent_path, perms); +} + +bool RealVfsFile::IsWritable() const { + return True(perms & Mode::Write); +} + +bool RealVfsFile::IsReadable() const { + return True(perms & Mode::Read); +} + +std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { + auto lk = base.RefreshReference(path, perms, *reference); + if (!reference->file || !reference->file->Seek(static_cast(offset))) { + return 0; + } + return reference->file->ReadSpan(std::span{data, length}); +} + +std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { + size.reset(); + auto lk = base.RefreshReference(path, perms, *reference); + if (!reference->file || !reference->file->Seek(static_cast(offset))) { + return 0; + } + return reference->file->WriteSpan(std::span{data, length}); +} + +bool RealVfsFile::Rename(std::string_view name) { + return base.MoveFile(path, parent_path + '/' + std::string(name)) != nullptr; +} + +// TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if +// constexpr' because there is a compile error in the branch not used. + +template <> +std::vector RealVfsDirectory::IterateEntries() const { + if (perms == Mode::Append) { + return {}; + } + + std::vector out; + + const FS::DirEntryCallable callback = [this, + &out](const std::filesystem::directory_entry& entry) { + const auto full_path_string = FS::PathToUTF8String(entry.path()); + + out.emplace_back(base.OpenFileFromEntry(full_path_string, entry.file_size(), perms)); + + return true; + }; + + FS::IterateDirEntries(path, callback, FS::DirEntryFilter::File); + + return out; +} + +template <> +std::vector RealVfsDirectory::IterateEntries() const { + if (perms == Mode::Append) { + return {}; + } + + std::vector out; + + const FS::DirEntryCallable callback = [this, + &out](const std::filesystem::directory_entry& entry) { + const auto full_path_string = FS::PathToUTF8String(entry.path()); + + out.emplace_back(base.OpenDirectory(full_path_string, perms)); + + return true; + }; + + FS::IterateDirEntries(path, callback, FS::DirEntryFilter::Directory); + + return out; +} + +RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_) + : base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)), + path_components(FS::SplitPathComponentsCopy(path)), perms(perms_) { + if (!FS::Exists(path) && True(perms & Mode::Write)) { + void(FS::CreateDirs(path)); + } +} + +RealVfsDirectory::~RealVfsDirectory() = default; + +VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) const { + const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); + if (!FS::Exists(full_path) || FS::IsDir(full_path)) { + return nullptr; + } + return base.OpenFile(full_path, perms); +} + +VirtualDir RealVfsDirectory::GetDirectoryRelative(std::string_view relative_path) const { + const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); + if (!FS::Exists(full_path) || !FS::IsDir(full_path)) { + return nullptr; + } + return base.OpenDirectory(full_path, perms); +} + +VirtualFile RealVfsDirectory::GetFile(std::string_view name) const { + return GetFileRelative(name); +} + +VirtualDir RealVfsDirectory::GetSubdirectory(std::string_view name) const { + return GetDirectoryRelative(name); +} + +VirtualFile RealVfsDirectory::CreateFileRelative(std::string_view relative_path) { + const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); + if (!FS::CreateParentDirs(full_path)) { + return nullptr; + } + return base.CreateFile(full_path, perms); +} + +VirtualDir RealVfsDirectory::CreateDirectoryRelative(std::string_view relative_path) { + const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); + return base.CreateDirectory(full_path, perms); +} + +bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { + const auto full_path = FS::SanitizePath(this->path + '/' + std::string(name)); + return base.DeleteDirectory(full_path); +} + +std::vector RealVfsDirectory::GetFiles() const { + return IterateEntries(); +} + +FileTimeStampRaw RealVfsDirectory::GetFileTimeStamp(std::string_view path_) const { + const auto full_path = FS::SanitizePath(path + '/' + std::string(path_)); + const auto fs_path = std::filesystem::path{FS::ToU8String(full_path)}; + struct stat file_status; + +#ifdef _WIN32 + const auto stat_result = _wstat64(fs_path.c_str(), &file_status); +#else + const auto stat_result = stat(fs_path.c_str(), &file_status); +#endif + + if (stat_result != 0) { + return {}; + } + + return { + .created{static_cast(file_status.st_ctime)}, + .accessed{static_cast(file_status.st_atime)}, + .modified{static_cast(file_status.st_mtime)}, + }; +} + +std::vector RealVfsDirectory::GetSubdirectories() const { + return IterateEntries(); +} + +bool RealVfsDirectory::IsWritable() const { + return True(perms & Mode::Write); +} + +bool RealVfsDirectory::IsReadable() const { + return True(perms & Mode::Read); +} + +std::string RealVfsDirectory::GetName() const { + return path_components.empty() ? "" : std::string(path_components.back()); +} + +VirtualDir RealVfsDirectory::GetParentDirectory() const { + if (path_components.size() <= 1) { + return nullptr; + } + + return base.OpenDirectory(parent_path, perms); +} + +VirtualDir RealVfsDirectory::CreateSubdirectory(std::string_view name) { + const std::string subdir_path = (path + '/').append(name); + return base.CreateDirectory(subdir_path, perms); +} + +VirtualFile RealVfsDirectory::CreateFile(std::string_view name) { + const std::string file_path = (path + '/').append(name); + return base.CreateFile(file_path, perms); +} + +bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) { + const std::string subdir_path = (path + '/').append(name); + return base.DeleteDirectory(subdir_path); +} + +bool RealVfsDirectory::DeleteFile(std::string_view name) { + const std::string file_path = (path + '/').append(name); + return base.DeleteFile(file_path); +} + +bool RealVfsDirectory::Rename(std::string_view name) { + const std::string new_name = (parent_path + '/').append(name); + return base.MoveFile(path, new_name) != nullptr; +} + +std::string RealVfsDirectory::GetFullPath() const { + auto out = path; + std::replace(out.begin(), out.end(), '\\', '/'); + return out; +} + +std::map> RealVfsDirectory::GetEntries() const { + if (perms == Mode::Append) { + return {}; + } + + std::map> out; + + const FS::DirEntryCallable callback = [&out](const std::filesystem::directory_entry& entry) { + const auto filename = FS::PathToUTF8String(entry.path().filename()); + out.insert_or_assign(filename, + entry.is_directory() ? VfsEntryType::Directory : VfsEntryType::File); + return true; + }; + + FS::IterateDirEntries(path, callback); + + return out; +} + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_real.h b/src/core/file_sys/vfs/vfs_real.h new file mode 100644 index 000000000..1560bc1f9 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_real.h @@ -0,0 +1,145 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include "common/intrusive_list.h" +#include "core/file_sys/mode.h" +#include "core/file_sys/vfs/vfs.h" + +namespace Common::FS { +class IOFile; +} + +namespace FileSys { + +struct FileReference : public Common::IntrusiveListBaseNode { + std::shared_ptr file{}; +}; + +class RealVfsFile; +class RealVfsDirectory; + +class RealVfsFilesystem : public VfsFilesystem { +public: + RealVfsFilesystem(); + ~RealVfsFilesystem() override; + + std::string GetName() const override; + bool IsReadable() const override; + bool IsWritable() const override; + VfsEntryType GetEntryType(std::string_view path) const override; + VirtualFile OpenFile(std::string_view path, Mode perms = Mode::Read) override; + VirtualFile CreateFile(std::string_view path, Mode perms = Mode::ReadWrite) override; + VirtualFile CopyFile(std::string_view old_path, std::string_view new_path) override; + VirtualFile MoveFile(std::string_view old_path, std::string_view new_path) override; + bool DeleteFile(std::string_view path) override; + VirtualDir OpenDirectory(std::string_view path, Mode perms = Mode::Read) override; + VirtualDir CreateDirectory(std::string_view path, Mode perms = Mode::ReadWrite) override; + VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path) override; + VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path) override; + bool DeleteDirectory(std::string_view path) override; + +private: + using ReferenceListType = Common::IntrusiveListBaseTraits::ListType; + std::map, std::less<>> cache; + ReferenceListType open_references; + ReferenceListType closed_references; + std::mutex list_lock; + size_t num_open_files{}; + +private: + friend class RealVfsFile; + std::unique_lock RefreshReference(const std::string& path, Mode perms, + FileReference& reference); + void DropReference(std::unique_ptr&& reference); + +private: + friend class RealVfsDirectory; + VirtualFile OpenFileFromEntry(std::string_view path, std::optional size, + Mode perms = Mode::Read); + +private: + void EvictSingleReferenceLocked(); + void InsertReferenceIntoListLocked(FileReference& reference); + void RemoveReferenceFromListLocked(FileReference& reference); +}; + +// An implementation of VfsFile that represents a file on the user's computer. +class RealVfsFile : public VfsFile { + friend class RealVfsDirectory; + friend class RealVfsFilesystem; + +public: + ~RealVfsFile() override; + + std::string GetName() const override; + std::size_t GetSize() const override; + bool Resize(std::size_t new_size) override; + VirtualDir GetContainingDirectory() const override; + bool IsWritable() const override; + bool IsReadable() const override; + std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; + std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; + bool Rename(std::string_view name) override; + +private: + RealVfsFile(RealVfsFilesystem& base, std::unique_ptr reference, + const std::string& path, Mode perms = Mode::Read, std::optional size = {}); + + RealVfsFilesystem& base; + std::unique_ptr reference; + std::string path; + std::string parent_path; + std::vector path_components; + std::optional size; + Mode perms; +}; + +// An implementation of VfsDirectory that represents a directory on the user's computer. +class RealVfsDirectory : public VfsDirectory { + friend class RealVfsFilesystem; + +public: + ~RealVfsDirectory() override; + + VirtualFile GetFileRelative(std::string_view relative_path) const override; + VirtualDir GetDirectoryRelative(std::string_view relative_path) const override; + VirtualFile GetFile(std::string_view name) const override; + VirtualDir GetSubdirectory(std::string_view name) const override; + VirtualFile CreateFileRelative(std::string_view relative_path) override; + VirtualDir CreateDirectoryRelative(std::string_view relative_path) override; + bool DeleteSubdirectoryRecursive(std::string_view name) override; + std::vector GetFiles() const override; + FileTimeStampRaw GetFileTimeStamp(std::string_view path) const override; + std::vector GetSubdirectories() const override; + bool IsWritable() const override; + bool IsReadable() const override; + std::string GetName() const override; + VirtualDir GetParentDirectory() const override; + VirtualDir CreateSubdirectory(std::string_view name) override; + VirtualFile CreateFile(std::string_view name) override; + bool DeleteSubdirectory(std::string_view name) override; + bool DeleteFile(std::string_view name) override; + bool Rename(std::string_view name) override; + std::string GetFullPath() const override; + std::map> GetEntries() const override; + +private: + RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read); + + template + std::vector> IterateEntries() const; + + RealVfsFilesystem& base; + std::string path; + std::string parent_path; + std::vector path_components; + Mode perms; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_static.h b/src/core/file_sys/vfs/vfs_static.h new file mode 100644 index 000000000..bb53560ac --- /dev/null +++ b/src/core/file_sys/vfs/vfs_static.h @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +#include "core/file_sys/vfs/vfs.h" + +namespace FileSys { + +class StaticVfsFile : public VfsFile { +public: + explicit StaticVfsFile(u8 value_, std::size_t size_ = 0, std::string name_ = "", + VirtualDir parent_ = nullptr) + : value{value_}, size{size_}, name{std::move(name_)}, parent{std::move(parent_)} {} + + std::string GetName() const override { + return name; + } + + std::size_t GetSize() const override { + return size; + } + + bool Resize(std::size_t new_size) override { + size = new_size; + return true; + } + + VirtualDir GetContainingDirectory() const override { + return parent; + } + + bool IsWritable() const override { + return false; + } + + bool IsReadable() const override { + return true; + } + + std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override { + const auto read = std::min(length, size - offset); + std::fill(data, data + read, value); + return read; + } + + std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override { + return 0; + } + + std::optional ReadByte(std::size_t offset) const override { + if (offset >= size) { + return std::nullopt; + } + + return value; + } + + std::vector ReadBytes(std::size_t length, std::size_t offset) const override { + const auto read = std::min(length, size - offset); + return std::vector(read, value); + } + + bool Rename(std::string_view new_name) override { + name = new_name; + return true; + } + +private: + u8 value; + std::size_t size; + std::string name; + VirtualDir parent; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_types.h b/src/core/file_sys/vfs/vfs_types.h new file mode 100644 index 000000000..4a583ed64 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_types.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" + +namespace FileSys { + +class VfsDirectory; +class VfsFile; +class VfsFilesystem; + +// Declarations for Vfs* pointer types + +using VirtualDir = std::shared_ptr; +using VirtualFile = std::shared_ptr; +using VirtualFilesystem = std::shared_ptr; + +struct FileTimeStampRaw { + u64 created{}; + u64 accessed{}; + u64 modified{}; + u64 padding{}; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_vector.cpp b/src/core/file_sys/vfs/vfs_vector.cpp new file mode 100644 index 000000000..0d54461c8 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_vector.cpp @@ -0,0 +1,133 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include "core/file_sys/vfs/vfs_vector.h" + +namespace FileSys { +VectorVfsFile::VectorVfsFile(std::vector initial_data, std::string name_, VirtualDir parent_) + : data(std::move(initial_data)), parent(std::move(parent_)), name(std::move(name_)) {} + +VectorVfsFile::~VectorVfsFile() = default; + +std::string VectorVfsFile::GetName() const { + return name; +} + +size_t VectorVfsFile::GetSize() const { + return data.size(); +} + +bool VectorVfsFile::Resize(size_t new_size) { + data.resize(new_size); + return true; +} + +VirtualDir VectorVfsFile::GetContainingDirectory() const { + return parent; +} + +bool VectorVfsFile::IsWritable() const { + return true; +} + +bool VectorVfsFile::IsReadable() const { + return true; +} + +std::size_t VectorVfsFile::Read(u8* data_, std::size_t length, std::size_t offset) const { + const auto read = std::min(length, data.size() - offset); + std::memcpy(data_, data.data() + offset, read); + return read; +} + +std::size_t VectorVfsFile::Write(const u8* data_, std::size_t length, std::size_t offset) { + if (offset + length > data.size()) + data.resize(offset + length); + const auto write = std::min(length, data.size() - offset); + std::memcpy(data.data() + offset, data_, write); + return write; +} + +bool VectorVfsFile::Rename(std::string_view name_) { + name = name_; + return true; +} + +void VectorVfsFile::Assign(std::vector new_data) { + data = std::move(new_data); +} + +VectorVfsDirectory::VectorVfsDirectory(std::vector files_, + std::vector dirs_, std::string name_, + VirtualDir parent_) + : files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)), + name(std::move(name_)) {} + +VectorVfsDirectory::~VectorVfsDirectory() = default; + +std::vector VectorVfsDirectory::GetFiles() const { + return files; +} + +std::vector VectorVfsDirectory::GetSubdirectories() const { + return dirs; +} + +bool VectorVfsDirectory::IsWritable() const { + return false; +} + +bool VectorVfsDirectory::IsReadable() const { + return true; +} + +std::string VectorVfsDirectory::GetName() const { + return name; +} + +VirtualDir VectorVfsDirectory::GetParentDirectory() const { + return parent; +} + +template +static bool FindAndRemoveVectorElement(std::vector& vec, std::string_view name) { + const auto iter = + std::find_if(vec.begin(), vec.end(), [name](const T& e) { return e->GetName() == name; }); + if (iter == vec.end()) + return false; + + vec.erase(iter); + return true; +} + +bool VectorVfsDirectory::DeleteSubdirectory(std::string_view subdir_name) { + return FindAndRemoveVectorElement(dirs, subdir_name); +} + +bool VectorVfsDirectory::DeleteFile(std::string_view file_name) { + return FindAndRemoveVectorElement(files, file_name); +} + +bool VectorVfsDirectory::Rename(std::string_view name_) { + name = name_; + return true; +} + +VirtualDir VectorVfsDirectory::CreateSubdirectory(std::string_view subdir_name) { + return nullptr; +} + +VirtualFile VectorVfsDirectory::CreateFile(std::string_view file_name) { + return nullptr; +} + +void VectorVfsDirectory::AddFile(VirtualFile file) { + files.push_back(std::move(file)); +} + +void VectorVfsDirectory::AddDirectory(VirtualDir dir) { + dirs.push_back(std::move(dir)); +} +} // namespace FileSys diff --git a/src/core/file_sys/vfs/vfs_vector.h b/src/core/file_sys/vfs/vfs_vector.h new file mode 100644 index 000000000..587187dd2 --- /dev/null +++ b/src/core/file_sys/vfs/vfs_vector.h @@ -0,0 +1,131 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include "core/file_sys/vfs/vfs.h" + +namespace FileSys { + +// An implementation of VfsFile that is backed by a statically-sized array +template +class ArrayVfsFile : public VfsFile { +public: + explicit ArrayVfsFile(const std::array& data_, std::string name_ = "", + VirtualDir parent_ = nullptr) + : data(data_), name(std::move(name_)), parent(std::move(parent_)) {} + + std::string GetName() const override { + return name; + } + + std::size_t GetSize() const override { + return size; + } + + bool Resize(std::size_t new_size) override { + return false; + } + + VirtualDir GetContainingDirectory() const override { + return parent; + } + + bool IsWritable() const override { + return false; + } + + bool IsReadable() const override { + return true; + } + + std::size_t Read(u8* data_, std::size_t length, std::size_t offset) const override { + const auto read = std::min(length, size - offset); + std::memcpy(data_, data.data() + offset, read); + return read; + } + + std::size_t Write(const u8* data_, std::size_t length, std::size_t offset) override { + return 0; + } + + bool Rename(std::string_view new_name) override { + name = new_name; + return true; + } + +private: + std::array data; + std::string name; + VirtualDir parent; +}; + +template +std::shared_ptr> MakeArrayFile(const std::array& data, + Args&&... args) { + return std::make_shared>(data, std::forward(args)...); +} + +// An implementation of VfsFile that is backed by a vector optionally supplied upon construction +class VectorVfsFile : public VfsFile { +public: + explicit VectorVfsFile(std::vector initial_data = {}, std::string name_ = "", + VirtualDir parent_ = nullptr); + ~VectorVfsFile() override; + + std::string GetName() const override; + std::size_t GetSize() const override; + bool Resize(std::size_t new_size) override; + VirtualDir GetContainingDirectory() const override; + bool IsWritable() const override; + bool IsReadable() const override; + std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; + std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; + bool Rename(std::string_view name) override; + + virtual void Assign(std::vector new_data); + +private: + std::vector data; + VirtualDir parent; + std::string name; +}; + +// An implementation of VfsDirectory that maintains two vectors for subdirectories and files. +// Vector data is supplied upon construction. +class VectorVfsDirectory : public VfsDirectory { +public: + explicit VectorVfsDirectory(std::vector files = {}, + std::vector dirs = {}, std::string name = "", + VirtualDir parent = nullptr); + ~VectorVfsDirectory() override; + + std::vector GetFiles() const override; + std::vector GetSubdirectories() const override; + bool IsWritable() const override; + bool IsReadable() const override; + std::string GetName() const override; + VirtualDir GetParentDirectory() const override; + bool DeleteSubdirectory(std::string_view subdir_name) override; + bool DeleteFile(std::string_view file_name) override; + bool Rename(std::string_view name) override; + VirtualDir CreateSubdirectory(std::string_view subdir_name) override; + VirtualFile CreateFile(std::string_view file_name) override; + + virtual void AddFile(VirtualFile file); + virtual void AddDirectory(VirtualDir dir); + +private: + std::vector files; + std::vector dirs; + + VirtualDir parent; + std::string name; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/vfs_cached.cpp b/src/core/file_sys/vfs_cached.cpp deleted file mode 100644 index 7ee5300e5..000000000 --- a/src/core/file_sys/vfs_cached.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/file_sys/vfs_cached.h" -#include "core/file_sys/vfs_types.h" - -namespace FileSys { - -CachedVfsDirectory::CachedVfsDirectory(VirtualDir&& source_dir) - : name(source_dir->GetName()), parent(source_dir->GetParentDirectory()) { - for (auto& dir : source_dir->GetSubdirectories()) { - dirs.emplace(dir->GetName(), std::make_shared(std::move(dir))); - } - for (auto& file : source_dir->GetFiles()) { - files.emplace(file->GetName(), std::move(file)); - } -} - -CachedVfsDirectory::~CachedVfsDirectory() = default; - -VirtualFile CachedVfsDirectory::GetFile(std::string_view file_name) const { - auto it = files.find(file_name); - if (it != files.end()) { - return it->second; - } - - return nullptr; -} - -VirtualDir CachedVfsDirectory::GetSubdirectory(std::string_view dir_name) const { - auto it = dirs.find(dir_name); - if (it != dirs.end()) { - return it->second; - } - - return nullptr; -} - -std::vector CachedVfsDirectory::GetFiles() const { - std::vector out; - for (auto& [file_name, file] : files) { - out.push_back(file); - } - return out; -} - -std::vector CachedVfsDirectory::GetSubdirectories() const { - std::vector out; - for (auto& [dir_name, dir] : dirs) { - out.push_back(dir); - } - return out; -} - -std::string CachedVfsDirectory::GetName() const { - return name; -} - -VirtualDir CachedVfsDirectory::GetParentDirectory() const { - return parent; -} - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_cached.h b/src/core/file_sys/vfs_cached.h deleted file mode 100644 index 1e5300784..000000000 --- a/src/core/file_sys/vfs_cached.h +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include "core/file_sys/vfs.h" - -namespace FileSys { - -class CachedVfsDirectory : public ReadOnlyVfsDirectory { -public: - CachedVfsDirectory(VirtualDir&& source_directory); - - ~CachedVfsDirectory() override; - VirtualFile GetFile(std::string_view file_name) const override; - VirtualDir GetSubdirectory(std::string_view dir_name) const override; - std::vector GetFiles() const override; - std::vector GetSubdirectories() const override; - std::string GetName() const override; - VirtualDir GetParentDirectory() const override; - -private: - std::string name; - VirtualDir parent; - std::map> dirs; - std::map> files; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp deleted file mode 100644 index 7c7298527..000000000 --- a/src/core/file_sys/vfs_concat.cpp +++ /dev/null @@ -1,192 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include - -#include "common/assert.h" -#include "core/file_sys/vfs_concat.h" -#include "core/file_sys/vfs_static.h" - -namespace FileSys { - -ConcatenatedVfsFile::ConcatenatedVfsFile(std::string&& name_, ConcatenationMap&& concatenation_map_) - : concatenation_map(std::move(concatenation_map_)), name(std::move(name_)) { - DEBUG_ASSERT(this->VerifyContinuity()); -} - -bool ConcatenatedVfsFile::VerifyContinuity() const { - u64 last_offset = 0; - for (auto& entry : concatenation_map) { - if (entry.offset != last_offset) { - return false; - } - - last_offset = entry.offset + entry.file->GetSize(); - } - - return true; -} - -ConcatenatedVfsFile::~ConcatenatedVfsFile() = default; - -VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::string&& name, - std::vector&& files) { - // Fold trivial cases. - if (files.empty()) { - return nullptr; - } - if (files.size() == 1) { - return files.front(); - } - - // Make the concatenation map from the input. - std::vector concatenation_map; - concatenation_map.reserve(files.size()); - u64 last_offset = 0; - - for (auto& file : files) { - const auto size = file->GetSize(); - - concatenation_map.emplace_back(ConcatenationEntry{ - .offset = last_offset, - .file = std::move(file), - }); - - last_offset += size; - } - - return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map))); -} - -VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile( - u8 filler_byte, std::string&& name, std::vector>&& files) { - // Fold trivial cases. - if (files.empty()) { - return nullptr; - } - if (files.size() == 1) { - return files.begin()->second; - } - - // Make the concatenation map from the input. - std::vector concatenation_map; - - concatenation_map.reserve(files.size()); - u64 last_offset = 0; - - // Iteration of a multimap is ordered, so offset will be strictly non-decreasing. - for (auto& [offset, file] : files) { - const auto size = file->GetSize(); - - if (offset > last_offset) { - concatenation_map.emplace_back(ConcatenationEntry{ - .offset = last_offset, - .file = std::make_shared(filler_byte, offset - last_offset), - }); - } - - concatenation_map.emplace_back(ConcatenationEntry{ - .offset = offset, - .file = std::move(file), - }); - - last_offset = offset + size; - } - - return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map))); -} - -std::string ConcatenatedVfsFile::GetName() const { - if (concatenation_map.empty()) { - return ""; - } - if (!name.empty()) { - return name; - } - return concatenation_map.front().file->GetName(); -} - -std::size_t ConcatenatedVfsFile::GetSize() const { - if (concatenation_map.empty()) { - return 0; - } - return concatenation_map.back().offset + concatenation_map.back().file->GetSize(); -} - -bool ConcatenatedVfsFile::Resize(std::size_t new_size) { - return false; -} - -VirtualDir ConcatenatedVfsFile::GetContainingDirectory() const { - if (concatenation_map.empty()) { - return nullptr; - } - return concatenation_map.front().file->GetContainingDirectory(); -} - -bool ConcatenatedVfsFile::IsWritable() const { - return false; -} - -bool ConcatenatedVfsFile::IsReadable() const { - return true; -} - -std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { - const ConcatenationEntry key{ - .offset = offset, - .file = nullptr, - }; - - // Read nothing if the map is empty. - if (concatenation_map.empty()) { - return 0; - } - - // Binary search to find the iterator to the first position we can check. - // It must exist, since we are not empty and are comparing unsigned integers. - auto it = std::prev(std::upper_bound(concatenation_map.begin(), concatenation_map.end(), key)); - u64 cur_length = length; - u64 cur_offset = offset; - - while (cur_length > 0 && it != concatenation_map.end()) { - // Check if we can read the file at this position. - const auto& file = it->file; - const u64 map_offset = it->offset; - const u64 file_size = file->GetSize(); - - if (cur_offset > map_offset + file_size) { - // Entirely out of bounds read. - break; - } - - // Read the file at this position. - const u64 file_seek = cur_offset - map_offset; - const u64 intended_read_size = std::min(cur_length, file_size - file_seek); - const u64 actual_read_size = - file->Read(data + (cur_offset - offset), intended_read_size, file_seek); - - // Update tracking. - cur_offset += actual_read_size; - cur_length -= actual_read_size; - it++; - - // If we encountered a short read, we're done. - if (actual_read_size < intended_read_size) { - break; - } - } - - return cur_offset - offset; -} - -std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { - return 0; -} - -bool ConcatenatedVfsFile::Rename(std::string_view new_name) { - return false; -} - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_concat.h b/src/core/file_sys/vfs_concat.h deleted file mode 100644 index b5f3d72e3..000000000 --- a/src/core/file_sys/vfs_concat.h +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include "core/file_sys/vfs.h" - -namespace FileSys { - -// Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently -// read-only. -class ConcatenatedVfsFile : public VfsFile { -private: - struct ConcatenationEntry { - u64 offset; - VirtualFile file; - - auto operator<=>(const ConcatenationEntry& other) const { - return this->offset <=> other.offset; - } - }; - using ConcatenationMap = std::vector; - - explicit ConcatenatedVfsFile(std::string&& name, - std::vector&& concatenation_map); - bool VerifyContinuity() const; - -public: - ~ConcatenatedVfsFile() override; - - /// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases. - static VirtualFile MakeConcatenatedFile(std::string&& name, std::vector&& files); - - /// Convenience function that turns a map of offsets to files into a concatenated file, filling - /// gaps with a given filler byte. - static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::string&& name, - std::vector>&& files); - - std::string GetName() const override; - std::size_t GetSize() const override; - bool Resize(std::size_t new_size) override; - VirtualDir GetContainingDirectory() const override; - bool IsWritable() const override; - bool IsReadable() const override; - std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; - std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; - bool Rename(std::string_view new_name) override; - -private: - ConcatenationMap concatenation_map; - std::string name; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs_layered.cpp deleted file mode 100644 index 5551743fb..000000000 --- a/src/core/file_sys/vfs_layered.cpp +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include -#include "core/file_sys/vfs_layered.h" - -namespace FileSys { - -LayeredVfsDirectory::LayeredVfsDirectory(std::vector dirs_, std::string name_) - : dirs(std::move(dirs_)), name(std::move(name_)) {} - -LayeredVfsDirectory::~LayeredVfsDirectory() = default; - -VirtualDir LayeredVfsDirectory::MakeLayeredDirectory(std::vector dirs, - std::string name) { - if (dirs.empty()) - return nullptr; - if (dirs.size() == 1) - return dirs[0]; - - return VirtualDir(new LayeredVfsDirectory(std::move(dirs), std::move(name))); -} - -VirtualFile LayeredVfsDirectory::GetFileRelative(std::string_view path) const { - for (const auto& layer : dirs) { - const auto file = layer->GetFileRelative(path); - if (file != nullptr) - return file; - } - - return nullptr; -} - -VirtualDir LayeredVfsDirectory::GetDirectoryRelative(std::string_view path) const { - std::vector out; - for (const auto& layer : dirs) { - auto dir = layer->GetDirectoryRelative(path); - if (dir != nullptr) { - out.emplace_back(std::move(dir)); - } - } - - return MakeLayeredDirectory(std::move(out)); -} - -VirtualFile LayeredVfsDirectory::GetFile(std::string_view file_name) const { - return GetFileRelative(file_name); -} - -VirtualDir LayeredVfsDirectory::GetSubdirectory(std::string_view subdir_name) const { - return GetDirectoryRelative(subdir_name); -} - -std::string LayeredVfsDirectory::GetFullPath() const { - return dirs[0]->GetFullPath(); -} - -std::vector LayeredVfsDirectory::GetFiles() const { - std::vector out; - std::unordered_set out_names; - - for (const auto& layer : dirs) { - for (auto& file : layer->GetFiles()) { - const auto [it, is_new] = out_names.emplace(file->GetName()); - if (is_new) { - out.emplace_back(std::move(file)); - } - } - } - - return out; -} - -std::vector LayeredVfsDirectory::GetSubdirectories() const { - std::vector out; - std::unordered_set out_names; - - for (const auto& layer : dirs) { - for (const auto& sd : layer->GetSubdirectories()) { - out_names.emplace(sd->GetName()); - } - } - - out.reserve(out_names.size()); - for (const auto& subdir : out_names) { - out.emplace_back(GetSubdirectory(subdir)); - } - - return out; -} - -bool LayeredVfsDirectory::IsWritable() const { - return false; -} - -bool LayeredVfsDirectory::IsReadable() const { - return true; -} - -std::string LayeredVfsDirectory::GetName() const { - return name.empty() ? dirs[0]->GetName() : name; -} - -VirtualDir LayeredVfsDirectory::GetParentDirectory() const { - return dirs[0]->GetParentDirectory(); -} - -VirtualDir LayeredVfsDirectory::CreateSubdirectory(std::string_view subdir_name) { - return nullptr; -} - -VirtualFile LayeredVfsDirectory::CreateFile(std::string_view file_name) { - return nullptr; -} - -bool LayeredVfsDirectory::DeleteSubdirectory(std::string_view subdir_name) { - return false; -} - -bool LayeredVfsDirectory::DeleteFile(std::string_view file_name) { - return false; -} - -bool LayeredVfsDirectory::Rename(std::string_view new_name) { - name = new_name; - return true; -} - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_layered.h b/src/core/file_sys/vfs_layered.h deleted file mode 100644 index a62112e9d..000000000 --- a/src/core/file_sys/vfs_layered.h +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include "core/file_sys/vfs.h" - -namespace FileSys { - -// Class that stacks multiple VfsDirectories on top of each other, attempting to read from the first -// one and falling back to the one after. The highest priority directory (overwrites all others) -// should be element 0 in the dirs vector. -class LayeredVfsDirectory : public VfsDirectory { - explicit LayeredVfsDirectory(std::vector dirs_, std::string name_); - -public: - ~LayeredVfsDirectory() override; - - /// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases. - static VirtualDir MakeLayeredDirectory(std::vector dirs, std::string name = ""); - - VirtualFile GetFileRelative(std::string_view path) const override; - VirtualDir GetDirectoryRelative(std::string_view path) const override; - VirtualFile GetFile(std::string_view file_name) const override; - VirtualDir GetSubdirectory(std::string_view subdir_name) const override; - std::string GetFullPath() const override; - - std::vector GetFiles() const override; - std::vector GetSubdirectories() const override; - bool IsWritable() const override; - bool IsReadable() const override; - std::string GetName() const override; - VirtualDir GetParentDirectory() const override; - VirtualDir CreateSubdirectory(std::string_view subdir_name) override; - VirtualFile CreateFile(std::string_view file_name) override; - bool DeleteSubdirectory(std::string_view subdir_name) override; - bool DeleteFile(std::string_view file_name) override; - bool Rename(std::string_view new_name) override; - -private: - std::vector dirs; - std::string name; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp deleted file mode 100644 index d950a6633..000000000 --- a/src/core/file_sys/vfs_offset.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include - -#include "core/file_sys/vfs_offset.h" - -namespace FileSys { - -OffsetVfsFile::OffsetVfsFile(VirtualFile file_, std::size_t size_, std::size_t offset_, - std::string name_, VirtualDir parent_) - : file(file_), offset(offset_), size(size_), name(std::move(name_)), - parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {} - -OffsetVfsFile::~OffsetVfsFile() = default; - -std::string OffsetVfsFile::GetName() const { - return name.empty() ? file->GetName() : name; -} - -std::size_t OffsetVfsFile::GetSize() const { - return size; -} - -bool OffsetVfsFile::Resize(std::size_t new_size) { - if (offset + new_size < file->GetSize()) { - size = new_size; - } else { - auto res = file->Resize(offset + new_size); - if (!res) - return false; - size = new_size; - } - - return true; -} - -VirtualDir OffsetVfsFile::GetContainingDirectory() const { - return parent; -} - -bool OffsetVfsFile::IsWritable() const { - return file->IsWritable(); -} - -bool OffsetVfsFile::IsReadable() const { - return file->IsReadable(); -} - -std::size_t OffsetVfsFile::Read(u8* data, std::size_t length, std::size_t r_offset) const { - return file->Read(data, TrimToFit(length, r_offset), offset + r_offset); -} - -std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t r_offset) { - return file->Write(data, TrimToFit(length, r_offset), offset + r_offset); -} - -std::optional OffsetVfsFile::ReadByte(std::size_t r_offset) const { - if (r_offset >= size) { - return std::nullopt; - } - - return file->ReadByte(offset + r_offset); -} - -std::vector OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const { - return file->ReadBytes(TrimToFit(r_size, r_offset), offset + r_offset); -} - -std::vector OffsetVfsFile::ReadAllBytes() const { - return file->ReadBytes(size, offset); -} - -bool OffsetVfsFile::WriteByte(u8 data, std::size_t r_offset) { - if (r_offset < size) - return file->WriteByte(data, offset + r_offset); - - return false; -} - -std::size_t OffsetVfsFile::WriteBytes(const std::vector& data, std::size_t r_offset) { - return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset); -} - -bool OffsetVfsFile::Rename(std::string_view new_name) { - return file->Rename(new_name); -} - -std::size_t OffsetVfsFile::GetOffset() const { - return offset; -} - -std::size_t OffsetVfsFile::TrimToFit(std::size_t r_size, std::size_t r_offset) const { - return std::clamp(r_size, std::size_t{0}, size - r_offset); -} - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h deleted file mode 100644 index 6c051ca00..000000000 --- a/src/core/file_sys/vfs_offset.h +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include - -#include "core/file_sys/vfs.h" - -namespace FileSys { - -// An implementation of VfsFile that wraps around another VfsFile at a certain offset. -// Similar to seeking to an offset. -// If the file is writable, operations that would write past the end of the offset file will expand -// the size of this wrapper. -class OffsetVfsFile : public VfsFile { -public: - OffsetVfsFile(VirtualFile file, std::size_t size, std::size_t offset = 0, - std::string new_name = "", VirtualDir new_parent = nullptr); - ~OffsetVfsFile() override; - - std::string GetName() const override; - std::size_t GetSize() const override; - bool Resize(std::size_t new_size) override; - VirtualDir GetContainingDirectory() const override; - bool IsWritable() const override; - bool IsReadable() const override; - std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; - std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; - std::optional ReadByte(std::size_t offset) const override; - std::vector ReadBytes(std::size_t size, std::size_t offset) const override; - std::vector ReadAllBytes() const override; - bool WriteByte(u8 data, std::size_t offset) override; - std::size_t WriteBytes(const std::vector& data, std::size_t offset) override; - - bool Rename(std::string_view new_name) override; - - std::size_t GetOffset() const; - -private: - std::size_t TrimToFit(std::size_t r_size, std::size_t r_offset) const; - - VirtualFile file; - std::size_t offset; - std::size_t size; - std::string name; - VirtualDir parent; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp deleted file mode 100644 index cd9b79786..000000000 --- a/src/core/file_sys/vfs_real.cpp +++ /dev/null @@ -1,528 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include -#include "common/assert.h" -#include "common/fs/file.h" -#include "common/fs/fs.h" -#include "common/fs/path_util.h" -#include "common/logging/log.h" -#include "core/file_sys/vfs.h" -#include "core/file_sys/vfs_real.h" - -// For FileTimeStampRaw -#include - -#ifdef _MSC_VER -#define stat _stat64 -#endif - -namespace FileSys { - -namespace FS = Common::FS; - -namespace { - -constexpr size_t MaxOpenFiles = 512; - -constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(Mode mode) { - switch (mode) { - case Mode::Read: - return FS::FileAccessMode::Read; - case Mode::Write: - case Mode::ReadWrite: - case Mode::Append: - case Mode::ReadAppend: - case Mode::WriteAppend: - case Mode::All: - return FS::FileAccessMode::ReadWrite; - default: - return {}; - } -} - -} // Anonymous namespace - -RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {} -RealVfsFilesystem::~RealVfsFilesystem() = default; - -std::string RealVfsFilesystem::GetName() const { - return "Real"; -} - -bool RealVfsFilesystem::IsReadable() const { - return true; -} - -bool RealVfsFilesystem::IsWritable() const { - return true; -} - -VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const { - const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - if (!FS::Exists(path)) { - return VfsEntryType::None; - } - if (FS::IsDir(path)) { - return VfsEntryType::Directory; - } - - return VfsEntryType::File; -} - -VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::optional size, - Mode perms) { - const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - std::scoped_lock lk{list_lock}; - - if (auto it = cache.find(path); it != cache.end()) { - if (auto file = it->second.lock(); file) { - return file; - } - } - - if (!size && !FS::IsFile(path)) { - return nullptr; - } - - auto reference = std::make_unique(); - this->InsertReferenceIntoListLocked(*reference); - - auto file = std::shared_ptr( - new RealVfsFile(*this, std::move(reference), path, perms, size)); - cache[path] = file; - - return file; -} - -VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { - return OpenFileFromEntry(path_, {}, perms); -} - -VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { - const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - { - std::scoped_lock lk{list_lock}; - cache.erase(path); - } - - // Current usages of CreateFile expect to delete the contents of an existing file. - if (FS::IsFile(path)) { - FS::IOFile temp{path, FS::FileAccessMode::Write, FS::FileType::BinaryFile}; - - if (!temp.IsOpen()) { - return nullptr; - } - - temp.Close(); - - return OpenFile(path, perms); - } - - if (!FS::NewFile(path)) { - return nullptr; - } - - return OpenFile(path, perms); -} - -VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { - // Unused - return nullptr; -} - -VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { - const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); - const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); - { - std::scoped_lock lk{list_lock}; - cache.erase(old_path); - cache.erase(new_path); - } - if (!FS::RenameFile(old_path, new_path)) { - return nullptr; - } - return OpenFile(new_path, Mode::ReadWrite); -} - -bool RealVfsFilesystem::DeleteFile(std::string_view path_) { - const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - { - std::scoped_lock lk{list_lock}; - cache.erase(path); - } - return FS::RemoveFile(path); -} - -VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { - const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - return std::shared_ptr(new RealVfsDirectory(*this, path, perms)); -} - -VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { - const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - if (!FS::CreateDirs(path)) { - return nullptr; - } - return std::shared_ptr(new RealVfsDirectory(*this, path, perms)); -} - -VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_, - std::string_view new_path_) { - // Unused - return nullptr; -} - -VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, - std::string_view new_path_) { - const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); - const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); - - if (!FS::RenameDir(old_path, new_path)) { - return nullptr; - } - return OpenDirectory(new_path, Mode::ReadWrite); -} - -bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { - const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - return FS::RemoveDirRecursively(path); -} - -std::unique_lock RealVfsFilesystem::RefreshReference(const std::string& path, - Mode perms, - FileReference& reference) { - std::unique_lock lk{list_lock}; - - // Temporarily remove from list. - this->RemoveReferenceFromListLocked(reference); - - // Restore file if needed. - if (!reference.file) { - this->EvictSingleReferenceLocked(); - - reference.file = - FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile); - if (reference.file) { - num_open_files++; - } - } - - // Reinsert into list. - this->InsertReferenceIntoListLocked(reference); - - return lk; -} - -void RealVfsFilesystem::DropReference(std::unique_ptr&& reference) { - std::scoped_lock lk{list_lock}; - - // Remove from list. - this->RemoveReferenceFromListLocked(*reference); - - // Close the file. - if (reference->file) { - reference->file.reset(); - num_open_files--; - } -} - -void RealVfsFilesystem::EvictSingleReferenceLocked() { - if (num_open_files < MaxOpenFiles || open_references.empty()) { - return; - } - - // Get and remove from list. - auto& reference = open_references.back(); - this->RemoveReferenceFromListLocked(reference); - - // Close the file. - if (reference.file) { - reference.file.reset(); - num_open_files--; - } - - // Reinsert into closed list. - this->InsertReferenceIntoListLocked(reference); -} - -void RealVfsFilesystem::InsertReferenceIntoListLocked(FileReference& reference) { - if (reference.file) { - open_references.push_front(reference); - } else { - closed_references.push_front(reference); - } -} - -void RealVfsFilesystem::RemoveReferenceFromListLocked(FileReference& reference) { - if (reference.file) { - open_references.erase(open_references.iterator_to(reference)); - } else { - closed_references.erase(closed_references.iterator_to(reference)); - } -} - -RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr reference_, - const std::string& path_, Mode perms_, std::optional size_) - : base(base_), reference(std::move(reference_)), path(path_), - parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponentsCopy(path_)), - size(size_), perms(perms_) {} - -RealVfsFile::~RealVfsFile() { - base.DropReference(std::move(reference)); -} - -std::string RealVfsFile::GetName() const { - return path_components.empty() ? "" : std::string(path_components.back()); -} - -std::size_t RealVfsFile::GetSize() const { - if (size) { - return *size; - } - auto lk = base.RefreshReference(path, perms, *reference); - return reference->file ? reference->file->GetSize() : 0; -} - -bool RealVfsFile::Resize(std::size_t new_size) { - size.reset(); - auto lk = base.RefreshReference(path, perms, *reference); - return reference->file ? reference->file->SetSize(new_size) : false; -} - -VirtualDir RealVfsFile::GetContainingDirectory() const { - return base.OpenDirectory(parent_path, perms); -} - -bool RealVfsFile::IsWritable() const { - return True(perms & Mode::Write); -} - -bool RealVfsFile::IsReadable() const { - return True(perms & Mode::Read); -} - -std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { - auto lk = base.RefreshReference(path, perms, *reference); - if (!reference->file || !reference->file->Seek(static_cast(offset))) { - return 0; - } - return reference->file->ReadSpan(std::span{data, length}); -} - -std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { - size.reset(); - auto lk = base.RefreshReference(path, perms, *reference); - if (!reference->file || !reference->file->Seek(static_cast(offset))) { - return 0; - } - return reference->file->WriteSpan(std::span{data, length}); -} - -bool RealVfsFile::Rename(std::string_view name) { - return base.MoveFile(path, parent_path + '/' + std::string(name)) != nullptr; -} - -// TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if -// constexpr' because there is a compile error in the branch not used. - -template <> -std::vector RealVfsDirectory::IterateEntries() const { - if (perms == Mode::Append) { - return {}; - } - - std::vector out; - - const FS::DirEntryCallable callback = [this, - &out](const std::filesystem::directory_entry& entry) { - const auto full_path_string = FS::PathToUTF8String(entry.path()); - - out.emplace_back(base.OpenFileFromEntry(full_path_string, entry.file_size(), perms)); - - return true; - }; - - FS::IterateDirEntries(path, callback, FS::DirEntryFilter::File); - - return out; -} - -template <> -std::vector RealVfsDirectory::IterateEntries() const { - if (perms == Mode::Append) { - return {}; - } - - std::vector out; - - const FS::DirEntryCallable callback = [this, - &out](const std::filesystem::directory_entry& entry) { - const auto full_path_string = FS::PathToUTF8String(entry.path()); - - out.emplace_back(base.OpenDirectory(full_path_string, perms)); - - return true; - }; - - FS::IterateDirEntries(path, callback, FS::DirEntryFilter::Directory); - - return out; -} - -RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_) - : base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)), - path_components(FS::SplitPathComponentsCopy(path)), perms(perms_) { - if (!FS::Exists(path) && True(perms & Mode::Write)) { - void(FS::CreateDirs(path)); - } -} - -RealVfsDirectory::~RealVfsDirectory() = default; - -VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) const { - const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); - if (!FS::Exists(full_path) || FS::IsDir(full_path)) { - return nullptr; - } - return base.OpenFile(full_path, perms); -} - -VirtualDir RealVfsDirectory::GetDirectoryRelative(std::string_view relative_path) const { - const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); - if (!FS::Exists(full_path) || !FS::IsDir(full_path)) { - return nullptr; - } - return base.OpenDirectory(full_path, perms); -} - -VirtualFile RealVfsDirectory::GetFile(std::string_view name) const { - return GetFileRelative(name); -} - -VirtualDir RealVfsDirectory::GetSubdirectory(std::string_view name) const { - return GetDirectoryRelative(name); -} - -VirtualFile RealVfsDirectory::CreateFileRelative(std::string_view relative_path) { - const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); - if (!FS::CreateParentDirs(full_path)) { - return nullptr; - } - return base.CreateFile(full_path, perms); -} - -VirtualDir RealVfsDirectory::CreateDirectoryRelative(std::string_view relative_path) { - const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); - return base.CreateDirectory(full_path, perms); -} - -bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { - const auto full_path = FS::SanitizePath(this->path + '/' + std::string(name)); - return base.DeleteDirectory(full_path); -} - -std::vector RealVfsDirectory::GetFiles() const { - return IterateEntries(); -} - -FileTimeStampRaw RealVfsDirectory::GetFileTimeStamp(std::string_view path_) const { - const auto full_path = FS::SanitizePath(path + '/' + std::string(path_)); - const auto fs_path = std::filesystem::path{FS::ToU8String(full_path)}; - struct stat file_status; - -#ifdef _WIN32 - const auto stat_result = _wstat64(fs_path.c_str(), &file_status); -#else - const auto stat_result = stat(fs_path.c_str(), &file_status); -#endif - - if (stat_result != 0) { - return {}; - } - - return { - .created{static_cast(file_status.st_ctime)}, - .accessed{static_cast(file_status.st_atime)}, - .modified{static_cast(file_status.st_mtime)}, - }; -} - -std::vector RealVfsDirectory::GetSubdirectories() const { - return IterateEntries(); -} - -bool RealVfsDirectory::IsWritable() const { - return True(perms & Mode::Write); -} - -bool RealVfsDirectory::IsReadable() const { - return True(perms & Mode::Read); -} - -std::string RealVfsDirectory::GetName() const { - return path_components.empty() ? "" : std::string(path_components.back()); -} - -VirtualDir RealVfsDirectory::GetParentDirectory() const { - if (path_components.size() <= 1) { - return nullptr; - } - - return base.OpenDirectory(parent_path, perms); -} - -VirtualDir RealVfsDirectory::CreateSubdirectory(std::string_view name) { - const std::string subdir_path = (path + '/').append(name); - return base.CreateDirectory(subdir_path, perms); -} - -VirtualFile RealVfsDirectory::CreateFile(std::string_view name) { - const std::string file_path = (path + '/').append(name); - return base.CreateFile(file_path, perms); -} - -bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) { - const std::string subdir_path = (path + '/').append(name); - return base.DeleteDirectory(subdir_path); -} - -bool RealVfsDirectory::DeleteFile(std::string_view name) { - const std::string file_path = (path + '/').append(name); - return base.DeleteFile(file_path); -} - -bool RealVfsDirectory::Rename(std::string_view name) { - const std::string new_name = (parent_path + '/').append(name); - return base.MoveFile(path, new_name) != nullptr; -} - -std::string RealVfsDirectory::GetFullPath() const { - auto out = path; - std::replace(out.begin(), out.end(), '\\', '/'); - return out; -} - -std::map> RealVfsDirectory::GetEntries() const { - if (perms == Mode::Append) { - return {}; - } - - std::map> out; - - const FS::DirEntryCallable callback = [&out](const std::filesystem::directory_entry& entry) { - const auto filename = FS::PathToUTF8String(entry.path().filename()); - out.insert_or_assign(filename, - entry.is_directory() ? VfsEntryType::Directory : VfsEntryType::File); - return true; - }; - - FS::IterateDirEntries(path, callback); - - return out; -} - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h deleted file mode 100644 index 26ea7df62..000000000 --- a/src/core/file_sys/vfs_real.h +++ /dev/null @@ -1,145 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include -#include "common/intrusive_list.h" -#include "core/file_sys/mode.h" -#include "core/file_sys/vfs.h" - -namespace Common::FS { -class IOFile; -} - -namespace FileSys { - -struct FileReference : public Common::IntrusiveListBaseNode { - std::shared_ptr file{}; -}; - -class RealVfsFile; -class RealVfsDirectory; - -class RealVfsFilesystem : public VfsFilesystem { -public: - RealVfsFilesystem(); - ~RealVfsFilesystem() override; - - std::string GetName() const override; - bool IsReadable() const override; - bool IsWritable() const override; - VfsEntryType GetEntryType(std::string_view path) const override; - VirtualFile OpenFile(std::string_view path, Mode perms = Mode::Read) override; - VirtualFile CreateFile(std::string_view path, Mode perms = Mode::ReadWrite) override; - VirtualFile CopyFile(std::string_view old_path, std::string_view new_path) override; - VirtualFile MoveFile(std::string_view old_path, std::string_view new_path) override; - bool DeleteFile(std::string_view path) override; - VirtualDir OpenDirectory(std::string_view path, Mode perms = Mode::Read) override; - VirtualDir CreateDirectory(std::string_view path, Mode perms = Mode::ReadWrite) override; - VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path) override; - VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path) override; - bool DeleteDirectory(std::string_view path) override; - -private: - using ReferenceListType = Common::IntrusiveListBaseTraits::ListType; - std::map, std::less<>> cache; - ReferenceListType open_references; - ReferenceListType closed_references; - std::mutex list_lock; - size_t num_open_files{}; - -private: - friend class RealVfsFile; - std::unique_lock RefreshReference(const std::string& path, Mode perms, - FileReference& reference); - void DropReference(std::unique_ptr&& reference); - -private: - friend class RealVfsDirectory; - VirtualFile OpenFileFromEntry(std::string_view path, std::optional size, - Mode perms = Mode::Read); - -private: - void EvictSingleReferenceLocked(); - void InsertReferenceIntoListLocked(FileReference& reference); - void RemoveReferenceFromListLocked(FileReference& reference); -}; - -// An implementation of VfsFile that represents a file on the user's computer. -class RealVfsFile : public VfsFile { - friend class RealVfsDirectory; - friend class RealVfsFilesystem; - -public: - ~RealVfsFile() override; - - std::string GetName() const override; - std::size_t GetSize() const override; - bool Resize(std::size_t new_size) override; - VirtualDir GetContainingDirectory() const override; - bool IsWritable() const override; - bool IsReadable() const override; - std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; - std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; - bool Rename(std::string_view name) override; - -private: - RealVfsFile(RealVfsFilesystem& base, std::unique_ptr reference, - const std::string& path, Mode perms = Mode::Read, std::optional size = {}); - - RealVfsFilesystem& base; - std::unique_ptr reference; - std::string path; - std::string parent_path; - std::vector path_components; - std::optional size; - Mode perms; -}; - -// An implementation of VfsDirectory that represents a directory on the user's computer. -class RealVfsDirectory : public VfsDirectory { - friend class RealVfsFilesystem; - -public: - ~RealVfsDirectory() override; - - VirtualFile GetFileRelative(std::string_view relative_path) const override; - VirtualDir GetDirectoryRelative(std::string_view relative_path) const override; - VirtualFile GetFile(std::string_view name) const override; - VirtualDir GetSubdirectory(std::string_view name) const override; - VirtualFile CreateFileRelative(std::string_view relative_path) override; - VirtualDir CreateDirectoryRelative(std::string_view relative_path) override; - bool DeleteSubdirectoryRecursive(std::string_view name) override; - std::vector GetFiles() const override; - FileTimeStampRaw GetFileTimeStamp(std::string_view path) const override; - std::vector GetSubdirectories() const override; - bool IsWritable() const override; - bool IsReadable() const override; - std::string GetName() const override; - VirtualDir GetParentDirectory() const override; - VirtualDir CreateSubdirectory(std::string_view name) override; - VirtualFile CreateFile(std::string_view name) override; - bool DeleteSubdirectory(std::string_view name) override; - bool DeleteFile(std::string_view name) override; - bool Rename(std::string_view name) override; - std::string GetFullPath() const override; - std::map> GetEntries() const override; - -private: - RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read); - - template - std::vector> IterateEntries() const; - - RealVfsFilesystem& base; - std::string path; - std::string parent_path; - std::vector path_components; - Mode perms; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs_static.h deleted file mode 100644 index ca3f989ef..000000000 --- a/src/core/file_sys/vfs_static.h +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include - -#include "core/file_sys/vfs.h" - -namespace FileSys { - -class StaticVfsFile : public VfsFile { -public: - explicit StaticVfsFile(u8 value_, std::size_t size_ = 0, std::string name_ = "", - VirtualDir parent_ = nullptr) - : value{value_}, size{size_}, name{std::move(name_)}, parent{std::move(parent_)} {} - - std::string GetName() const override { - return name; - } - - std::size_t GetSize() const override { - return size; - } - - bool Resize(std::size_t new_size) override { - size = new_size; - return true; - } - - VirtualDir GetContainingDirectory() const override { - return parent; - } - - bool IsWritable() const override { - return false; - } - - bool IsReadable() const override { - return true; - } - - std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override { - const auto read = std::min(length, size - offset); - std::fill(data, data + read, value); - return read; - } - - std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override { - return 0; - } - - std::optional ReadByte(std::size_t offset) const override { - if (offset >= size) { - return std::nullopt; - } - - return value; - } - - std::vector ReadBytes(std::size_t length, std::size_t offset) const override { - const auto read = std::min(length, size - offset); - return std::vector(read, value); - } - - bool Rename(std::string_view new_name) override { - name = new_name; - return true; - } - -private: - u8 value; - std::size_t size; - std::string name; - VirtualDir parent; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_types.h b/src/core/file_sys/vfs_types.h deleted file mode 100644 index 4a583ed64..000000000 --- a/src/core/file_sys/vfs_types.h +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include - -#include "common/common_types.h" - -namespace FileSys { - -class VfsDirectory; -class VfsFile; -class VfsFilesystem; - -// Declarations for Vfs* pointer types - -using VirtualDir = std::shared_ptr; -using VirtualFile = std::shared_ptr; -using VirtualFilesystem = std::shared_ptr; - -struct FileTimeStampRaw { - u64 created{}; - u64 accessed{}; - u64 modified{}; - u64 padding{}; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp deleted file mode 100644 index 251d9d7c9..000000000 --- a/src/core/file_sys/vfs_vector.cpp +++ /dev/null @@ -1,133 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include "core/file_sys/vfs_vector.h" - -namespace FileSys { -VectorVfsFile::VectorVfsFile(std::vector initial_data, std::string name_, VirtualDir parent_) - : data(std::move(initial_data)), parent(std::move(parent_)), name(std::move(name_)) {} - -VectorVfsFile::~VectorVfsFile() = default; - -std::string VectorVfsFile::GetName() const { - return name; -} - -size_t VectorVfsFile::GetSize() const { - return data.size(); -} - -bool VectorVfsFile::Resize(size_t new_size) { - data.resize(new_size); - return true; -} - -VirtualDir VectorVfsFile::GetContainingDirectory() const { - return parent; -} - -bool VectorVfsFile::IsWritable() const { - return true; -} - -bool VectorVfsFile::IsReadable() const { - return true; -} - -std::size_t VectorVfsFile::Read(u8* data_, std::size_t length, std::size_t offset) const { - const auto read = std::min(length, data.size() - offset); - std::memcpy(data_, data.data() + offset, read); - return read; -} - -std::size_t VectorVfsFile::Write(const u8* data_, std::size_t length, std::size_t offset) { - if (offset + length > data.size()) - data.resize(offset + length); - const auto write = std::min(length, data.size() - offset); - std::memcpy(data.data() + offset, data_, write); - return write; -} - -bool VectorVfsFile::Rename(std::string_view name_) { - name = name_; - return true; -} - -void VectorVfsFile::Assign(std::vector new_data) { - data = std::move(new_data); -} - -VectorVfsDirectory::VectorVfsDirectory(std::vector files_, - std::vector dirs_, std::string name_, - VirtualDir parent_) - : files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)), - name(std::move(name_)) {} - -VectorVfsDirectory::~VectorVfsDirectory() = default; - -std::vector VectorVfsDirectory::GetFiles() const { - return files; -} - -std::vector VectorVfsDirectory::GetSubdirectories() const { - return dirs; -} - -bool VectorVfsDirectory::IsWritable() const { - return false; -} - -bool VectorVfsDirectory::IsReadable() const { - return true; -} - -std::string VectorVfsDirectory::GetName() const { - return name; -} - -VirtualDir VectorVfsDirectory::GetParentDirectory() const { - return parent; -} - -template -static bool FindAndRemoveVectorElement(std::vector& vec, std::string_view name) { - const auto iter = - std::find_if(vec.begin(), vec.end(), [name](const T& e) { return e->GetName() == name; }); - if (iter == vec.end()) - return false; - - vec.erase(iter); - return true; -} - -bool VectorVfsDirectory::DeleteSubdirectory(std::string_view subdir_name) { - return FindAndRemoveVectorElement(dirs, subdir_name); -} - -bool VectorVfsDirectory::DeleteFile(std::string_view file_name) { - return FindAndRemoveVectorElement(files, file_name); -} - -bool VectorVfsDirectory::Rename(std::string_view name_) { - name = name_; - return true; -} - -VirtualDir VectorVfsDirectory::CreateSubdirectory(std::string_view subdir_name) { - return nullptr; -} - -VirtualFile VectorVfsDirectory::CreateFile(std::string_view file_name) { - return nullptr; -} - -void VectorVfsDirectory::AddFile(VirtualFile file) { - files.push_back(std::move(file)); -} - -void VectorVfsDirectory::AddDirectory(VirtualDir dir) { - dirs.push_back(std::move(dir)); -} -} // namespace FileSys diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h deleted file mode 100644 index bfedb6e42..000000000 --- a/src/core/file_sys/vfs_vector.h +++ /dev/null @@ -1,131 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include -#include -#include "core/file_sys/vfs.h" - -namespace FileSys { - -// An implementation of VfsFile that is backed by a statically-sized array -template -class ArrayVfsFile : public VfsFile { -public: - explicit ArrayVfsFile(const std::array& data_, std::string name_ = "", - VirtualDir parent_ = nullptr) - : data(data_), name(std::move(name_)), parent(std::move(parent_)) {} - - std::string GetName() const override { - return name; - } - - std::size_t GetSize() const override { - return size; - } - - bool Resize(std::size_t new_size) override { - return false; - } - - VirtualDir GetContainingDirectory() const override { - return parent; - } - - bool IsWritable() const override { - return false; - } - - bool IsReadable() const override { - return true; - } - - std::size_t Read(u8* data_, std::size_t length, std::size_t offset) const override { - const auto read = std::min(length, size - offset); - std::memcpy(data_, data.data() + offset, read); - return read; - } - - std::size_t Write(const u8* data_, std::size_t length, std::size_t offset) override { - return 0; - } - - bool Rename(std::string_view new_name) override { - name = new_name; - return true; - } - -private: - std::array data; - std::string name; - VirtualDir parent; -}; - -template -std::shared_ptr> MakeArrayFile(const std::array& data, - Args&&... args) { - return std::make_shared>(data, std::forward(args)...); -} - -// An implementation of VfsFile that is backed by a vector optionally supplied upon construction -class VectorVfsFile : public VfsFile { -public: - explicit VectorVfsFile(std::vector initial_data = {}, std::string name_ = "", - VirtualDir parent_ = nullptr); - ~VectorVfsFile() override; - - std::string GetName() const override; - std::size_t GetSize() const override; - bool Resize(std::size_t new_size) override; - VirtualDir GetContainingDirectory() const override; - bool IsWritable() const override; - bool IsReadable() const override; - std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; - std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; - bool Rename(std::string_view name) override; - - virtual void Assign(std::vector new_data); - -private: - std::vector data; - VirtualDir parent; - std::string name; -}; - -// An implementation of VfsDirectory that maintains two vectors for subdirectories and files. -// Vector data is supplied upon construction. -class VectorVfsDirectory : public VfsDirectory { -public: - explicit VectorVfsDirectory(std::vector files = {}, - std::vector dirs = {}, std::string name = "", - VirtualDir parent = nullptr); - ~VectorVfsDirectory() override; - - std::vector GetFiles() const override; - std::vector GetSubdirectories() const override; - bool IsWritable() const override; - bool IsReadable() const override; - std::string GetName() const override; - VirtualDir GetParentDirectory() const override; - bool DeleteSubdirectory(std::string_view subdir_name) override; - bool DeleteFile(std::string_view file_name) override; - bool Rename(std::string_view name) override; - VirtualDir CreateSubdirectory(std::string_view subdir_name) override; - VirtualFile CreateFile(std::string_view file_name) override; - - virtual void AddFile(VirtualFile file); - virtual void AddDirectory(VirtualDir dir); - -private: - std::vector files; - std::vector dirs; - - VirtualDir parent; - std::string name; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp index ede0aa11a..6692211e1 100644 --- a/src/core/file_sys/xts_archive.cpp +++ b/src/core/file_sys/xts_archive.cpp @@ -17,7 +17,7 @@ #include "core/crypto/key_manager.h" #include "core/crypto/xts_encryption_layer.h" #include "core/file_sys/content_archive.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs_offset.h" #include "core/file_sys/xts_archive.h" #include "core/loader/loader.h" diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h index abbe5f716..7589b7c38 100644 --- a/src/core/file_sys/xts_archive.h +++ b/src/core/file_sys/xts_archive.h @@ -8,7 +8,7 @@ #include "common/common_types.h" #include "common/swap.h" #include "core/crypto/key_manager.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace Loader { enum class ResultStatus : u16; -- cgit v1.2.3 From cc09c265e15e9598844482a8b5a22b12650b3f1b Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Thu, 18 Jan 2024 21:31:41 +0100 Subject: fs: Replace Mode enum by OpenMode enum --- src/core/file_sys/bis_factory.cpp | 3 +-- src/core/file_sys/directory.h | 39 ----------------------------- src/core/file_sys/fs_directory.h | 33 ++++++++++++++++++++++++ src/core/file_sys/fs_filesystem.h | 39 +++++++++++++++++++++++++++++ src/core/file_sys/mode.h | 23 ----------------- src/core/file_sys/vfs/vfs.cpp | 27 ++++++++++---------- src/core/file_sys/vfs/vfs.h | 11 ++++---- src/core/file_sys/vfs/vfs_real.cpp | 51 +++++++++++++++++++------------------- src/core/file_sys/vfs/vfs_real.h | 25 +++++++++++-------- 9 files changed, 130 insertions(+), 121 deletions(-) delete mode 100644 src/core/file_sys/directory.h create mode 100644 src/core/file_sys/fs_directory.h create mode 100644 src/core/file_sys/fs_filesystem.h delete mode 100644 src/core/file_sys/mode.h (limited to 'src/core/file_sys') diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index f275f5fe4..db667438e 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp @@ -4,7 +4,6 @@ #include #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/vfs.h" @@ -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/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 -#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/fs_directory.h b/src/core/file_sys/fs_directory.h new file mode 100644 index 000000000..cba6155f8 --- /dev/null +++ b/src/core/file_sys/fs_directory.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2023 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(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_filesystem.h b/src/core/file_sys/fs_filesystem.h new file mode 100644 index 000000000..436d6c731 --- /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 + +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), + + /* TODO: Separate enum, like N? */ + _NotRequireFileSize = (1 << 31), +}; +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/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/vfs/vfs.cpp b/src/core/file_sys/vfs/vfs.cpp index b88a5f91d..a04292760 100644 --- a/src/core/file_sys/vfs/vfs.cpp +++ b/src/core/file_sys/vfs/vfs.cpp @@ -5,7 +5,6 @@ #include #include #include "common/fs/path_util.h" -#include "core/file_sys/mode.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/vfs.h b/src/core/file_sys/vfs/vfs.h index 6830244e3..f846a9669 100644 --- a/src/core/file_sys/vfs/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/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/vfs_real.cpp b/src/core/file_sys/vfs/vfs_real.cpp index 1e6d8163b..627d5d251 100644 --- a/src/core/file_sys/vfs/vfs_real.cpp +++ b/src/core/file_sys/vfs/vfs_real.cpp @@ -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 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(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 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 reference_, - const std::string& path_, Mode perms_, std::optional size_) + const std::string& path_, OpenMode perms_, std::optional 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 RealVfsDirectory::IterateEntries() const { - if (perms == Mode::Append) { + if (perms == OpenMode::AllowAppend) { return {}; } @@ -353,7 +351,7 @@ std::vector RealVfsDirectory::IterateEntries( template <> std::vector RealVfsDirectory::IterateEntries() const { - if (perms == Mode::Append) { + if (perms == OpenMode::AllowAppend) { return {}; } @@ -373,10 +371,11 @@ std::vector RealVfsDirectory::IterateEntries 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> RealVfsDirectory::GetEntries() const { - if (perms == Mode::Append) { + if (perms == OpenMode::AllowAppend) { return {}; } diff --git a/src/core/file_sys/vfs/vfs_real.h b/src/core/file_sys/vfs/vfs_real.h index 1560bc1f9..5c2172cce 100644 --- a/src/core/file_sys/vfs/vfs_real.h +++ b/src/core/file_sys/vfs/vfs_real.h @@ -8,7 +8,7 @@ #include #include #include "common/intrusive_list.h" -#include "core/file_sys/mode.h" +#include "core/file_sys/fs_filesystem.h" #include "core/file_sys/vfs/vfs.h" namespace Common::FS { @@ -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 RefreshReference(const std::string& path, Mode perms, + std::unique_lock RefreshReference(const std::string& path, OpenMode perms, FileReference& reference); void DropReference(std::unique_ptr&& reference); private: friend class RealVfsDirectory; VirtualFile OpenFileFromEntry(std::string_view path, std::optional size, - Mode perms = Mode::Read); + OpenMode perms = OpenMode::Read); private: void EvictSingleReferenceLocked(); @@ -89,7 +90,8 @@ public: private: RealVfsFile(RealVfsFilesystem& base, std::unique_ptr reference, - const std::string& path, Mode perms = Mode::Read, std::optional size = {}); + const std::string& path, OpenMode perms = OpenMode::Read, + std::optional size = {}); RealVfsFilesystem& base; std::unique_ptr reference; @@ -97,7 +99,7 @@ private: std::string parent_path; std::vector path_components; std::optional 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> 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 std::vector> IterateEntries() const; @@ -139,7 +142,7 @@ private: std::string path; std::string parent_path; std::vector path_components; - Mode perms; + OpenMode perms; }; } // namespace FileSys -- cgit v1.2.3 From c60ab6bbf61021e247c2574a771d3d88a77ed398 Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Thu, 18 Jan 2024 21:55:15 +0100 Subject: fs/errors: Unify naming of result codes --- src/core/file_sys/errors.h | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'src/core/file_sys') diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h index 2f5045a67..7134445cf 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}; -- cgit v1.2.3 From 2c049ae06dbbdfff7542c23ca4d748879f4beb71 Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Thu, 18 Jan 2024 23:08:37 +0100 Subject: fs: Add path class --- src/core/file_sys/errors.h | 11 + src/core/file_sys/fs_file.h | 63 ++ src/core/file_sys/fs_memory_management.h | 48 ++ src/core/file_sys/fs_operate_range.h | 20 + src/core/file_sys/fs_path.h | 570 ++++++++++++++ src/core/file_sys/fs_path_utility.h | 1240 ++++++++++++++++++++++++++++++ src/core/file_sys/fs_string_util.h | 241 ++++++ 7 files changed, 2193 insertions(+) create mode 100644 src/core/file_sys/fs_file.h create mode 100644 src/core/file_sys/fs_memory_management.h create mode 100644 src/core/file_sys/fs_operate_range.h create mode 100644 src/core/file_sys/fs_path.h create mode 100644 src/core/file_sys/fs_path_utility.h create mode 100644 src/core/file_sys/fs_string_util.h (limited to 'src/core/file_sys') diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h index 7134445cf..d4e0eb6f4 100644 --- a/src/core/file_sys/errors.h +++ b/src/core/file_sys/errors.h @@ -73,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_file.h b/src/core/file_sys/fs_file.h new file mode 100644 index 000000000..35f66b959 --- /dev/null +++ b/src/core/file_sys/fs_file.h @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +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_memory_management.h b/src/core/file_sys/fs_memory_management.h new file mode 100644 index 000000000..596143ba9 --- /dev/null +++ b/src/core/file_sys/fs_memory_management.h @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/alignment.h" + +namespace FileSys { + +std::mutex g_mutex; + +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(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) { + /* Lock the allocator. */ + std::scoped_lock lk(g_mutex); + + return AllocateUnsafe(size); +} + +void Deallocate(void* ptr, size_t size) { + /* If the pointer is non-null, deallocate it. */ + if (ptr != nullptr) { + /* Lock the allocator. */ + std::scoped_lock lk(g_mutex); + + 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..750999907 --- /dev/null +++ b/src/core/file_sys/fs_operate_range.h @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +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..9ea452644 --- /dev/null +++ b/src/core/file_sys/fs_path.h @@ -0,0 +1,570 @@ +// SPDX-FileCopyrightText: Copyright 2023 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(m_length_and_is_normalized & 1); + } + + constexpr void SetNormalized() { + m_length_and_is_normalized |= static_cast(1); + } + + constexpr void SetNotNormalized() { + m_length_and_is_normalized &= ~static_cast(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(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() { /* ... */ + } + + 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(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 hvea 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(dst, old_write_buffer.Get(), cur_len + 1); + } + + /* Add separator. */ + dst[cur_len] = '/'; + + /* Copy the child path. */ + const size_t copied = Strlcpy(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(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(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..276445707 --- /dev/null +++ b/src/core/file_sys/fs_path_utility.h @@ -0,0 +1,1240 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/assert.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; +using Result = ::Result; + +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 +consteval u64 MakeInvalidCharacterMask(size_t n) { + u64 mask = 0; + for (size_t i = 0; i < NumInvalidCharacters; ++i) { + if ((static_cast(InvalidCharacterSet[i]) >> 6) == n) { + mask |= static_cast(1) << (static_cast(InvalidCharacterSet[i]) & 0x3F); + } + } + return mask; +} + +template +constexpr bool IsInvalidCharacterImpl(char c) { + constexpr u64 Masks[4] = { + MakeInvalidCharacterMask(0), + MakeInvalidCharacterMask(1), + MakeInvalidCharacterMask(2), + MakeInvalidCharacterMask(3)}; + + return (Masks[static_cast(c) >> 6] & + (static_cast(1) << (static_cast(c) & 0x3F))) != 0; +} + +} // namespace impl + +constexpr bool IsInvalidCharacter(char c) { + return impl::IsInvalidCharacterImpl(c); +} +constexpr bool IsInvalidCharacterForHostName(char c) { + return impl::IsInvalidCharacterImpl(c); +} +constexpr bool IsInvalidCharacterForMountName(char c) { + return impl::IsInvalidCharacterImpl(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 + requires(std::same_as || std::same_as) +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 + requires(std::same_as || std::same_as) +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(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(Allocate(replaced_src_len)); + } + + Strlcpy(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..68114e72c --- /dev/null +++ b/src/core/file_sys/fs_string_util.h @@ -0,0 +1,241 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/assert.h" + +namespace FileSys { + +template +constexpr int Strlen(const T* str) { + ASSERT(str != nullptr); + + int length = 0; + while (*str++) { + ++length; + } + + return length; +} + +template +constexpr int Strnlen(const T* str, int count) { + ASSERT(str != nullptr); + ASSERT(count >= 0); + + int length = 0; + while (count-- && *str++) { + ++length; + } + + return length; +} + +template +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 +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(cur - src); +} + +/* std::size() does not support zero-size C arrays. We're fixing that. */ +template +constexpr auto size(const C& c) -> decltype(c.size()) { + return std::size(c); +} + +template +constexpr std::size_t size(const C& c) { + if constexpr (sizeof(C) == 0) { + return 0; + } else { + return std::size(c); + } +} + +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(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(p[0]))) { + case 1: + *dst = static_cast(p[0]); + return CharacterEncodingResult_Success; + case 2: + if ((static_cast(p[0]) & 0x1E) != 0) { + if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast(p[1])) == + 0) { + *dst = (static_cast(p[0] & 0x1F) << 6) | (static_cast(p[1] & 0x3F) << 0); + return CharacterEncodingResult_Success; + } + } + break; + case 3: + if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast(p[1])) == 0 && + impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast(p[2])) == 0) { + const u32 c = (static_cast(p[0] & 0xF) << 12) | + (static_cast(p[1] & 0x3F) << 6) | + (static_cast(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(p[1])) == 0 && + impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast(p[2])) == 0 && + impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast(p[3])) == 0) { + const u32 c = + (static_cast(p[0] & 0x7) << 18) | (static_cast(p[1] & 0x3F) << 12) | + (static_cast(p[2] & 0x3F) << 6) | (static_cast(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(*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(p[1])) == + 0) { + c = (static_cast(p[0] & 0x1F) << 6) | (static_cast(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(p[1])) == 0 && + impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast(p[2])) == 0) { + c = (static_cast(p[0] & 0xF) << 12) | (static_cast(p[1] & 0x3F) << 6) | + (static_cast(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(p[1])) == 0 && + impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast(p[2])) == 0 && + impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast(p[3])) == 0) { + c = (static_cast(p[0] & 0x7) << 18) | (static_cast(p[1] & 0x3F) << 12) | + (static_cast(p[2] & 0x3F) << 6) | (static_cast(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 -- cgit v1.2.3 From 975deb7528cd98460528553f6a9162bfbcd6cab1 Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Fri, 19 Jan 2024 00:17:28 +0100 Subject: Address review comments and fix compilation problems --- src/core/file_sys/fs_directory.h | 2 +- src/core/file_sys/fs_file.h | 14 +- src/core/file_sys/fs_filesystem.h | 6 +- src/core/file_sys/fs_memory_management.h | 20 +-- src/core/file_sys/fs_operate_range.h | 4 +- src/core/file_sys/fs_path.h | 150 +++++++++---------- src/core/file_sys/fs_path_utility.h | 249 +++++++++++++++---------------- src/core/file_sys/fs_string_util.h | 29 +--- 8 files changed, 225 insertions(+), 249 deletions(-) (limited to 'src/core/file_sys') diff --git a/src/core/file_sys/fs_directory.h b/src/core/file_sys/fs_directory.h index cba6155f8..25c9cb18a 100644 --- a/src/core/file_sys/fs_directory.h +++ b/src/core/file_sys/fs_directory.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/core/file_sys/fs_file.h b/src/core/file_sys/fs_file.h index 35f66b959..4fb77e8db 100644 --- a/src/core/file_sys/fs_file.h +++ b/src/core/file_sys/fs_file.h @@ -1,12 +1,14 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// 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; + u32 value; static const ReadOption None; }; @@ -18,7 +20,7 @@ enum ReadOptionFlag : u32 { inline constexpr const ReadOption ReadOption::None = {ReadOptionFlag_None}; inline constexpr bool operator==(const ReadOption& lhs, const ReadOption& rhs) { - return lhs._value == rhs._value; + return lhs.value == rhs.value; } inline constexpr bool operator!=(const ReadOption& lhs, const ReadOption& rhs) { @@ -33,10 +35,10 @@ enum WriteOptionFlag : u32 { }; struct WriteOption { - u32 _value; + u32 value; constexpr inline bool HasFlushFlag() const { - return _value & WriteOptionFlag_Flush; + return value & WriteOptionFlag_Flush; } static const WriteOption None; @@ -47,7 +49,7 @@ 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; + return lhs.value == rhs.value; } inline constexpr bool operator!=(const WriteOption& lhs, const WriteOption& rhs) { diff --git a/src/core/file_sys/fs_filesystem.h b/src/core/file_sys/fs_filesystem.h index 436d6c731..7f237b7fa 100644 --- a/src/core/file_sys/fs_filesystem.h +++ b/src/core/file_sys/fs_filesystem.h @@ -3,6 +3,9 @@ #pragma once +#include "common/common_funcs.h" +#include "common/common_types.h" + namespace FileSys { enum class OpenMode : u32 { @@ -20,9 +23,6 @@ enum class OpenDirectoryMode : u64 { File = (1 << 1), All = (Directory | File), - - /* TODO: Separate enum, like N? */ - _NotRequireFileSize = (1 << 31), }; DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode) diff --git a/src/core/file_sys/fs_memory_management.h b/src/core/file_sys/fs_memory_management.h index 596143ba9..f03c6354b 100644 --- a/src/core/file_sys/fs_memory_management.h +++ b/src/core/file_sys/fs_memory_management.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,39 +8,31 @@ namespace FileSys { -std::mutex g_mutex; - constexpr size_t RequiredAlignment = alignof(u64); void* AllocateUnsafe(size_t size) { - /* Allocate. */ + // Allocate void* const ptr = ::operator new(size, std::align_val_t{RequiredAlignment}); - /* Check alignment. */ + // Check alignment ASSERT(Common::IsAligned(reinterpret_cast(ptr), RequiredAlignment)); - /* Return allocated pointer. */ + // Return allocated pointer return ptr; } void DeallocateUnsafe(void* ptr, size_t size) { - /* Deallocate the pointer. */ + // Deallocate the pointer ::operator delete(ptr, std::align_val_t{RequiredAlignment}); } void* Allocate(size_t size) { - /* Lock the allocator. */ - std::scoped_lock lk(g_mutex); - return AllocateUnsafe(size); } void Deallocate(void* ptr, size_t size) { - /* If the pointer is non-null, deallocate it. */ + // If the pointer is non-null, deallocate it if (ptr != nullptr) { - /* Lock the allocator. */ - std::scoped_lock lk(g_mutex); - DeallocateUnsafe(ptr, size); } } diff --git a/src/core/file_sys/fs_operate_range.h b/src/core/file_sys/fs_operate_range.h index 750999907..04ea64cc0 100644 --- a/src/core/file_sys/fs_operate_range.h +++ b/src/core/file_sys/fs_operate_range.h @@ -1,8 +1,10 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// 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 { diff --git a/src/core/file_sys/fs_path.h b/src/core/file_sys/fs_path.h index 9ea452644..56ba08a6a 100644 --- a/src/core/file_sys/fs_path.h +++ b/src/core/file_sys/fs_path.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -34,8 +34,7 @@ public: size_t m_length_and_is_normalized; public: - constexpr WriteBuffer() : m_buffer(nullptr), m_length_and_is_normalized(0) { /* ... */ - } + constexpr WriteBuffer() : m_buffer(nullptr), m_length_and_is_normalized(0) {} constexpr ~WriteBuffer() { if (m_buffer != nullptr) { @@ -113,35 +112,32 @@ private: WriteBuffer m_write_buffer; public: - constexpr Path() : m_str(EmptyPath), m_write_buffer() { - /* ... */ - } + 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() { /* ... */ - } + constexpr ~Path() = default; constexpr Result SetShallowBuffer(const char* buffer) { - /* Check pre-conditions. */ + // Check pre-conditions ASSERT(m_write_buffer.GetLength() == 0); - /* Check the buffer is valid. */ + // Check the buffer is valid R_UNLESS(buffer != nullptr, ResultNullptrArgument); - /* Set buffer. */ + // Set buffer this->SetReadOnlyBuffer(buffer); - /* Note that we're normalized. */ + // Note that we're normalized this->SetNormalized(); R_SUCCEED(); } constexpr const char* GetString() const { - /* Check pre-conditions. */ + // Check pre-conditions ASSERT(this->IsNormalized()); return m_str; @@ -164,69 +160,69 @@ public: } Result Initialize(const Path& rhs) { - /* Check the other path is normalized. */ + // Check the other path is normalized const bool normalized = rhs.IsNormalized(); R_UNLESS(normalized, ResultNotNormalized); - /* Allocate buffer for our path. */ + // Allocate buffer for our path const auto len = rhs.GetLength(); R_TRY(this->Preallocate(len + 1)); - /* Copy the path. */ + // Copy the path const size_t copied = Strlcpy(m_write_buffer.Get(), rhs.GetString(), len + 1); R_UNLESS(copied == len, ResultUnexpectedInPathA); - /* Set normalized. */ + // Set normalized this->SetNormalized(); R_SUCCEED(); } Result Initialize(const char* path, size_t len) { - /* Check the path is valid. */ + // Check the path is valid R_UNLESS(path != nullptr, ResultNullptrArgument); - /* Initialize. */ + // Initialize R_TRY(this->InitializeImpl(path, len)); - /* Set not normalized. */ + // Set not normalized this->SetNotNormalized(); R_SUCCEED(); } Result Initialize(const char* path) { - /* Check the path is valid. */ + // 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. */ + // Check the path is valid R_UNLESS(path != nullptr, ResultNullptrArgument); - /* Initialize. */ + // Initialize R_TRY(this->InitializeImpl(path, std::strlen(path))); - /* Replace slashes as desired. */ + // 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. */ + // Set not normalized this->SetNotNormalized(); R_SUCCEED(); } Result InitializeWithReplaceForwardSlashes(const char* path) { - /* Check the path is valid. */ + // Check the path is valid R_UNLESS(path != nullptr, ResultNullptrArgument); - /* Initialize. */ + // Initialize R_TRY(this->InitializeImpl(path, std::strlen(path))); - /* Replace slashes as desired. */ + // Replace slashes as desired if (m_write_buffer.GetLength() > 1) { if (auto* p = m_write_buffer.Get(); p[0] == '/' && p[1] == '/') { p[0] = '\\'; @@ -234,23 +230,23 @@ public: } } - /* Set not normalized. */ + // Set not normalized this->SetNotNormalized(); R_SUCCEED(); } Result InitializeWithNormalization(const char* path, size_t size) { - /* Check the path is valid. */ + // Check the path is valid R_UNLESS(path != nullptr, ResultNullptrArgument); - /* Initialize. */ + // Initialize R_TRY(this->InitializeImpl(path, size)); - /* Set not normalized. */ + // Set not normalized this->SetNotNormalized(); - /* Perform normalization. */ + // Perform normalization PathFlags path_flags; if (IsPathRelative(m_str)) { path_flags.AllowRelativePath(); @@ -269,7 +265,7 @@ public: R_SUCCEED(); } - /* Normalize. */ + // Normalize R_TRY(this->Normalize(path_flags)); this->SetNormalized(); @@ -277,30 +273,30 @@ public: } Result InitializeWithNormalization(const char* path) { - /* Check the path is valid. */ + // Check the path is valid R_UNLESS(path != nullptr, ResultNullptrArgument); R_RETURN(this->InitializeWithNormalization(path, std::strlen(path))); } Result InitializeAsEmpty() { - /* Clear our buffer. */ + // Clear our buffer this->ClearBuffer(); - /* Set normalized. */ + // Set normalized this->SetNormalized(); R_SUCCEED(); } Result AppendChild(const char* child) { - /* Check the path is valid. */ + // Check the path is valid R_UNLESS(child != nullptr, ResultNullptrArgument); - /* Basic checks. If we hvea a path and the child is empty, we have nothing to do. */ + // 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. */ + // Skip an early separator if (*c == '/') { ++c; } @@ -308,40 +304,40 @@ public: R_SUCCEED_IF(*c == '\x00'); } - /* If we don't have a string, we can just initialize. */ + // 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. */ + // Remove a trailing separator if (m_str[cur_len - 1] == '/' || m_str[cur_len - 1] == '\\') { --cur_len; } - /* Get the child path's length. */ + // Get the child path's length auto child_len = std::strlen(c); - /* Reset our write buffer. */ + // 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. */ + // Pre-allocate the new buffer R_TRY(this->Preallocate(cur_len + 1 + child_len + 1)); - /* Get our write buffer. */ + // Get our write buffer auto* dst = m_write_buffer.Get(); if (old_write_buffer.Get() != nullptr && cur_len > 0) { Strlcpy(dst, old_write_buffer.Get(), cur_len + 1); } - /* Add separator. */ + // Add separator dst[cur_len] = '/'; - /* Copy the child path. */ + // Copy the child path const size_t copied = Strlcpy(dst + cur_len + 1, c, child_len + 1); R_UNLESS(copied == child_len, ResultUnexpectedInPathA); @@ -353,21 +349,21 @@ public: } Result Combine(const Path& parent, const Path& child) { - /* Get the lengths. */ + // Get the lengths const auto p_len = parent.GetLength(); const auto c_len = child.GetLength(); - /* Allocate our buffer. */ + // Allocate our buffer R_TRY(this->Preallocate(p_len + c_len + 1)); - /* Initialize as parent. */ + // Initialize as parent R_TRY(this->Initialize(parent)); - /* If we're empty, we can just initialize as child. */ + // 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. */ + // Otherwise, we should append the child R_TRY(this->AppendChild(child)); } @@ -375,7 +371,7 @@ public: } Result RemoveChild() { - /* If we don't have a write-buffer, ensure that we have one. */ + // 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)); @@ -383,17 +379,17 @@ public: } } - /* Check that it's possible for us to remove a child. */ + // 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. */ + // Handle a trailing separator if (len > 0 && (p[len - 1] == '\\' || p[len - 1] == '/')) { --len; } - /* Remove the child path segment. */ + // Remove the child path segment while ((--len) >= 0 && p[len]) { if (p[len] == '/' || p[len] == '\\') { if (len > 0) { @@ -406,25 +402,25 @@ public: } } - /* Check that length remains > 0. */ + // 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. */ + // If we're already normalized, nothing to do R_SUCCEED_IF(this->IsNormalized()); - /* Check if we're normalized. */ + // 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 we're not normalized, normalize if (!normalized) { - /* Determine necessary buffer length. */ + // Determine necessary buffer length auto len = m_write_buffer.GetLength(); if (flags.IsRelativePathAllowed() && IsPathRelative(m_str)) { len += 2; @@ -433,20 +429,20 @@ public: len += 1; } - /* Allocate a new buffer. */ + // 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. */ + // 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. */ + // Set the normalized buffer as our buffer this->SetModifiableBuffer(std::move(buf)); } - /* Set normalized. */ + // Set normalized this->SetNormalized(); R_SUCCEED(); } @@ -458,19 +454,19 @@ private: } void SetModifiableBuffer(WriteBuffer&& buffer) { - /* Check pre-conditions. */ + // Check pre-conditions ASSERT(buffer.Get() != nullptr); ASSERT(buffer.GetLength() > 0); ASSERT(Common::IsAligned(buffer.GetLength(), WriteBufferAlignmentLength)); - /* Get whether we're normalized. */ + // Get whether we're normalized if (m_write_buffer.IsNormalized()) { buffer.SetNormalized(); } else { buffer.SetNotNormalized(); } - /* Set write buffer. */ + // Set write buffer m_write_buffer = std::move(buffer); m_str = m_write_buffer.Get(); } @@ -481,14 +477,14 @@ private: } Result Preallocate(size_t length) { - /* Allocate additional space, if needed. */ + // Allocate additional space, if needed if (length > m_write_buffer.GetLength()) { - /* Allocate buffer. */ + // Allocate buffer const size_t size = Common::AlignUp(length, WriteBufferAlignmentLength); auto buf = WriteBuffer::Make(size); R_UNLESS(buf.Get() != nullptr, ResultAllocationMemoryFailedMakeUnique); - /* Set write buffer. */ + // Set write buffer this->SetModifiableBuffer(std::move(buf)); } @@ -497,14 +493,14 @@ private: Result InitializeImpl(const char* path, size_t size) { if (size > 0 && path[0]) { - /* Pre allocate a buffer for the path. */ + // Pre allocate a buffer for the path R_TRY(this->Preallocate(size + 1)); - /* Copy the path. */ + // Copy the path const size_t copied = Strlcpy(m_write_buffer.Get(), path, size + 1); R_UNLESS(copied >= size, ResultUnexpectedInPathA); } else { - /* We can just clear the buffer. */ + // We can just clear the buffer this->ClearBuffer(); } @@ -548,14 +544,14 @@ public: }; inline Result SetUpFixedPath(FileSys::Path* out, const char* s) { - /* Verify the path is normalized. */ + // 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. */ + // Set the fixed path R_RETURN(out->SetShallowBuffer(s)); } diff --git a/src/core/file_sys/fs_path_utility.h b/src/core/file_sys/fs_path_utility.h index 276445707..e9011d065 100644 --- a/src/core/file_sys/fs_path_utility.h +++ b/src/core/file_sys/fs_path_utility.h @@ -1,9 +1,10 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// 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" @@ -14,7 +15,6 @@ namespace FileSys { constexpr inline size_t MountNameLengthMax = 15; -using Result = ::Result; namespace StringTraits { @@ -57,15 +57,15 @@ constexpr bool IsInvalidCharacterImpl(char c) { } // namespace impl constexpr bool IsInvalidCharacter(char c) { - return impl::IsInvalidCharacterImpl(c); + return impl::IsInvalidCharacterImpl(c); } constexpr bool IsInvalidCharacterForHostName(char c) { return impl::IsInvalidCharacterImpl(c); + Common::Size(InvalidCharactersForHostName)>(c); } constexpr bool IsInvalidCharacterForMountName(char c) { return impl::IsInvalidCharacterImpl(c); + Common::Size(InvalidCharactersForMountName)>(c); } } // namespace StringTraits @@ -177,14 +177,14 @@ constexpr inline bool IsPathStartWithCurrentDirectory(const char* path) { } constexpr inline bool IsSubPath(const char* lhs, const char* rhs) { - /* Check pre-conditions. */ + // Check pre-conditions ASSERT(lhs != nullptr); ASSERT(rhs != nullptr); - /* Import StringTraits names for current scope. */ + // Import StringTraits names for current scope using namespace StringTraits; - /* Special case certain paths. */ + // Special case certain paths if (IsUncPath(lhs) && !IsUncPath(rhs)) { return false; } @@ -201,7 +201,7 @@ constexpr inline bool IsSubPath(const char* lhs, const char* rhs) { return true; } - /* Check subpath. */ + // Check subpath for (size_t i = 0; /* ... */; ++i) { if (lhs[i] == NullTerminator) { return rhs[i] == DirectorySeparator; @@ -213,7 +213,7 @@ constexpr inline bool IsSubPath(const char* lhs, const char* rhs) { } } -/* Path utilities. */ +// 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) { @@ -224,10 +224,10 @@ constexpr inline void Replace(char* dst, size_t dst_size, char old_char, char ne } constexpr inline Result CheckUtf8(const char* s) { - /* Check pre-conditions. */ + // Check pre-conditions ASSERT(s != nullptr); - /* Iterate, checking for utf8-validity. */ + // Iterate, checking for utf8-validity while (*s) { char utf8_buf[4] = {}; @@ -242,7 +242,7 @@ constexpr inline Result CheckUtf8(const char* s) { R_SUCCEED(); } -/* Path formatting. */ +// Path formatting class PathNormalizer { private: enum class PathState { @@ -256,10 +256,10 @@ private: private: static constexpr void ReplaceParentDirectoryPath(char* dst, const char* src) { - /* Use StringTraits names for remainder of scope. */ + // Use StringTraits names for remainder of scope using namespace StringTraits; - /* Start with a dir-separator. */ + // Start with a dir-separator dst[0] = DirectorySeparator; auto i = 1; @@ -292,14 +292,14 @@ private: public: static constexpr bool IsParentDirectoryPathReplacementNeeded(const char* path) { - /* Use StringTraits names for remainder of scope. */ + // 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. */ + // 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) { @@ -333,24 +333,24 @@ public: 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. */ + // Use StringTraits names for remainder of scope using namespace StringTraits; - /* Parse the path. */ + // Parse the path auto state = PathState::Start; size_t len = 0; while (path[len] != NullTerminator) { - /* Get the current character. */ + // Get the current character const char c = path[len++]; - /* Check the current character is valid. */ + // Check the current character is valid if (!allow_all_characters && state != PathState::Start) { R_UNLESS(!IsInvalidCharacter(c), ResultInvalidCharacter); } - /* Process depending on current state. */ + // Process depending on current state switch (state) { - /* Import the PathState enums for convenience. */ + // Import the PathState enums for convenience using enum PathState; case Start: @@ -401,9 +401,9 @@ public: } } - /* Check the final state. */ + // Check the final state switch (state) { - /* Import the PathState enums for convenience. */ + // Import the PathState enums for convenience using enum PathState; case Start: R_THROW(ResultInvalidPathFormat); @@ -421,7 +421,7 @@ public: break; } - /* Set the output length. */ + // Set the output length *out_len = len; R_SUCCEED(); } @@ -429,21 +429,21 @@ public: 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. */ + // Use StringTraits names for remainder of scope using namespace StringTraits; - /* Prepare to iterate. */ + // 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 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. */ + // We're going to need to do path replacement, potentially char* replacement_path = nullptr; size_t replacement_path_size = 0; @@ -457,7 +457,7 @@ public: } }); - /* Perform path replacement, if necessary. */ + // Perform path replacement, if necessary if (IsParentDirectoryPathReplacementNeeded(cur_path)) { if (std::is_constant_evaluated()) { replacement_path_size = EntryNameLengthMax + 1; @@ -472,25 +472,24 @@ public: cur_path = replacement_path; } - /* Iterate, normalizing path components. */ + // 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. */ + // Process a directory separator, if we run into one if (cur_path[i] == DirectorySeparator) { - /* Swallow separators. */ + // Swallow separators do { ++i; } while (cur_path[i] == DirectorySeparator); - /* Check if we hit end of string. */ + // 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 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; @@ -501,15 +500,15 @@ public: dst[total_len++] = DirectorySeparator; } - /* Don't skip the next separator. */ + // Don't skip the next separator skip_next_sep = false; } - /* Get the length of the current directory component. */ + // 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. */ + // Check for validity if (!allow_all_characters) { R_UNLESS(!IsInvalidCharacter(cur_path[i + dir_len]), ResultInvalidCharacter); } @@ -517,19 +516,19 @@ public: ++dir_len; } - /* Handle the current dir component. */ + // 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. */ + // We should have just written a separator ASSERT(dst[total_len - 1] == DirectorySeparator); - /* We should have started with a separator, for non-windows paths. */ + // We should have started with a separator, for non-windows paths if (!is_windows_path) { ASSERT(dst[0] == DirectorySeparator); } - /* Remove the previous component. */ + // Remove the previous component if (total_len == 1) { R_UNLESS(is_windows_path, ResultDirectoryUnobtainable); @@ -544,15 +543,15 @@ public: } while ((--total_len) != 0); } - /* We should be pointing to a directory separator, for non-windows paths. */ + // 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. */ + // We should remain in bounds ASSERT(total_len < max_out_size); } else { - /* Copy, possibly truncating. */ + // Copy, possibly truncating if (total_len + dir_len + 1 > max_out_size) { const size_t copy_len = max_out_size - (total_len + 1); @@ -570,7 +569,7 @@ public: } } - /* Advance past the current directory component. */ + // Advance past the current directory component i += dir_len; } @@ -583,22 +582,22 @@ public: dst[0] = DirectorySeparator; } - /* NOTE: Probable nintendo bug, as max_out_size must be at least total_len + 1 for the null - * terminator. */ + // 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. */ + // 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 that the result path is normalized ASSERT(is_normalized); - /* Set the output length. */ + // Set the output length *out_len = total_len; R_SUCCEED(); } @@ -607,7 +606,7 @@ public: class PathFormatter { private: static constexpr Result CheckSharedName(const char* name, size_t len) { - /* Use StringTraits names for remainder of scope. */ + // Use StringTraits names for remainder of scope using namespace StringTraits; if (len == 1) { @@ -624,7 +623,7 @@ private: } static constexpr Result CheckHostName(const char* name, size_t len) { - /* Use StringTraits names for remainder of scope. */ + // Use StringTraits names for remainder of scope using namespace StringTraits; if (len == 2) { @@ -640,10 +639,10 @@ private: static constexpr Result CheckInvalidBackslash(bool* out_contains_backslash, const char* path, bool allow_backslash) { - /* Use StringTraits names for remainder of scope. */ + // Use StringTraits names for remainder of scope using namespace StringTraits; - /* Default to no backslashes, so we can just write if we see one. */ + // Default to no backslashes, so we can just write if we see one *out_contains_backslash = false; while (*path != NullTerminator) { @@ -670,34 +669,34 @@ public: 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. */ + // 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. */ + // Use StringTraits names for remainder of scope using namespace StringTraits; - /* Determine max mount length. */ + // 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. */ + // 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 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 we see a directory separator, we're not in a mount name if (c == DirectorySeparator || c == AlternateDirectorySeparator) { *out = path; *out_len = 0; @@ -705,19 +704,19 @@ public: } } - /* Check to be sure we're actually looking at a mount name. */ + // 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. */ + // 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. */ + // Copy out the mount name if (out_mount_name_buffer_size > 0) { R_UNLESS(mount_len < out_mount_name_buffer_size, ResultTooLongPath); @@ -727,7 +726,7 @@ public: out_mount_name[mount_len] = NullTerminator; } - /* Set the output. */ + // Set the output *out = path + mount_len; *out_len = mount_len; R_SUCCEED(); @@ -742,21 +741,21 @@ public: char* out_relative, size_t out_relative_buffer_size, const char* path) { - /* Check pre-conditions. */ + // 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. */ + // Use StringTraits names for remainder of scope using namespace StringTraits; - /* Initialize the output buffer, if we have one. */ + // Initialize the output buffer, if we have one if (out_relative_buffer_size > 0) { out_relative[0] = NullTerminator; } - /* Check if the path is relative. */ + // 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) { @@ -771,10 +770,10 @@ public: R_SUCCEED(); } - /* Ensure the path isn't a parent directory. */ + // Ensure the path isn't a parent directory R_UNLESS(!(path[0] == Dot && path[1] == Dot), ResultDirectoryUnobtainable); - /* There was no relative dot path. */ + // There was no relative dot path *out = path; *out_len = 0; R_SUCCEED(); @@ -782,7 +781,7 @@ public: 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(). */ + // 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)) { @@ -801,21 +800,21 @@ public: 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. */ + // 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. */ + // Use StringTraits names for remainder of scope using namespace StringTraits; - /* Initialize the output buffer, if we have one. */ + // Initialize the output buffer, if we have one if (out_win_buffer_size > 0) { out_win[0] = NullTerminator; } - /* Handle path start. */ + // Handle path start const char* cur_path = path; if (has_mount_name && path[0] == DirectorySeparator) { if (path[1] == AlternateDirectorySeparator && path[2] == AlternateDirectorySeparator) { @@ -829,9 +828,9 @@ public: } } - /* Handle windows drive. */ + // Handle windows drive if (IsWindowsDrive(cur_path)) { - /* Parse up to separator. */ + // 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); @@ -842,13 +841,13 @@ public: } } - /* Ensure that we're normalized, if we're required to be. */ + // 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. */ + // 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) { @@ -864,7 +863,7 @@ public: R_SUCCEED(); } - /* Handle DOS device. */ + // Handle DOS device if (IsDosDevicePath(cur_path)) { size_t dos_prefix_len = DosDevicePathPrefixLength; @@ -875,7 +874,7 @@ public: } if (out_win_buffer_size > 0) { - /* Ensure we can copy into the normalized buffer. */ + // 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) { @@ -891,7 +890,7 @@ public: R_SUCCEED(); } - /* Handle UNC path. */ + // Handle UNC path if (IsUncPath(cur_path, false, true)) { const char* final_path = cur_path; @@ -932,13 +931,13 @@ public: size_t unc_prefix_len = final_path - cur_path; - /* Ensure that we're normalized, if we're required to be. */ + // 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. */ + // 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) { @@ -954,7 +953,7 @@ public: R_SUCCEED(); } - /* There's no windows path to parse. */ + // There's no windows path to parse *out = path; *out_len = 0; R_SUCCEED(); @@ -962,18 +961,18 @@ public: static constexpr Result IsNormalized(bool* out, size_t* out_len, const char* path, const PathFlags& flags = {}) { - /* Ensure nothing is null. */ + // 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. */ + // Verify that the path is valid utf-8 R_TRY(CheckUtf8(path)); - /* Use StringTraits names for remainder of scope. */ + // Use StringTraits names for remainder of scope using namespace StringTraits; - /* Handle the case where the path is empty. */ + // Handle the case where the path is empty if (path[0] == NullTerminator) { R_UNLESS(flags.IsEmptyPathAllowed(), ResultInvalidPathFormat); @@ -982,32 +981,32 @@ public: R_SUCCEED(); } - /* All normalized paths start with a directory separator...unless they're windows paths, - * relative paths, or have mount names. */ + // 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. */ + // 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. */ + // 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 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. */ + // Check that the path starts as a normalized path should if (path[0] != DirectorySeparator && !IsPathStartWithCurrentDirectory(path) && !IsWindowsPath(path, false)) { R_UNLESS(flags.IsRelativePathAllowed(), ResultInvalidPathFormat); @@ -1017,11 +1016,11 @@ public: R_SUCCEED(); } - /* Process relative path. */ + // 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 we have a relative path, check that was allowed if (relative_len > 0) { R_UNLESS(flags.IsRelativePathAllowed(), ResultInvalidPathFormat); @@ -1034,13 +1033,13 @@ public: } } - /* Process windows path. */ + // 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 the windows path wasn't normalized, we're not normalized if (!normalized_win) { R_UNLESS(flags.IsWindowsPathAllowed(), ResultInvalidPathFormat); @@ -1048,22 +1047,22 @@ public: R_SUCCEED(); } - /* If we had a windows path, check that was allowed. */ + // 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. */ + // 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. */ + // 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. */ + // 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; @@ -1072,48 +1071,48 @@ public: } } - /* Check that parent directory replacement is not needed if backslashes are allowed. */ + // 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. */ + // 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. */ + // 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. */ + // 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. */ + // Add the normal length total_len += normal_len; - /* Set the output length. */ + // 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. */ + // Use StringTraits names for remainder of scope using namespace StringTraits; - /* Prepare to iterate. */ + // Prepare to iterate const char* src = path; size_t cur_pos = 0; bool is_windows_path = false; - /* Check if the path is empty. */ + // Check if the path is empty if (src[0] == NullTerminator) { if (dst_size != 0) { dst[0] = NullTerminator; @@ -1124,7 +1123,7 @@ public: R_SUCCEED(); } - /* Handle a mount name. */ + // 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, @@ -1133,7 +1132,7 @@ public: cur_pos += mount_name_len; } - /* Handle a drive-relative prefix. */ + // Handle a drive-relative prefix bool is_drive_relative = false; if (src[0] != DirectorySeparator && !IsPathStartWithCurrentDirectory(src) && !IsWindowsPath(src, false)) { @@ -1161,7 +1160,7 @@ public: } } - /* Handle a windows path. */ + // Handle a windows path if (flags.IsWindowsPathAllowed()) { const char* const orig = src; @@ -1187,16 +1186,16 @@ public: } } - /* Check for invalid backslash. */ + // 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. */ + // 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. */ + // 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; @@ -1226,7 +1225,7 @@ public: dst_size - cur_pos, is_windows_path, is_drive_relative, flags.IsAllCharactersAllowed())); } else { - /* We can just do normalization. */ + // 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, diff --git a/src/core/file_sys/fs_string_util.h b/src/core/file_sys/fs_string_util.h index 68114e72c..874e09054 100644 --- a/src/core/file_sys/fs_string_util.h +++ b/src/core/file_sys/fs_string_util.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -71,21 +71,6 @@ static constexpr int Strlcpy(T* dst, const T* src, int count) { return static_cast(cur - src); } -/* std::size() does not support zero-size C arrays. We're fixing that. */ -template -constexpr auto size(const C& c) -> decltype(c.size()) { - return std::size(c); -} - -template -constexpr std::size_t size(const C& c) { - if constexpr (sizeof(C) == 0) { - return 0; - } else { - return std::size(c); - } -} - enum CharacterEncodingResult { CharacterEncodingResult_Success = 0, CharacterEncodingResult_InsufficientLength = 1, @@ -116,11 +101,11 @@ public: } // namespace impl constexpr inline CharacterEncodingResult ConvertCharacterUtf8ToUtf32(u32* dst, const char* src) { - /* Check pre-conditions. */ + // Check pre-conditions ASSERT(dst != nullptr); ASSERT(src != nullptr); - /* Perform the conversion. */ + // Perform the conversion const auto* p = src; switch (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast(p[0]))) { case 1: @@ -164,24 +149,24 @@ constexpr inline CharacterEncodingResult ConvertCharacterUtf8ToUtf32(u32* dst, c break; } - /* We failed to convert. */ + // We failed to convert return CharacterEncodingResult_InvalidFormat; } constexpr inline CharacterEncodingResult PickOutCharacterFromUtf8String(char* dst, const char** str) { - /* Check pre-conditions. */ + // Check pre-conditions ASSERT(dst != nullptr); ASSERT(str != nullptr); ASSERT(*str != nullptr); - /* Clear the output. */ + // Clear the output dst[0] = 0; dst[1] = 0; dst[2] = 0; dst[3] = 0; - /* Perform the conversion. */ + // Perform the conversion const auto* p = *str; u32 c = static_cast(*p); switch (impl::CharacterEncodingHelper::GetUtf8NBytes(c)) { -- cgit v1.2.3