diff options
Diffstat (limited to 'src/core/file_sys')
30 files changed, 1267 insertions, 1211 deletions
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp new file mode 100644 index 000000000..6cfef774d --- /dev/null +++ b/src/core/file_sys/content_archive.cpp | |||
| @@ -0,0 +1,167 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "core/file_sys/content_archive.h" | ||
| 7 | #include "core/file_sys/vfs_offset.h" | ||
| 8 | #include "core/loader/loader.h" | ||
| 9 | |||
| 10 | namespace FileSys { | ||
| 11 | |||
| 12 | // Media offsets in headers are stored divided by 512. Mult. by this to get real offset. | ||
| 13 | constexpr u64 MEDIA_OFFSET_MULTIPLIER = 0x200; | ||
| 14 | |||
| 15 | constexpr u64 SECTION_HEADER_SIZE = 0x200; | ||
| 16 | constexpr u64 SECTION_HEADER_OFFSET = 0x400; | ||
| 17 | |||
| 18 | constexpr u32 IVFC_MAX_LEVEL = 6; | ||
| 19 | |||
| 20 | enum class NCASectionFilesystemType : u8 { | ||
| 21 | PFS0 = 0x2, | ||
| 22 | ROMFS = 0x3, | ||
| 23 | }; | ||
| 24 | |||
| 25 | struct NCASectionHeaderBlock { | ||
| 26 | INSERT_PADDING_BYTES(3); | ||
| 27 | NCASectionFilesystemType filesystem_type; | ||
| 28 | u8 crypto_type; | ||
| 29 | INSERT_PADDING_BYTES(3); | ||
| 30 | }; | ||
| 31 | static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size."); | ||
| 32 | |||
| 33 | struct PFS0Superblock { | ||
| 34 | NCASectionHeaderBlock header_block; | ||
| 35 | std::array<u8, 0x20> hash; | ||
| 36 | u32_le size; | ||
| 37 | INSERT_PADDING_BYTES(4); | ||
| 38 | u64_le hash_table_offset; | ||
| 39 | u64_le hash_table_size; | ||
| 40 | u64_le pfs0_header_offset; | ||
| 41 | u64_le pfs0_size; | ||
| 42 | INSERT_PADDING_BYTES(432); | ||
| 43 | }; | ||
| 44 | static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size."); | ||
| 45 | |||
| 46 | struct IVFCLevel { | ||
| 47 | u64_le offset; | ||
| 48 | u64_le size; | ||
| 49 | u32_le block_size; | ||
| 50 | u32_le reserved; | ||
| 51 | }; | ||
| 52 | static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size."); | ||
| 53 | |||
| 54 | struct RomFSSuperblock { | ||
| 55 | NCASectionHeaderBlock header_block; | ||
| 56 | u32_le magic; | ||
| 57 | u32_le magic_number; | ||
| 58 | INSERT_PADDING_BYTES(8); | ||
| 59 | std::array<IVFCLevel, 6> levels; | ||
| 60 | INSERT_PADDING_BYTES(64); | ||
| 61 | }; | ||
| 62 | static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size."); | ||
| 63 | |||
| 64 | NCA::NCA(VirtualFile file_) : file(file_) { | ||
| 65 | if (sizeof(NCAHeader) != file->ReadObject(&header)) | ||
| 66 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 67 | |||
| 68 | if (!IsValidNCA(header)) { | ||
| 69 | status = Loader::ResultStatus::ErrorInvalidFormat; | ||
| 70 | return; | ||
| 71 | } | ||
| 72 | |||
| 73 | std::ptrdiff_t number_sections = | ||
| 74 | std::count_if(std::begin(header.section_tables), std::end(header.section_tables), | ||
| 75 | [](NCASectionTableEntry entry) { return entry.media_offset > 0; }); | ||
| 76 | |||
| 77 | for (std::ptrdiff_t i = 0; i < number_sections; ++i) { | ||
| 78 | // Seek to beginning of this section. | ||
| 79 | NCASectionHeaderBlock block{}; | ||
| 80 | if (sizeof(NCASectionHeaderBlock) != | ||
| 81 | file->ReadObject(&block, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) | ||
| 82 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 83 | |||
| 84 | if (block.filesystem_type == NCASectionFilesystemType::ROMFS) { | ||
| 85 | RomFSSuperblock sb{}; | ||
| 86 | if (sizeof(RomFSSuperblock) != | ||
| 87 | file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) | ||
| 88 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 89 | |||
| 90 | const size_t romfs_offset = | ||
| 91 | header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER + | ||
| 92 | sb.levels[IVFC_MAX_LEVEL - 1].offset; | ||
| 93 | const size_t romfs_size = sb.levels[IVFC_MAX_LEVEL - 1].size; | ||
| 94 | files.emplace_back(std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset)); | ||
| 95 | romfs = files.back(); | ||
| 96 | } else if (block.filesystem_type == NCASectionFilesystemType::PFS0) { | ||
| 97 | PFS0Superblock sb{}; | ||
| 98 | // Seek back to beginning of this section. | ||
| 99 | if (sizeof(PFS0Superblock) != | ||
| 100 | file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) | ||
| 101 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 102 | |||
| 103 | u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) * | ||
| 104 | MEDIA_OFFSET_MULTIPLIER) + | ||
| 105 | sb.pfs0_header_offset; | ||
| 106 | u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset - | ||
| 107 | header.section_tables[i].media_offset); | ||
| 108 | auto npfs = std::make_shared<PartitionFilesystem>( | ||
| 109 | std::make_shared<OffsetVfsFile>(file, size, offset)); | ||
| 110 | |||
| 111 | if (npfs->GetStatus() == Loader::ResultStatus::Success) { | ||
| 112 | dirs.emplace_back(npfs); | ||
| 113 | if (IsDirectoryExeFS(dirs.back())) | ||
| 114 | exefs = dirs.back(); | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | status = Loader::ResultStatus::Success; | ||
| 120 | } | ||
| 121 | |||
| 122 | Loader::ResultStatus NCA::GetStatus() const { | ||
| 123 | return status; | ||
| 124 | } | ||
| 125 | |||
| 126 | std::vector<std::shared_ptr<VfsFile>> NCA::GetFiles() const { | ||
| 127 | if (status != Loader::ResultStatus::Success) | ||
| 128 | return {}; | ||
| 129 | return files; | ||
| 130 | } | ||
| 131 | |||
| 132 | std::vector<std::shared_ptr<VfsDirectory>> NCA::GetSubdirectories() const { | ||
| 133 | if (status != Loader::ResultStatus::Success) | ||
| 134 | return {}; | ||
| 135 | return dirs; | ||
| 136 | } | ||
| 137 | |||
| 138 | std::string NCA::GetName() const { | ||
| 139 | return file->GetName(); | ||
| 140 | } | ||
| 141 | |||
| 142 | std::shared_ptr<VfsDirectory> NCA::GetParentDirectory() const { | ||
| 143 | return file->GetContainingDirectory(); | ||
| 144 | } | ||
| 145 | |||
| 146 | NCAContentType NCA::GetType() const { | ||
| 147 | return header.content_type; | ||
| 148 | } | ||
| 149 | |||
| 150 | u64 NCA::GetTitleId() const { | ||
| 151 | if (status != Loader::ResultStatus::Success) | ||
| 152 | return {}; | ||
| 153 | return header.title_id; | ||
| 154 | } | ||
| 155 | |||
| 156 | VirtualFile NCA::GetRomFS() const { | ||
| 157 | return romfs; | ||
| 158 | } | ||
| 159 | |||
| 160 | VirtualDir NCA::GetExeFS() const { | ||
| 161 | return exefs; | ||
| 162 | } | ||
| 163 | |||
| 164 | bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | ||
| 165 | return false; | ||
| 166 | } | ||
| 167 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h new file mode 100644 index 000000000..129a70b97 --- /dev/null +++ b/src/core/file_sys/content_archive.h | |||
| @@ -0,0 +1,95 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_funcs.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "common/swap.h" | ||
| 10 | #include "core/file_sys/partition_filesystem.h" | ||
| 11 | |||
| 12 | namespace FileSys { | ||
| 13 | |||
| 14 | enum class NCAContentType : u8 { | ||
| 15 | Program = 0, | ||
| 16 | Meta = 1, | ||
| 17 | Control = 2, | ||
| 18 | Manual = 3, | ||
| 19 | Data = 4, | ||
| 20 | }; | ||
| 21 | |||
| 22 | struct NCASectionTableEntry { | ||
| 23 | u32_le media_offset; | ||
| 24 | u32_le media_end_offset; | ||
| 25 | INSERT_PADDING_BYTES(0x8); | ||
| 26 | }; | ||
| 27 | static_assert(sizeof(NCASectionTableEntry) == 0x10, "NCASectionTableEntry has incorrect size."); | ||
| 28 | |||
| 29 | struct NCAHeader { | ||
| 30 | std::array<u8, 0x100> rsa_signature_1; | ||
| 31 | std::array<u8, 0x100> rsa_signature_2; | ||
| 32 | u32_le magic; | ||
| 33 | u8 is_system; | ||
| 34 | NCAContentType content_type; | ||
| 35 | u8 crypto_type; | ||
| 36 | u8 key_index; | ||
| 37 | u64_le size; | ||
| 38 | u64_le title_id; | ||
| 39 | INSERT_PADDING_BYTES(0x4); | ||
| 40 | u32_le sdk_version; | ||
| 41 | u8 crypto_type_2; | ||
| 42 | INSERT_PADDING_BYTES(15); | ||
| 43 | std::array<u8, 0x10> rights_id; | ||
| 44 | std::array<NCASectionTableEntry, 0x4> section_tables; | ||
| 45 | std::array<std::array<u8, 0x20>, 0x4> hash_tables; | ||
| 46 | std::array<std::array<u8, 0x10>, 0x4> key_area; | ||
| 47 | INSERT_PADDING_BYTES(0xC0); | ||
| 48 | }; | ||
| 49 | static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size."); | ||
| 50 | |||
| 51 | inline bool IsDirectoryExeFS(std::shared_ptr<FileSys::VfsDirectory> pfs) { | ||
| 52 | // According to switchbrew, an exefs must only contain these two files: | ||
| 53 | return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr; | ||
| 54 | } | ||
| 55 | |||
| 56 | inline bool IsValidNCA(const NCAHeader& header) { | ||
| 57 | return header.magic == Common::MakeMagic('N', 'C', 'A', '2') || | ||
| 58 | header.magic == Common::MakeMagic('N', 'C', 'A', '3'); | ||
| 59 | } | ||
| 60 | |||
| 61 | // An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner. | ||
| 62 | // After construction, use GetStatus to determine if the file is valid and ready to be used. | ||
| 63 | class NCA : public ReadOnlyVfsDirectory { | ||
| 64 | public: | ||
| 65 | explicit NCA(VirtualFile file); | ||
| 66 | Loader::ResultStatus GetStatus() const; | ||
| 67 | |||
| 68 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; | ||
| 69 | std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; | ||
| 70 | std::string GetName() const override; | ||
| 71 | std::shared_ptr<VfsDirectory> GetParentDirectory() const override; | ||
| 72 | |||
| 73 | NCAContentType GetType() const; | ||
| 74 | u64 GetTitleId() const; | ||
| 75 | |||
| 76 | VirtualFile GetRomFS() const; | ||
| 77 | VirtualDir GetExeFS() const; | ||
| 78 | |||
| 79 | protected: | ||
| 80 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 81 | |||
| 82 | private: | ||
| 83 | std::vector<VirtualDir> dirs; | ||
| 84 | std::vector<VirtualFile> files; | ||
| 85 | |||
| 86 | VirtualFile romfs = nullptr; | ||
| 87 | VirtualDir exefs = nullptr; | ||
| 88 | VirtualFile file; | ||
| 89 | |||
| 90 | NCAHeader header{}; | ||
| 91 | |||
| 92 | Loader::ResultStatus status{}; | ||
| 93 | }; | ||
| 94 | |||
| 95 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h index c7639795e..213ce1826 100644 --- a/src/core/file_sys/directory.h +++ b/src/core/file_sys/directory.h | |||
| @@ -8,13 +8,17 @@ | |||
| 8 | #include <cstddef> | 8 | #include <cstddef> |
| 9 | #include "common/common_funcs.h" | 9 | #include "common/common_funcs.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "core/file_sys/filesystem.h" | ||
| 12 | 11 | ||
| 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 14 | // FileSys namespace | 13 | // FileSys namespace |
| 15 | 14 | ||
| 16 | namespace FileSys { | 15 | namespace FileSys { |
| 17 | 16 | ||
| 17 | enum EntryType : u8 { | ||
| 18 | Directory = 0, | ||
| 19 | File = 1, | ||
| 20 | }; | ||
| 21 | |||
| 18 | // Structure of a directory entry, from | 22 | // Structure of a directory entry, from |
| 19 | // http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry | 23 | // http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry |
| 20 | const size_t FILENAME_LENGTH = 0x300; | 24 | const size_t FILENAME_LENGTH = 0x300; |
diff --git a/src/core/file_sys/disk_filesystem.cpp b/src/core/file_sys/disk_filesystem.cpp deleted file mode 100644 index d248c2df4..000000000 --- a/src/core/file_sys/disk_filesystem.cpp +++ /dev/null | |||
| @@ -1,239 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstring> | ||
| 6 | #include <memory> | ||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/file_sys/disk_filesystem.h" | ||
| 10 | #include "core/file_sys/errors.h" | ||
| 11 | |||
| 12 | namespace FileSys { | ||
| 13 | |||
| 14 | static std::string ModeFlagsToString(Mode mode) { | ||
| 15 | std::string mode_str; | ||
| 16 | u32 mode_flags = static_cast<u32>(mode); | ||
| 17 | |||
| 18 | // Calculate the correct open mode for the file. | ||
| 19 | if ((mode_flags & static_cast<u32>(Mode::Read)) && | ||
| 20 | (mode_flags & static_cast<u32>(Mode::Write))) { | ||
| 21 | if (mode_flags & static_cast<u32>(Mode::Append)) | ||
| 22 | mode_str = "a+"; | ||
| 23 | else | ||
| 24 | mode_str = "r+"; | ||
| 25 | } else { | ||
| 26 | if (mode_flags & static_cast<u32>(Mode::Read)) | ||
| 27 | mode_str = "r"; | ||
| 28 | else if (mode_flags & static_cast<u32>(Mode::Append)) | ||
| 29 | mode_str = "a"; | ||
| 30 | else if (mode_flags & static_cast<u32>(Mode::Write)) | ||
| 31 | mode_str = "w"; | ||
| 32 | } | ||
| 33 | |||
| 34 | mode_str += "b"; | ||
| 35 | |||
| 36 | return mode_str; | ||
| 37 | } | ||
| 38 | |||
| 39 | std::string Disk_FileSystem::GetName() const { | ||
| 40 | return "Disk"; | ||
| 41 | } | ||
| 42 | |||
| 43 | ResultVal<std::unique_ptr<StorageBackend>> Disk_FileSystem::OpenFile(const std::string& path, | ||
| 44 | Mode mode) const { | ||
| 45 | |||
| 46 | // Calculate the correct open mode for the file. | ||
| 47 | std::string mode_str = ModeFlagsToString(mode); | ||
| 48 | |||
| 49 | std::string full_path = base_directory + path; | ||
| 50 | auto file = std::make_shared<FileUtil::IOFile>(full_path, mode_str.c_str()); | ||
| 51 | |||
| 52 | if (!file->IsOpen()) { | ||
| 53 | return ERROR_PATH_NOT_FOUND; | ||
| 54 | } | ||
| 55 | |||
| 56 | return MakeResult<std::unique_ptr<StorageBackend>>( | ||
| 57 | std::make_unique<Disk_Storage>(std::move(file))); | ||
| 58 | } | ||
| 59 | |||
| 60 | ResultCode Disk_FileSystem::DeleteFile(const std::string& path) const { | ||
| 61 | std::string full_path = base_directory + path; | ||
| 62 | |||
| 63 | if (!FileUtil::Exists(full_path)) { | ||
| 64 | return ERROR_PATH_NOT_FOUND; | ||
| 65 | } | ||
| 66 | |||
| 67 | FileUtil::Delete(full_path); | ||
| 68 | |||
| 69 | return RESULT_SUCCESS; | ||
| 70 | } | ||
| 71 | |||
| 72 | ResultCode Disk_FileSystem::RenameFile(const std::string& src_path, | ||
| 73 | const std::string& dest_path) const { | ||
| 74 | const std::string full_src_path = base_directory + src_path; | ||
| 75 | const std::string full_dest_path = base_directory + dest_path; | ||
| 76 | |||
| 77 | if (!FileUtil::Exists(full_src_path)) { | ||
| 78 | return ERROR_PATH_NOT_FOUND; | ||
| 79 | } | ||
| 80 | // TODO(wwylele): Use correct error code | ||
| 81 | return FileUtil::Rename(full_src_path, full_dest_path) ? RESULT_SUCCESS : ResultCode(-1); | ||
| 82 | } | ||
| 83 | |||
| 84 | ResultCode Disk_FileSystem::DeleteDirectory(const Path& path) const { | ||
| 85 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 86 | // TODO(wwylele): Use correct error code | ||
| 87 | return ResultCode(-1); | ||
| 88 | } | ||
| 89 | |||
| 90 | ResultCode Disk_FileSystem::DeleteDirectoryRecursively(const Path& path) const { | ||
| 91 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 92 | // TODO(wwylele): Use correct error code | ||
| 93 | return ResultCode(-1); | ||
| 94 | } | ||
| 95 | |||
| 96 | ResultCode Disk_FileSystem::CreateFile(const std::string& path, u64 size) const { | ||
| 97 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 98 | |||
| 99 | std::string full_path = base_directory + path; | ||
| 100 | if (size == 0) { | ||
| 101 | FileUtil::CreateEmptyFile(full_path); | ||
| 102 | return RESULT_SUCCESS; | ||
| 103 | } | ||
| 104 | |||
| 105 | FileUtil::IOFile file(full_path, "wb"); | ||
| 106 | // Creates a sparse file (or a normal file on filesystems without the concept of sparse files) | ||
| 107 | // We do this by seeking to the right size, then writing a single null byte. | ||
| 108 | if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) { | ||
| 109 | return RESULT_SUCCESS; | ||
| 110 | } | ||
| 111 | |||
| 112 | LOG_ERROR(Service_FS, "Too large file"); | ||
| 113 | // TODO(Subv): Find out the correct error code | ||
| 114 | return ResultCode(-1); | ||
| 115 | } | ||
| 116 | |||
| 117 | ResultCode Disk_FileSystem::CreateDirectory(const std::string& path) const { | ||
| 118 | // TODO(Subv): Perform path validation to prevent escaping the emulator sandbox. | ||
| 119 | std::string full_path = base_directory + path; | ||
| 120 | |||
| 121 | if (FileUtil::CreateDir(full_path)) { | ||
| 122 | return RESULT_SUCCESS; | ||
| 123 | } | ||
| 124 | |||
| 125 | LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating {}", full_path); | ||
| 126 | // TODO(wwylele): Use correct error code | ||
| 127 | return ResultCode(-1); | ||
| 128 | } | ||
| 129 | |||
| 130 | ResultCode Disk_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const { | ||
| 131 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 132 | // TODO(wwylele): Use correct error code | ||
| 133 | return ResultCode(-1); | ||
| 134 | } | ||
| 135 | |||
| 136 | ResultVal<std::unique_ptr<DirectoryBackend>> Disk_FileSystem::OpenDirectory( | ||
| 137 | const std::string& path) const { | ||
| 138 | |||
| 139 | std::string full_path = base_directory + path; | ||
| 140 | |||
| 141 | if (!FileUtil::IsDirectory(full_path)) { | ||
| 142 | // TODO(Subv): Find the correct error code for this. | ||
| 143 | return ResultCode(-1); | ||
| 144 | } | ||
| 145 | |||
| 146 | auto directory = std::make_unique<Disk_Directory>(full_path); | ||
| 147 | return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory)); | ||
| 148 | } | ||
| 149 | |||
| 150 | u64 Disk_FileSystem::GetFreeSpaceSize() const { | ||
| 151 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 152 | return 0; | ||
| 153 | } | ||
| 154 | |||
| 155 | ResultVal<FileSys::EntryType> Disk_FileSystem::GetEntryType(const std::string& path) const { | ||
| 156 | std::string full_path = base_directory + path; | ||
| 157 | if (!FileUtil::Exists(full_path)) { | ||
| 158 | return ERROR_PATH_NOT_FOUND; | ||
| 159 | } | ||
| 160 | |||
| 161 | if (FileUtil::IsDirectory(full_path)) | ||
| 162 | return MakeResult(EntryType::Directory); | ||
| 163 | |||
| 164 | return MakeResult(EntryType::File); | ||
| 165 | } | ||
| 166 | |||
| 167 | ResultVal<size_t> Disk_Storage::Read(const u64 offset, const size_t length, u8* buffer) const { | ||
| 168 | LOG_TRACE(Service_FS, "called offset={}, length={}", offset, length); | ||
| 169 | file->Seek(offset, SEEK_SET); | ||
| 170 | return MakeResult<size_t>(file->ReadBytes(buffer, length)); | ||
| 171 | } | ||
| 172 | |||
| 173 | ResultVal<size_t> Disk_Storage::Write(const u64 offset, const size_t length, const bool flush, | ||
| 174 | const u8* buffer) const { | ||
| 175 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 176 | file->Seek(offset, SEEK_SET); | ||
| 177 | size_t written = file->WriteBytes(buffer, length); | ||
| 178 | if (flush) { | ||
| 179 | file->Flush(); | ||
| 180 | } | ||
| 181 | return MakeResult<size_t>(written); | ||
| 182 | } | ||
| 183 | |||
| 184 | u64 Disk_Storage::GetSize() const { | ||
| 185 | return file->GetSize(); | ||
| 186 | } | ||
| 187 | |||
| 188 | bool Disk_Storage::SetSize(const u64 size) const { | ||
| 189 | file->Resize(size); | ||
| 190 | file->Flush(); | ||
| 191 | return true; | ||
| 192 | } | ||
| 193 | |||
| 194 | Disk_Directory::Disk_Directory(const std::string& path) { | ||
| 195 | unsigned size = FileUtil::ScanDirectoryTree(path, directory); | ||
| 196 | directory.size = size; | ||
| 197 | directory.isDirectory = true; | ||
| 198 | children_iterator = directory.children.begin(); | ||
| 199 | } | ||
| 200 | |||
| 201 | u64 Disk_Directory::Read(const u64 count, Entry* entries) { | ||
| 202 | u64 entries_read = 0; | ||
| 203 | |||
| 204 | while (entries_read < count && children_iterator != directory.children.cend()) { | ||
| 205 | const FileUtil::FSTEntry& file = *children_iterator; | ||
| 206 | const std::string& filename = file.virtualName; | ||
| 207 | Entry& entry = entries[entries_read]; | ||
| 208 | |||
| 209 | LOG_TRACE(Service_FS, "File {}: size={} dir={}", filename, file.size, file.isDirectory); | ||
| 210 | |||
| 211 | // TODO(Link Mauve): use a proper conversion to UTF-16. | ||
| 212 | for (size_t j = 0; j < FILENAME_LENGTH; ++j) { | ||
| 213 | entry.filename[j] = filename[j]; | ||
| 214 | if (!filename[j]) | ||
| 215 | break; | ||
| 216 | } | ||
| 217 | |||
| 218 | if (file.isDirectory) { | ||
| 219 | entry.file_size = 0; | ||
| 220 | entry.type = EntryType::Directory; | ||
| 221 | } else { | ||
| 222 | entry.file_size = file.size; | ||
| 223 | entry.type = EntryType::File; | ||
| 224 | } | ||
| 225 | |||
| 226 | ++entries_read; | ||
| 227 | ++children_iterator; | ||
| 228 | } | ||
| 229 | return entries_read; | ||
| 230 | } | ||
| 231 | |||
| 232 | u64 Disk_Directory::GetEntryCount() const { | ||
| 233 | // We convert the children iterator into a const_iterator to allow template argument deduction | ||
| 234 | // in std::distance. | ||
| 235 | std::vector<FileUtil::FSTEntry>::const_iterator current = children_iterator; | ||
| 236 | return std::distance(current, directory.children.end()); | ||
| 237 | } | ||
| 238 | |||
| 239 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/disk_filesystem.h b/src/core/file_sys/disk_filesystem.h deleted file mode 100644 index 591e39fda..000000000 --- a/src/core/file_sys/disk_filesystem.h +++ /dev/null | |||
| @@ -1,84 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstddef> | ||
| 8 | #include <memory> | ||
| 9 | #include <string> | ||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "common/file_util.h" | ||
| 12 | #include "core/file_sys/directory.h" | ||
| 13 | #include "core/file_sys/filesystem.h" | ||
| 14 | #include "core/file_sys/storage.h" | ||
| 15 | #include "core/hle/result.h" | ||
| 16 | |||
| 17 | namespace FileSys { | ||
| 18 | |||
| 19 | class Disk_FileSystem : public FileSystemBackend { | ||
| 20 | public: | ||
| 21 | explicit Disk_FileSystem(std::string base_directory) | ||
| 22 | : base_directory(std::move(base_directory)) {} | ||
| 23 | |||
| 24 | std::string GetName() const override; | ||
| 25 | |||
| 26 | ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path, | ||
| 27 | Mode mode) const override; | ||
| 28 | ResultCode DeleteFile(const std::string& path) const override; | ||
| 29 | ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const override; | ||
| 30 | ResultCode DeleteDirectory(const Path& path) const override; | ||
| 31 | ResultCode DeleteDirectoryRecursively(const Path& path) const override; | ||
| 32 | ResultCode CreateFile(const std::string& path, u64 size) const override; | ||
| 33 | ResultCode CreateDirectory(const std::string& path) const override; | ||
| 34 | ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override; | ||
| 35 | ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory( | ||
| 36 | const std::string& path) const override; | ||
| 37 | u64 GetFreeSpaceSize() const override; | ||
| 38 | ResultVal<EntryType> GetEntryType(const std::string& path) const override; | ||
| 39 | |||
| 40 | protected: | ||
| 41 | std::string base_directory; | ||
| 42 | }; | ||
| 43 | |||
| 44 | class Disk_Storage : public StorageBackend { | ||
| 45 | public: | ||
| 46 | explicit Disk_Storage(std::shared_ptr<FileUtil::IOFile> file) : file(std::move(file)) {} | ||
| 47 | |||
| 48 | ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; | ||
| 49 | ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; | ||
| 50 | u64 GetSize() const override; | ||
| 51 | bool SetSize(u64 size) const override; | ||
| 52 | bool Close() const override { | ||
| 53 | return false; | ||
| 54 | } | ||
| 55 | void Flush() const override {} | ||
| 56 | |||
| 57 | private: | ||
| 58 | std::shared_ptr<FileUtil::IOFile> file; | ||
| 59 | }; | ||
| 60 | |||
| 61 | class Disk_Directory : public DirectoryBackend { | ||
| 62 | public: | ||
| 63 | explicit Disk_Directory(const std::string& path); | ||
| 64 | |||
| 65 | ~Disk_Directory() override { | ||
| 66 | Close(); | ||
| 67 | } | ||
| 68 | |||
| 69 | u64 Read(const u64 count, Entry* entries) override; | ||
| 70 | u64 GetEntryCount() const override; | ||
| 71 | |||
| 72 | bool Close() const override { | ||
| 73 | return true; | ||
| 74 | } | ||
| 75 | |||
| 76 | protected: | ||
| 77 | FileUtil::FSTEntry directory; | ||
| 78 | |||
| 79 | // We need to remember the last entry we returned, so a subsequent call to Read will continue | ||
| 80 | // from the next one. This iterator will always point to the next unread entry. | ||
| 81 | std::vector<FileUtil::FSTEntry>::iterator children_iterator; | ||
| 82 | }; | ||
| 83 | |||
| 84 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h index 1f3b8fa84..a152dbd33 100644 --- a/src/core/file_sys/errors.h +++ b/src/core/file_sys/errors.h | |||
| @@ -11,7 +11,7 @@ namespace FileSys { | |||
| 11 | namespace ErrCodes { | 11 | namespace ErrCodes { |
| 12 | enum { | 12 | enum { |
| 13 | NotFound = 1, | 13 | NotFound = 1, |
| 14 | SaveDataNotFound = 1002, | 14 | TitleNotFound = 1002, |
| 15 | SdCardNotFound = 2001, | 15 | SdCardNotFound = 2001, |
| 16 | RomFSNotFound = 2520, | 16 | RomFSNotFound = 2520, |
| 17 | }; | 17 | }; |
diff --git a/src/core/file_sys/filesystem.cpp b/src/core/file_sys/filesystem.cpp deleted file mode 100644 index 82fdb3c46..000000000 --- a/src/core/file_sys/filesystem.cpp +++ /dev/null | |||
| @@ -1,122 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstddef> | ||
| 6 | #include <iomanip> | ||
| 7 | #include <sstream> | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "common/string_util.h" | ||
| 10 | #include "core/file_sys/filesystem.h" | ||
| 11 | #include "core/memory.h" | ||
| 12 | |||
| 13 | namespace FileSys { | ||
| 14 | |||
| 15 | Path::Path(LowPathType type, u32 size, u32 pointer) : type(type) { | ||
| 16 | switch (type) { | ||
| 17 | case Binary: { | ||
| 18 | binary.resize(size); | ||
| 19 | Memory::ReadBlock(pointer, binary.data(), binary.size()); | ||
| 20 | break; | ||
| 21 | } | ||
| 22 | |||
| 23 | case Char: { | ||
| 24 | string.resize(size - 1); // Data is always null-terminated. | ||
| 25 | Memory::ReadBlock(pointer, &string[0], string.size()); | ||
| 26 | break; | ||
| 27 | } | ||
| 28 | |||
| 29 | case Wchar: { | ||
| 30 | u16str.resize(size / 2 - 1); // Data is always null-terminated. | ||
| 31 | Memory::ReadBlock(pointer, &u16str[0], u16str.size() * sizeof(char16_t)); | ||
| 32 | break; | ||
| 33 | } | ||
| 34 | |||
| 35 | default: | ||
| 36 | break; | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | std::string Path::DebugStr() const { | ||
| 41 | switch (GetType()) { | ||
| 42 | case Invalid: | ||
| 43 | default: | ||
| 44 | return "[Invalid]"; | ||
| 45 | case Empty: | ||
| 46 | return "[Empty]"; | ||
| 47 | case Binary: { | ||
| 48 | std::stringstream res; | ||
| 49 | res << "[Binary: "; | ||
| 50 | for (unsigned byte : binary) | ||
| 51 | res << std::hex << std::setw(2) << std::setfill('0') << byte; | ||
| 52 | res << ']'; | ||
| 53 | return res.str(); | ||
| 54 | } | ||
| 55 | case Char: | ||
| 56 | return "[Char: " + AsString() + ']'; | ||
| 57 | case Wchar: | ||
| 58 | return "[Wchar: " + AsString() + ']'; | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | std::string Path::AsString() const { | ||
| 63 | switch (GetType()) { | ||
| 64 | case Char: | ||
| 65 | return string; | ||
| 66 | case Wchar: | ||
| 67 | return Common::UTF16ToUTF8(u16str); | ||
| 68 | case Empty: | ||
| 69 | return {}; | ||
| 70 | case Invalid: | ||
| 71 | case Binary: | ||
| 72 | default: | ||
| 73 | // TODO(yuriks): Add assert | ||
| 74 | LOG_ERROR(Service_FS, "LowPathType cannot be converted to string!"); | ||
| 75 | return {}; | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | std::u16string Path::AsU16Str() const { | ||
| 80 | switch (GetType()) { | ||
| 81 | case Char: | ||
| 82 | return Common::UTF8ToUTF16(string); | ||
| 83 | case Wchar: | ||
| 84 | return u16str; | ||
| 85 | case Empty: | ||
| 86 | return {}; | ||
| 87 | case Invalid: | ||
| 88 | case Binary: | ||
| 89 | // TODO(yuriks): Add assert | ||
| 90 | LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!"); | ||
| 91 | return {}; | ||
| 92 | } | ||
| 93 | |||
| 94 | UNREACHABLE(); | ||
| 95 | } | ||
| 96 | |||
| 97 | std::vector<u8> Path::AsBinary() const { | ||
| 98 | switch (GetType()) { | ||
| 99 | case Binary: | ||
| 100 | return binary; | ||
| 101 | case Char: | ||
| 102 | return std::vector<u8>(string.begin(), string.end()); | ||
| 103 | case Wchar: { | ||
| 104 | // use two u8 for each character of u16str | ||
| 105 | std::vector<u8> to_return(u16str.size() * 2); | ||
| 106 | for (size_t i = 0; i < u16str.size(); ++i) { | ||
| 107 | u16 tmp_char = u16str.at(i); | ||
| 108 | to_return[i * 2] = (tmp_char & 0xFF00) >> 8; | ||
| 109 | to_return[i * 2 + 1] = (tmp_char & 0x00FF); | ||
| 110 | } | ||
| 111 | return to_return; | ||
| 112 | } | ||
| 113 | case Empty: | ||
| 114 | return {}; | ||
| 115 | case Invalid: | ||
| 116 | default: | ||
| 117 | // TODO(yuriks): Add assert | ||
| 118 | LOG_ERROR(Service_FS, "LowPathType cannot be converted to binary!"); | ||
| 119 | return {}; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/filesystem.h b/src/core/file_sys/filesystem.h deleted file mode 100644 index 1a32a373b..000000000 --- a/src/core/file_sys/filesystem.h +++ /dev/null | |||
| @@ -1,170 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <string> | ||
| 9 | #include <utility> | ||
| 10 | #include <vector> | ||
| 11 | #include "common/bit_field.h" | ||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "common/swap.h" | ||
| 14 | #include "core/hle/result.h" | ||
| 15 | |||
| 16 | namespace FileSys { | ||
| 17 | |||
| 18 | class StorageBackend; | ||
| 19 | class DirectoryBackend; | ||
| 20 | |||
| 21 | // Path string type | ||
| 22 | enum LowPathType : u32 { | ||
| 23 | Invalid = 0, | ||
| 24 | Empty = 1, | ||
| 25 | Binary = 2, | ||
| 26 | Char = 3, | ||
| 27 | Wchar = 4, | ||
| 28 | }; | ||
| 29 | |||
| 30 | enum EntryType : u8 { | ||
| 31 | Directory = 0, | ||
| 32 | File = 1, | ||
| 33 | }; | ||
| 34 | |||
| 35 | enum class Mode : u32 { | ||
| 36 | Read = 1, | ||
| 37 | Write = 2, | ||
| 38 | Append = 4, | ||
| 39 | }; | ||
| 40 | |||
| 41 | class Path { | ||
| 42 | public: | ||
| 43 | Path() : type(Invalid) {} | ||
| 44 | Path(const char* path) : type(Char), string(path) {} | ||
| 45 | Path(std::vector<u8> binary_data) : type(Binary), binary(std::move(binary_data)) {} | ||
| 46 | Path(LowPathType type, u32 size, u32 pointer); | ||
| 47 | |||
| 48 | LowPathType GetType() const { | ||
| 49 | return type; | ||
| 50 | } | ||
| 51 | |||
| 52 | /** | ||
| 53 | * Gets the string representation of the path for debugging | ||
| 54 | * @return String representation of the path for debugging | ||
| 55 | */ | ||
| 56 | std::string DebugStr() const; | ||
| 57 | |||
| 58 | std::string AsString() const; | ||
| 59 | std::u16string AsU16Str() const; | ||
| 60 | std::vector<u8> AsBinary() const; | ||
| 61 | |||
| 62 | private: | ||
| 63 | LowPathType type; | ||
| 64 | std::vector<u8> binary; | ||
| 65 | std::string string; | ||
| 66 | std::u16string u16str; | ||
| 67 | }; | ||
| 68 | |||
| 69 | /// Parameters of the archive, as specified in the Create or Format call. | ||
| 70 | struct ArchiveFormatInfo { | ||
| 71 | u32_le total_size; ///< The pre-defined size of the archive. | ||
| 72 | u32_le number_directories; ///< The pre-defined number of directories in the archive. | ||
| 73 | u32_le number_files; ///< The pre-defined number of files in the archive. | ||
| 74 | u8 duplicate_data; ///< Whether the archive should duplicate the data. | ||
| 75 | }; | ||
| 76 | static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD"); | ||
| 77 | |||
| 78 | class FileSystemBackend : NonCopyable { | ||
| 79 | public: | ||
| 80 | virtual ~FileSystemBackend() {} | ||
| 81 | |||
| 82 | /** | ||
| 83 | * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) | ||
| 84 | */ | ||
| 85 | virtual std::string GetName() const = 0; | ||
| 86 | |||
| 87 | /** | ||
| 88 | * Create a file specified by its path | ||
| 89 | * @param path Path relative to the Archive | ||
| 90 | * @param size The size of the new file, filled with zeroes | ||
| 91 | * @return Result of the operation | ||
| 92 | */ | ||
| 93 | virtual ResultCode CreateFile(const std::string& path, u64 size) const = 0; | ||
| 94 | |||
| 95 | /** | ||
| 96 | * Delete a file specified by its path | ||
| 97 | * @param path Path relative to the archive | ||
| 98 | * @return Result of the operation | ||
| 99 | */ | ||
| 100 | virtual ResultCode DeleteFile(const std::string& path) const = 0; | ||
| 101 | |||
| 102 | /** | ||
| 103 | * Create a directory specified by its path | ||
| 104 | * @param path Path relative to the archive | ||
| 105 | * @return Result of the operation | ||
| 106 | */ | ||
| 107 | virtual ResultCode CreateDirectory(const std::string& path) const = 0; | ||
| 108 | |||
| 109 | /** | ||
| 110 | * Delete a directory specified by its path | ||
| 111 | * @param path Path relative to the archive | ||
| 112 | * @return Result of the operation | ||
| 113 | */ | ||
| 114 | virtual ResultCode DeleteDirectory(const Path& path) const = 0; | ||
| 115 | |||
| 116 | /** | ||
| 117 | * Delete a directory specified by its path and anything under it | ||
| 118 | * @param path Path relative to the archive | ||
| 119 | * @return Result of the operation | ||
| 120 | */ | ||
| 121 | virtual ResultCode DeleteDirectoryRecursively(const Path& path) const = 0; | ||
| 122 | |||
| 123 | /** | ||
| 124 | * Rename a File specified by its path | ||
| 125 | * @param src_path Source path relative to the archive | ||
| 126 | * @param dest_path Destination path relative to the archive | ||
| 127 | * @return Result of the operation | ||
| 128 | */ | ||
| 129 | virtual ResultCode RenameFile(const std::string& src_path, | ||
| 130 | const std::string& dest_path) const = 0; | ||
| 131 | |||
| 132 | /** | ||
| 133 | * Rename a Directory specified by its path | ||
| 134 | * @param src_path Source path relative to the archive | ||
| 135 | * @param dest_path Destination path relative to the archive | ||
| 136 | * @return Result of the operation | ||
| 137 | */ | ||
| 138 | virtual ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const = 0; | ||
| 139 | |||
| 140 | /** | ||
| 141 | * Open a file specified by its path, using the specified mode | ||
| 142 | * @param path Path relative to the archive | ||
| 143 | * @param mode Mode to open the file with | ||
| 144 | * @return Opened file, or error code | ||
| 145 | */ | ||
| 146 | virtual ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path, | ||
| 147 | Mode mode) const = 0; | ||
| 148 | |||
| 149 | /** | ||
| 150 | * Open a directory specified by its path | ||
| 151 | * @param path Path relative to the archive | ||
| 152 | * @return Opened directory, or error code | ||
| 153 | */ | ||
| 154 | virtual ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory( | ||
| 155 | const std::string& path) const = 0; | ||
| 156 | |||
| 157 | /** | ||
| 158 | * Get the free space | ||
| 159 | * @return The number of free bytes in the archive | ||
| 160 | */ | ||
| 161 | virtual u64 GetFreeSpaceSize() const = 0; | ||
| 162 | |||
| 163 | /** | ||
| 164 | * Get the type of the specified path | ||
| 165 | * @return The type of the specified path or error code | ||
| 166 | */ | ||
| 167 | virtual ResultVal<EntryType> GetEntryType(const std::string& path) const = 0; | ||
| 168 | }; | ||
| 169 | |||
| 170 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/mode.h b/src/core/file_sys/mode.h new file mode 100644 index 000000000..b4363152a --- /dev/null +++ b/src/core/file_sys/mode.h | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace FileSys { | ||
| 10 | |||
| 11 | enum class Mode : u32 { | ||
| 12 | Read = 1, | ||
| 13 | Write = 2, | ||
| 14 | Append = 4, | ||
| 15 | }; | ||
| 16 | |||
| 17 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp index 46d438aca..15b1fb946 100644 --- a/src/core/file_sys/partition_filesystem.cpp +++ b/src/core/file_sys/partition_filesystem.cpp | |||
| @@ -6,29 +6,30 @@ | |||
| 6 | #include "common/file_util.h" | 6 | #include "common/file_util.h" |
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "core/file_sys/partition_filesystem.h" | 8 | #include "core/file_sys/partition_filesystem.h" |
| 9 | #include "core/file_sys/vfs_offset.h" | ||
| 9 | #include "core/loader/loader.h" | 10 | #include "core/loader/loader.h" |
| 10 | 11 | ||
| 11 | namespace FileSys { | 12 | namespace FileSys { |
| 12 | 13 | ||
| 13 | Loader::ResultStatus PartitionFilesystem::Load(const std::string& file_path, size_t offset) { | 14 | PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) { |
| 14 | FileUtil::IOFile file(file_path, "rb"); | ||
| 15 | if (!file.IsOpen()) | ||
| 16 | return Loader::ResultStatus::Error; | ||
| 17 | |||
| 18 | // At least be as large as the header | 15 | // At least be as large as the header |
| 19 | if (file.GetSize() < sizeof(Header)) | 16 | if (file->GetSize() < sizeof(Header)) { |
| 20 | return Loader::ResultStatus::Error; | 17 | status = Loader::ResultStatus::Error; |
| 18 | return; | ||
| 19 | } | ||
| 21 | 20 | ||
| 22 | file.Seek(offset, SEEK_SET); | ||
| 23 | // For cartridges, HFSs can get very large, so we need to calculate the size up to | 21 | // For cartridges, HFSs can get very large, so we need to calculate the size up to |
| 24 | // the actual content itself instead of just blindly reading in the entire file. | 22 | // the actual content itself instead of just blindly reading in the entire file. |
| 25 | Header pfs_header; | 23 | Header pfs_header; |
| 26 | if (!file.ReadBytes(&pfs_header, sizeof(Header))) | 24 | if (sizeof(Header) != file->ReadObject(&pfs_header)) { |
| 27 | return Loader::ResultStatus::Error; | 25 | status = Loader::ResultStatus::Error; |
| 26 | return; | ||
| 27 | } | ||
| 28 | 28 | ||
| 29 | if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') && | 29 | if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') && |
| 30 | pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) { | 30 | pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) { |
| 31 | return Loader::ResultStatus::ErrorInvalidFormat; | 31 | status = Loader::ResultStatus::ErrorInvalidFormat; |
| 32 | return; | ||
| 32 | } | 33 | } |
| 33 | 34 | ||
| 34 | bool is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0'); | 35 | bool is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0'); |
| @@ -38,99 +39,86 @@ Loader::ResultStatus PartitionFilesystem::Load(const std::string& file_path, siz | |||
| 38 | sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size; | 39 | sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size; |
| 39 | 40 | ||
| 40 | // Actually read in now... | 41 | // Actually read in now... |
| 41 | file.Seek(offset, SEEK_SET); | 42 | std::vector<u8> file_data = file->ReadBytes(metadata_size); |
| 42 | std::vector<u8> file_data(metadata_size); | ||
| 43 | |||
| 44 | if (!file.ReadBytes(file_data.data(), metadata_size)) | ||
| 45 | return Loader::ResultStatus::Error; | ||
| 46 | 43 | ||
| 47 | Loader::ResultStatus result = Load(file_data); | 44 | if (file_data.size() != metadata_size) { |
| 48 | if (result != Loader::ResultStatus::Success) | 45 | status = Loader::ResultStatus::Error; |
| 49 | LOG_ERROR(Service_FS, "Failed to load PFS from file {}!", file_path); | 46 | return; |
| 50 | 47 | } | |
| 51 | return result; | ||
| 52 | } | ||
| 53 | 48 | ||
| 54 | Loader::ResultStatus PartitionFilesystem::Load(const std::vector<u8>& file_data, size_t offset) { | 49 | size_t total_size = file_data.size(); |
| 55 | size_t total_size = file_data.size() - offset; | 50 | if (total_size < sizeof(Header)) { |
| 56 | if (total_size < sizeof(Header)) | 51 | status = Loader::ResultStatus::Error; |
| 57 | return Loader::ResultStatus::Error; | 52 | return; |
| 53 | } | ||
| 58 | 54 | ||
| 59 | memcpy(&pfs_header, &file_data[offset], sizeof(Header)); | 55 | memcpy(&pfs_header, file_data.data(), sizeof(Header)); |
| 60 | if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') && | 56 | if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') && |
| 61 | pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) { | 57 | pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) { |
| 62 | return Loader::ResultStatus::ErrorInvalidFormat; | 58 | status = Loader::ResultStatus::ErrorInvalidFormat; |
| 59 | return; | ||
| 63 | } | 60 | } |
| 64 | 61 | ||
| 65 | is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0'); | 62 | is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0'); |
| 66 | 63 | ||
| 67 | size_t entries_offset = offset + sizeof(Header); | 64 | size_t entries_offset = sizeof(Header); |
| 68 | size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry); | ||
| 69 | size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size); | 65 | size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size); |
| 66 | content_offset = strtab_offset + pfs_header.strtab_size; | ||
| 70 | for (u16 i = 0; i < pfs_header.num_entries; i++) { | 67 | for (u16 i = 0; i < pfs_header.num_entries; i++) { |
| 71 | FileEntry entry; | 68 | FSEntry entry; |
| 72 | 69 | ||
| 73 | memcpy(&entry.fs_entry, &file_data[entries_offset + (i * entry_size)], sizeof(FSEntry)); | 70 | memcpy(&entry, &file_data[entries_offset + (i * entry_size)], sizeof(FSEntry)); |
| 74 | entry.name = std::string(reinterpret_cast<const char*>( | 71 | std::string name( |
| 75 | &file_data[strtab_offset + entry.fs_entry.strtab_offset])); | 72 | reinterpret_cast<const char*>(&file_data[strtab_offset + entry.strtab_offset])); |
| 76 | pfs_entries.push_back(std::move(entry)); | ||
| 77 | } | ||
| 78 | 73 | ||
| 79 | content_offset = strtab_offset + pfs_header.strtab_size; | 74 | pfs_files.emplace_back( |
| 75 | std::make_shared<OffsetVfsFile>(file, entry.size, content_offset + entry.offset, name)); | ||
| 76 | } | ||
| 80 | 77 | ||
| 81 | return Loader::ResultStatus::Success; | 78 | status = Loader::ResultStatus::Success; |
| 82 | } | 79 | } |
| 83 | 80 | ||
| 84 | u32 PartitionFilesystem::GetNumEntries() const { | 81 | Loader::ResultStatus PartitionFilesystem::GetStatus() const { |
| 85 | return pfs_header.num_entries; | 82 | return status; |
| 86 | } | 83 | } |
| 87 | 84 | ||
| 88 | u64 PartitionFilesystem::GetEntryOffset(u32 index) const { | 85 | std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const { |
| 89 | if (index > GetNumEntries()) | 86 | return pfs_files; |
| 90 | return 0; | ||
| 91 | |||
| 92 | return content_offset + pfs_entries[index].fs_entry.offset; | ||
| 93 | } | 87 | } |
| 94 | 88 | ||
| 95 | u64 PartitionFilesystem::GetEntrySize(u32 index) const { | 89 | std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const { |
| 96 | if (index > GetNumEntries()) | 90 | return {}; |
| 97 | return 0; | ||
| 98 | |||
| 99 | return pfs_entries[index].fs_entry.size; | ||
| 100 | } | 91 | } |
| 101 | 92 | ||
| 102 | std::string PartitionFilesystem::GetEntryName(u32 index) const { | 93 | std::string PartitionFilesystem::GetName() const { |
| 103 | if (index > GetNumEntries()) | 94 | return is_hfs ? "HFS0" : "PFS0"; |
| 104 | return ""; | 95 | } |
| 105 | 96 | ||
| 106 | return pfs_entries[index].name; | 97 | std::shared_ptr<VfsDirectory> PartitionFilesystem::GetParentDirectory() const { |
| 98 | // TODO(DarkLordZach): Add support for nested containers. | ||
| 99 | return nullptr; | ||
| 107 | } | 100 | } |
| 108 | 101 | ||
| 109 | u64 PartitionFilesystem::GetFileOffset(const std::string& name) const { | 102 | void PartitionFilesystem::PrintDebugInfo() const { |
| 103 | LOG_DEBUG(Service_FS, "Magic: {:.4}", pfs_header.magic); | ||
| 104 | LOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries); | ||
| 110 | for (u32 i = 0; i < pfs_header.num_entries; i++) { | 105 | for (u32 i = 0; i < pfs_header.num_entries; i++) { |
| 111 | if (pfs_entries[i].name == name) | 106 | LOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes, at 0x{:X})", i, |
| 112 | return content_offset + pfs_entries[i].fs_entry.offset; | 107 | pfs_files[i]->GetName(), pfs_files[i]->GetSize(), |
| 108 | dynamic_cast<OffsetVfsFile*>(pfs_files[i].get())->GetOffset()); | ||
| 113 | } | 109 | } |
| 114 | |||
| 115 | return 0; | ||
| 116 | } | 110 | } |
| 117 | 111 | ||
| 118 | u64 PartitionFilesystem::GetFileSize(const std::string& name) const { | 112 | bool PartitionFilesystem::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { |
| 119 | for (u32 i = 0; i < pfs_header.num_entries; i++) { | 113 | auto iter = std::find(pfs_files.begin(), pfs_files.end(), file); |
| 120 | if (pfs_entries[i].name == name) | 114 | if (iter == pfs_files.end()) |
| 121 | return pfs_entries[i].fs_entry.size; | 115 | return false; |
| 122 | } | ||
| 123 | 116 | ||
| 124 | return 0; | 117 | pfs_files[iter - pfs_files.begin()] = pfs_files.back(); |
| 125 | } | 118 | pfs_files.pop_back(); |
| 126 | 119 | ||
| 127 | void PartitionFilesystem::Print() const { | 120 | pfs_dirs.emplace_back(dir); |
| 128 | LOG_DEBUG(Service_FS, "Magic: {}", pfs_header.magic); | 121 | |
| 129 | LOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries); | 122 | return true; |
| 130 | for (u32 i = 0; i < pfs_header.num_entries; i++) { | ||
| 131 | LOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes, at 0x{:X})", i, | ||
| 132 | pfs_entries[i].name.c_str(), pfs_entries[i].fs_entry.size, | ||
| 133 | GetFileOffset(pfs_entries[i].name)); | ||
| 134 | } | ||
| 135 | } | 123 | } |
| 136 | } // namespace FileSys | 124 | } // namespace FileSys |
diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h index 9c5810cf1..9656b40bf 100644 --- a/src/core/file_sys/partition_filesystem.h +++ b/src/core/file_sys/partition_filesystem.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "common/common_funcs.h" | 10 | #include "common/common_funcs.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/swap.h" | 12 | #include "common/swap.h" |
| 13 | #include "core/file_sys/vfs.h" | ||
| 13 | 14 | ||
| 14 | namespace Loader { | 15 | namespace Loader { |
| 15 | enum class ResultStatus; | 16 | enum class ResultStatus; |
| @@ -21,19 +22,19 @@ namespace FileSys { | |||
| 21 | * Helper which implements an interface to parse PFS/HFS filesystems. | 22 | * Helper which implements an interface to parse PFS/HFS filesystems. |
| 22 | * Data can either be loaded from a file path or data with an offset into it. | 23 | * Data can either be loaded from a file path or data with an offset into it. |
| 23 | */ | 24 | */ |
| 24 | class PartitionFilesystem { | 25 | class PartitionFilesystem : public ReadOnlyVfsDirectory { |
| 25 | public: | 26 | public: |
| 26 | Loader::ResultStatus Load(const std::string& file_path, size_t offset = 0); | 27 | explicit PartitionFilesystem(std::shared_ptr<VfsFile> file); |
| 27 | Loader::ResultStatus Load(const std::vector<u8>& file_data, size_t offset = 0); | 28 | Loader::ResultStatus GetStatus() const; |
| 28 | 29 | ||
| 29 | u32 GetNumEntries() const; | 30 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; |
| 30 | u64 GetEntryOffset(u32 index) const; | 31 | std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; |
| 31 | u64 GetEntrySize(u32 index) const; | 32 | std::string GetName() const override; |
| 32 | std::string GetEntryName(u32 index) const; | 33 | std::shared_ptr<VfsDirectory> GetParentDirectory() const override; |
| 33 | u64 GetFileOffset(const std::string& name) const; | 34 | void PrintDebugInfo() const; |
| 34 | u64 GetFileSize(const std::string& name) const; | ||
| 35 | 35 | ||
| 36 | void Print() const; | 36 | protected: |
| 37 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 37 | 38 | ||
| 38 | private: | 39 | private: |
| 39 | struct Header { | 40 | struct Header { |
| @@ -72,16 +73,14 @@ private: | |||
| 72 | 73 | ||
| 73 | #pragma pack(pop) | 74 | #pragma pack(pop) |
| 74 | 75 | ||
| 75 | struct FileEntry { | 76 | Loader::ResultStatus status; |
| 76 | FSEntry fs_entry; | ||
| 77 | std::string name; | ||
| 78 | }; | ||
| 79 | 77 | ||
| 80 | Header pfs_header; | 78 | Header pfs_header; |
| 81 | bool is_hfs; | 79 | bool is_hfs; |
| 82 | size_t content_offset; | 80 | size_t content_offset; |
| 83 | 81 | ||
| 84 | std::vector<FileEntry> pfs_entries; | 82 | std::vector<VirtualFile> pfs_files; |
| 83 | std::vector<VirtualDir> pfs_dirs; | ||
| 85 | }; | 84 | }; |
| 86 | 85 | ||
| 87 | } // namespace FileSys | 86 | } // namespace FileSys |
diff --git a/src/core/file_sys/path_parser.cpp b/src/core/file_sys/path_parser.cpp deleted file mode 100644 index 5a89b02b8..000000000 --- a/src/core/file_sys/path_parser.cpp +++ /dev/null | |||
| @@ -1,98 +0,0 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <set> | ||
| 7 | #include "common/file_util.h" | ||
| 8 | #include "common/string_util.h" | ||
| 9 | #include "core/file_sys/path_parser.h" | ||
| 10 | |||
| 11 | namespace FileSys { | ||
| 12 | |||
| 13 | PathParser::PathParser(const Path& path) { | ||
| 14 | if (path.GetType() != LowPathType::Char && path.GetType() != LowPathType::Wchar) { | ||
| 15 | is_valid = false; | ||
| 16 | return; | ||
| 17 | } | ||
| 18 | |||
| 19 | auto path_string = path.AsString(); | ||
| 20 | if (path_string.size() == 0 || path_string[0] != '/') { | ||
| 21 | is_valid = false; | ||
| 22 | return; | ||
| 23 | } | ||
| 24 | |||
| 25 | // Filter out invalid characters for the host system. | ||
| 26 | // Although some of these characters are valid on 3DS, they are unlikely to be used by games. | ||
| 27 | if (std::find_if(path_string.begin(), path_string.end(), [](char c) { | ||
| 28 | static const std::set<char> invalid_chars{'<', '>', '\\', '|', ':', '\"', '*', '?'}; | ||
| 29 | return invalid_chars.find(c) != invalid_chars.end(); | ||
| 30 | }) != path_string.end()) { | ||
| 31 | is_valid = false; | ||
| 32 | return; | ||
| 33 | } | ||
| 34 | |||
| 35 | Common::SplitString(path_string, '/', path_sequence); | ||
| 36 | |||
| 37 | auto begin = path_sequence.begin(); | ||
| 38 | auto end = path_sequence.end(); | ||
| 39 | end = std::remove_if(begin, end, [](std::string& str) { return str == "" || str == "."; }); | ||
| 40 | path_sequence = std::vector<std::string>(begin, end); | ||
| 41 | |||
| 42 | // checks if the path is out of bounds. | ||
| 43 | int level = 0; | ||
| 44 | for (auto& node : path_sequence) { | ||
| 45 | if (node == "..") { | ||
| 46 | --level; | ||
| 47 | if (level < 0) { | ||
| 48 | is_valid = false; | ||
| 49 | return; | ||
| 50 | } | ||
| 51 | } else { | ||
| 52 | ++level; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | is_valid = true; | ||
| 57 | is_root = level == 0; | ||
| 58 | } | ||
| 59 | |||
| 60 | PathParser::HostStatus PathParser::GetHostStatus(const std::string& mount_point) const { | ||
| 61 | auto path = mount_point; | ||
| 62 | if (!FileUtil::IsDirectory(path)) | ||
| 63 | return InvalidMountPoint; | ||
| 64 | if (path_sequence.empty()) { | ||
| 65 | return DirectoryFound; | ||
| 66 | } | ||
| 67 | |||
| 68 | for (auto iter = path_sequence.begin(); iter != path_sequence.end() - 1; iter++) { | ||
| 69 | if (path.back() != '/') | ||
| 70 | path += '/'; | ||
| 71 | path += *iter; | ||
| 72 | |||
| 73 | if (!FileUtil::Exists(path)) | ||
| 74 | return PathNotFound; | ||
| 75 | if (FileUtil::IsDirectory(path)) | ||
| 76 | continue; | ||
| 77 | return FileInPath; | ||
| 78 | } | ||
| 79 | |||
| 80 | path += "/" + path_sequence.back(); | ||
| 81 | if (!FileUtil::Exists(path)) | ||
| 82 | return NotFound; | ||
| 83 | if (FileUtil::IsDirectory(path)) | ||
| 84 | return DirectoryFound; | ||
| 85 | return FileFound; | ||
| 86 | } | ||
| 87 | |||
| 88 | std::string PathParser::BuildHostPath(const std::string& mount_point) const { | ||
| 89 | std::string path = mount_point; | ||
| 90 | for (auto& node : path_sequence) { | ||
| 91 | if (path.back() != '/') | ||
| 92 | path += '/'; | ||
| 93 | path += node; | ||
| 94 | } | ||
| 95 | return path; | ||
| 96 | } | ||
| 97 | |||
| 98 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/path_parser.h b/src/core/file_sys/path_parser.h deleted file mode 100644 index 184f59d55..000000000 --- a/src/core/file_sys/path_parser.h +++ /dev/null | |||
| @@ -1,61 +0,0 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <string> | ||
| 8 | #include <vector> | ||
| 9 | #include "core/file_sys/filesystem.h" | ||
| 10 | |||
| 11 | namespace FileSys { | ||
| 12 | |||
| 13 | /** | ||
| 14 | * A helper class parsing and verifying a string-type Path. | ||
| 15 | * Every archives with a sub file system should use this class to parse the path argument and check | ||
| 16 | * the status of the file / directory in question on the host file system. | ||
| 17 | */ | ||
| 18 | class PathParser { | ||
| 19 | public: | ||
| 20 | explicit PathParser(const Path& path); | ||
| 21 | |||
| 22 | /** | ||
| 23 | * Checks if the Path is valid. | ||
| 24 | * This function should be called once a PathParser is constructed. | ||
| 25 | * A Path is valid if: | ||
| 26 | * - it is a string path (with type LowPathType::Char or LowPathType::Wchar), | ||
| 27 | * - it starts with "/" (this seems a hard requirement in real 3DS), | ||
| 28 | * - it doesn't contain invalid characters, and | ||
| 29 | * - it doesn't go out of the root directory using "..". | ||
| 30 | */ | ||
| 31 | bool IsValid() const { | ||
| 32 | return is_valid; | ||
| 33 | } | ||
| 34 | |||
| 35 | /// Checks if the Path represents the root directory. | ||
| 36 | bool IsRootDirectory() const { | ||
| 37 | return is_root; | ||
| 38 | } | ||
| 39 | |||
| 40 | enum HostStatus { | ||
| 41 | InvalidMountPoint, | ||
| 42 | PathNotFound, // "/a/b/c" when "a" doesn't exist | ||
| 43 | FileInPath, // "/a/b/c" when "a" is a file | ||
| 44 | FileFound, // "/a/b/c" when "c" is a file | ||
| 45 | DirectoryFound, // "/a/b/c" when "c" is a directory | ||
| 46 | NotFound // "/a/b/c" when "a/b/" exists but "c" doesn't exist | ||
| 47 | }; | ||
| 48 | |||
| 49 | /// Checks the status of the specified file / directory by the Path on the host file system. | ||
| 50 | HostStatus GetHostStatus(const std::string& mount_point) const; | ||
| 51 | |||
| 52 | /// Builds a full path on the host file system. | ||
| 53 | std::string BuildHostPath(const std::string& mount_point) const; | ||
| 54 | |||
| 55 | private: | ||
| 56 | std::vector<std::string> path_sequence; | ||
| 57 | bool is_valid{}; | ||
| 58 | bool is_root{}; | ||
| 59 | }; | ||
| 60 | |||
| 61 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 226811115..63d4b6e4f 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp | |||
| @@ -9,40 +9,29 @@ | |||
| 9 | 9 | ||
| 10 | namespace FileSys { | 10 | namespace FileSys { |
| 11 | 11 | ||
| 12 | Loader::ResultStatus ProgramMetadata::Load(const std::string& file_path) { | 12 | Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { |
| 13 | FileUtil::IOFile file(file_path, "rb"); | 13 | size_t total_size = static_cast<size_t>(file->GetSize()); |
| 14 | if (!file.IsOpen()) | 14 | if (total_size < sizeof(Header)) |
| 15 | return Loader::ResultStatus::Error; | 15 | return Loader::ResultStatus::Error; |
| 16 | 16 | ||
| 17 | std::vector<u8> file_data(file.GetSize()); | 17 | // TODO(DarkLordZach): Use ReadObject when Header/AcidHeader becomes trivially copyable. |
| 18 | 18 | std::vector<u8> npdm_header_data = file->ReadBytes(sizeof(Header)); | |
| 19 | if (!file.ReadBytes(file_data.data(), file_data.size())) | 19 | if (sizeof(Header) != npdm_header_data.size()) |
| 20 | return Loader::ResultStatus::Error; | 20 | return Loader::ResultStatus::Error; |
| 21 | std::memcpy(&npdm_header, npdm_header_data.data(), sizeof(Header)); | ||
| 21 | 22 | ||
| 22 | Loader::ResultStatus result = Load(file_data); | 23 | std::vector<u8> acid_header_data = file->ReadBytes(sizeof(AcidHeader), npdm_header.acid_offset); |
| 23 | if (result != Loader::ResultStatus::Success) | 24 | if (sizeof(AcidHeader) != acid_header_data.size()) |
| 24 | LOG_ERROR(Service_FS, "Failed to load NPDM from file {}!", file_path); | ||
| 25 | |||
| 26 | return result; | ||
| 27 | } | ||
| 28 | |||
| 29 | Loader::ResultStatus ProgramMetadata::Load(const std::vector<u8> file_data, size_t offset) { | ||
| 30 | size_t total_size = static_cast<size_t>(file_data.size() - offset); | ||
| 31 | if (total_size < sizeof(Header)) | ||
| 32 | return Loader::ResultStatus::Error; | 25 | return Loader::ResultStatus::Error; |
| 26 | std::memcpy(&acid_header, acid_header_data.data(), sizeof(AcidHeader)); | ||
| 33 | 27 | ||
| 34 | size_t header_offset = offset; | 28 | if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset)) |
| 35 | memcpy(&npdm_header, &file_data[offset], sizeof(Header)); | 29 | return Loader::ResultStatus::Error; |
| 36 | |||
| 37 | size_t aci_offset = header_offset + npdm_header.aci_offset; | ||
| 38 | size_t acid_offset = header_offset + npdm_header.acid_offset; | ||
| 39 | memcpy(&aci_header, &file_data[aci_offset], sizeof(AciHeader)); | ||
| 40 | memcpy(&acid_header, &file_data[acid_offset], sizeof(AcidHeader)); | ||
| 41 | 30 | ||
| 42 | size_t fac_offset = acid_offset + acid_header.fac_offset; | 31 | if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset)) |
| 43 | size_t fah_offset = aci_offset + aci_header.fah_offset; | 32 | return Loader::ResultStatus::Error; |
| 44 | memcpy(&acid_file_access, &file_data[fac_offset], sizeof(FileAccessControl)); | 33 | if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) |
| 45 | memcpy(&aci_file_access, &file_data[fah_offset], sizeof(FileAccessHeader)); | 34 | return Loader::ResultStatus::Error; |
| 46 | 35 | ||
| 47 | return Loader::ResultStatus::Success; | 36 | return Loader::ResultStatus::Success; |
| 48 | } | 37 | } |
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index b80a08485..06a7315db 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "common/bit_field.h" | 10 | #include "common/bit_field.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/swap.h" | 12 | #include "common/swap.h" |
| 13 | #include "partition_filesystem.h" | ||
| 13 | 14 | ||
| 14 | namespace Loader { | 15 | namespace Loader { |
| 15 | enum class ResultStatus; | 16 | enum class ResultStatus; |
| @@ -37,8 +38,7 @@ enum class ProgramFilePermission : u64 { | |||
| 37 | */ | 38 | */ |
| 38 | class ProgramMetadata { | 39 | class ProgramMetadata { |
| 39 | public: | 40 | public: |
| 40 | Loader::ResultStatus Load(const std::string& file_path); | 41 | Loader::ResultStatus Load(VirtualFile file); |
| 41 | Loader::ResultStatus Load(const std::vector<u8> file_data, size_t offset = 0); | ||
| 42 | 42 | ||
| 43 | bool Is64BitProgram() const; | 43 | bool Is64BitProgram() const; |
| 44 | ProgramAddressSpaceType GetAddressSpaceType() const; | 44 | ProgramAddressSpaceType GetAddressSpaceType() const; |
| @@ -51,6 +51,7 @@ public: | |||
| 51 | void Print() const; | 51 | void Print() const; |
| 52 | 52 | ||
| 53 | private: | 53 | private: |
| 54 | // TODO(DarkLordZach): BitField is not trivially copyable. | ||
| 54 | struct Header { | 55 | struct Header { |
| 55 | std::array<char, 4> magic; | 56 | std::array<char, 4> magic; |
| 56 | std::array<u8, 8> reserved; | 57 | std::array<u8, 8> reserved; |
| @@ -77,6 +78,7 @@ private: | |||
| 77 | 78 | ||
| 78 | static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong"); | 79 | static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong"); |
| 79 | 80 | ||
| 81 | // TODO(DarkLordZach): BitField is not trivially copyable. | ||
| 80 | struct AcidHeader { | 82 | struct AcidHeader { |
| 81 | std::array<u8, 0x100> signature; | 83 | std::array<u8, 0x100> signature; |
| 82 | std::array<u8, 0x100> nca_modulus; | 84 | std::array<u8, 0x100> nca_modulus; |
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index 946fc0452..54fbd3267 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp | |||
| @@ -7,21 +7,19 @@ | |||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "core/file_sys/romfs_factory.h" | 9 | #include "core/file_sys/romfs_factory.h" |
| 10 | #include "core/file_sys/romfs_filesystem.h" | ||
| 11 | 10 | ||
| 12 | namespace FileSys { | 11 | namespace FileSys { |
| 13 | 12 | ||
| 14 | RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) { | 13 | RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) { |
| 15 | // Load the RomFS from the app | 14 | // Load the RomFS from the app |
| 16 | if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) { | 15 | if (Loader::ResultStatus::Success != app_loader.ReadRomFS(file)) { |
| 17 | LOG_ERROR(Service_FS, "Unable to read RomFS!"); | 16 | LOG_ERROR(Service_FS, "Unable to read RomFS!"); |
| 18 | } | 17 | } |
| 19 | } | 18 | } |
| 20 | 19 | ||
| 21 | ResultVal<std::unique_ptr<FileSystemBackend>> RomFSFactory::Open(u64 title_id) { | 20 | ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id) { |
| 22 | // TODO(DarkLordZach): Use title id. | 21 | // TODO(DarkLordZach): Use title id. |
| 23 | auto archive = std::make_unique<RomFS_FileSystem>(romfs_file, data_offset, data_size); | 22 | return MakeResult<VirtualFile>(file); |
| 24 | return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive)); | ||
| 25 | } | 23 | } |
| 26 | 24 | ||
| 27 | } // namespace FileSys | 25 | } // namespace FileSys |
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h index c9e20c3ab..c19787cd4 100644 --- a/src/core/file_sys/romfs_factory.h +++ b/src/core/file_sys/romfs_factory.h | |||
| @@ -5,10 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <string> | ||
| 9 | #include <vector> | ||
| 10 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 11 | #include "core/file_sys/filesystem.h" | ||
| 12 | #include "core/hle/result.h" | 9 | #include "core/hle/result.h" |
| 13 | #include "core/loader/loader.h" | 10 | #include "core/loader/loader.h" |
| 14 | 11 | ||
| @@ -19,12 +16,10 @@ class RomFSFactory { | |||
| 19 | public: | 16 | public: |
| 20 | explicit RomFSFactory(Loader::AppLoader& app_loader); | 17 | explicit RomFSFactory(Loader::AppLoader& app_loader); |
| 21 | 18 | ||
| 22 | ResultVal<std::unique_ptr<FileSystemBackend>> Open(u64 title_id); | 19 | ResultVal<VirtualFile> Open(u64 title_id); |
| 23 | 20 | ||
| 24 | private: | 21 | private: |
| 25 | std::shared_ptr<FileUtil::IOFile> romfs_file; | 22 | VirtualFile file; |
| 26 | u64 data_offset; | ||
| 27 | u64 data_size; | ||
| 28 | }; | 23 | }; |
| 29 | 24 | ||
| 30 | } // namespace FileSys | 25 | } // namespace FileSys |
diff --git a/src/core/file_sys/romfs_filesystem.cpp b/src/core/file_sys/romfs_filesystem.cpp deleted file mode 100644 index 83162622b..000000000 --- a/src/core/file_sys/romfs_filesystem.cpp +++ /dev/null | |||
| @@ -1,110 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstring> | ||
| 6 | #include <memory> | ||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/file_sys/romfs_filesystem.h" | ||
| 10 | |||
| 11 | namespace FileSys { | ||
| 12 | |||
| 13 | std::string RomFS_FileSystem::GetName() const { | ||
| 14 | return "RomFS"; | ||
| 15 | } | ||
| 16 | |||
| 17 | ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const std::string& path, | ||
| 18 | Mode mode) const { | ||
| 19 | return MakeResult<std::unique_ptr<StorageBackend>>( | ||
| 20 | std::make_unique<RomFS_Storage>(romfs_file, data_offset, data_size)); | ||
| 21 | } | ||
| 22 | |||
| 23 | ResultCode RomFS_FileSystem::DeleteFile(const std::string& path) const { | ||
| 24 | LOG_CRITICAL(Service_FS, "Attempted to delete a file from an ROMFS archive ({}).", GetName()); | ||
| 25 | // TODO(bunnei): Use correct error code | ||
| 26 | return ResultCode(-1); | ||
| 27 | } | ||
| 28 | |||
| 29 | ResultCode RomFS_FileSystem::RenameFile(const std::string& src_path, | ||
| 30 | const std::string& dest_path) const { | ||
| 31 | LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive ({}).", GetName()); | ||
| 32 | // TODO(wwylele): Use correct error code | ||
| 33 | return ResultCode(-1); | ||
| 34 | } | ||
| 35 | |||
| 36 | ResultCode RomFS_FileSystem::DeleteDirectory(const Path& path) const { | ||
| 37 | LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive ({}).", | ||
| 38 | GetName()); | ||
| 39 | // TODO(wwylele): Use correct error code | ||
| 40 | return ResultCode(-1); | ||
| 41 | } | ||
| 42 | |||
| 43 | ResultCode RomFS_FileSystem::DeleteDirectoryRecursively(const Path& path) const { | ||
| 44 | LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive ({}).", | ||
| 45 | GetName()); | ||
| 46 | // TODO(wwylele): Use correct error code | ||
| 47 | return ResultCode(-1); | ||
| 48 | } | ||
| 49 | |||
| 50 | ResultCode RomFS_FileSystem::CreateFile(const std::string& path, u64 size) const { | ||
| 51 | LOG_CRITICAL(Service_FS, "Attempted to create a file in an ROMFS archive ({}).", GetName()); | ||
| 52 | // TODO(bunnei): Use correct error code | ||
| 53 | return ResultCode(-1); | ||
| 54 | } | ||
| 55 | |||
| 56 | ResultCode RomFS_FileSystem::CreateDirectory(const std::string& path) const { | ||
| 57 | LOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive ({}).", | ||
| 58 | GetName()); | ||
| 59 | // TODO(wwylele): Use correct error code | ||
| 60 | return ResultCode(-1); | ||
| 61 | } | ||
| 62 | |||
| 63 | ResultCode RomFS_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const { | ||
| 64 | LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive ({}).", GetName()); | ||
| 65 | // TODO(wwylele): Use correct error code | ||
| 66 | return ResultCode(-1); | ||
| 67 | } | ||
| 68 | |||
| 69 | ResultVal<std::unique_ptr<DirectoryBackend>> RomFS_FileSystem::OpenDirectory( | ||
| 70 | const std::string& path) const { | ||
| 71 | LOG_WARNING(Service_FS, "Opening Directory in a ROMFS archive"); | ||
| 72 | return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<ROMFSDirectory>()); | ||
| 73 | } | ||
| 74 | |||
| 75 | u64 RomFS_FileSystem::GetFreeSpaceSize() const { | ||
| 76 | LOG_WARNING(Service_FS, "Attempted to get the free space in an ROMFS archive"); | ||
| 77 | return 0; | ||
| 78 | } | ||
| 79 | |||
| 80 | ResultVal<FileSys::EntryType> RomFS_FileSystem::GetEntryType(const std::string& path) const { | ||
| 81 | LOG_CRITICAL(Service_FS, "Called within an ROMFS archive (path {}).", path); | ||
| 82 | // TODO(wwylele): Use correct error code | ||
| 83 | return ResultCode(-1); | ||
| 84 | } | ||
| 85 | |||
| 86 | ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* buffer) const { | ||
| 87 | LOG_TRACE(Service_FS, "called offset={}, length={}", offset, length); | ||
| 88 | romfs_file->Seek(data_offset + offset, SEEK_SET); | ||
| 89 | size_t read_length = (size_t)std::min((u64)length, data_size - offset); | ||
| 90 | |||
| 91 | return MakeResult<size_t>(romfs_file->ReadBytes(buffer, read_length)); | ||
| 92 | } | ||
| 93 | |||
| 94 | ResultVal<size_t> RomFS_Storage::Write(const u64 offset, const size_t length, const bool flush, | ||
| 95 | const u8* buffer) const { | ||
| 96 | LOG_ERROR(Service_FS, "Attempted to write to ROMFS file"); | ||
| 97 | // TODO(Subv): Find error code | ||
| 98 | return MakeResult<size_t>(0); | ||
| 99 | } | ||
| 100 | |||
| 101 | u64 RomFS_Storage::GetSize() const { | ||
| 102 | return data_size; | ||
| 103 | } | ||
| 104 | |||
| 105 | bool RomFS_Storage::SetSize(const u64 size) const { | ||
| 106 | LOG_ERROR(Service_FS, "Attempted to set the size of an ROMFS file"); | ||
| 107 | return false; | ||
| 108 | } | ||
| 109 | |||
| 110 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/romfs_filesystem.h b/src/core/file_sys/romfs_filesystem.h deleted file mode 100644 index ba9d85823..000000000 --- a/src/core/file_sys/romfs_filesystem.h +++ /dev/null | |||
| @@ -1,85 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstddef> | ||
| 8 | #include <memory> | ||
| 9 | #include <string> | ||
| 10 | #include <vector> | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/file_util.h" | ||
| 13 | #include "core/file_sys/directory.h" | ||
| 14 | #include "core/file_sys/filesystem.h" | ||
| 15 | #include "core/file_sys/storage.h" | ||
| 16 | #include "core/hle/result.h" | ||
| 17 | |||
| 18 | namespace FileSys { | ||
| 19 | |||
| 20 | /** | ||
| 21 | * Helper which implements an interface to deal with Switch .istorage ROMFS images used in some | ||
| 22 | * archives This should be subclassed by concrete archive types, which will provide the input data | ||
| 23 | * (load the raw ROMFS archive) and override any required methods | ||
| 24 | */ | ||
| 25 | class RomFS_FileSystem : public FileSystemBackend { | ||
| 26 | public: | ||
| 27 | RomFS_FileSystem(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) | ||
| 28 | : romfs_file(file), data_offset(offset), data_size(size) {} | ||
| 29 | |||
| 30 | std::string GetName() const override; | ||
| 31 | |||
| 32 | ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path, | ||
| 33 | Mode mode) const override; | ||
| 34 | ResultCode DeleteFile(const std::string& path) const override; | ||
| 35 | ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const override; | ||
| 36 | ResultCode DeleteDirectory(const Path& path) const override; | ||
| 37 | ResultCode DeleteDirectoryRecursively(const Path& path) const override; | ||
| 38 | ResultCode CreateFile(const std::string& path, u64 size) const override; | ||
| 39 | ResultCode CreateDirectory(const std::string& path) const override; | ||
| 40 | ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override; | ||
| 41 | ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory( | ||
| 42 | const std::string& path) const override; | ||
| 43 | u64 GetFreeSpaceSize() const override; | ||
| 44 | ResultVal<EntryType> GetEntryType(const std::string& path) const override; | ||
| 45 | |||
| 46 | protected: | ||
| 47 | std::shared_ptr<FileUtil::IOFile> romfs_file; | ||
| 48 | u64 data_offset; | ||
| 49 | u64 data_size; | ||
| 50 | }; | ||
| 51 | |||
| 52 | class RomFS_Storage : public StorageBackend { | ||
| 53 | public: | ||
| 54 | RomFS_Storage(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) | ||
| 55 | : romfs_file(file), data_offset(offset), data_size(size) {} | ||
| 56 | |||
| 57 | ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; | ||
| 58 | ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; | ||
| 59 | u64 GetSize() const override; | ||
| 60 | bool SetSize(u64 size) const override; | ||
| 61 | bool Close() const override { | ||
| 62 | return false; | ||
| 63 | } | ||
| 64 | void Flush() const override {} | ||
| 65 | |||
| 66 | private: | ||
| 67 | std::shared_ptr<FileUtil::IOFile> romfs_file; | ||
| 68 | u64 data_offset; | ||
| 69 | u64 data_size; | ||
| 70 | }; | ||
| 71 | |||
| 72 | class ROMFSDirectory : public DirectoryBackend { | ||
| 73 | public: | ||
| 74 | u64 Read(const u64 count, Entry* entries) override { | ||
| 75 | return 0; | ||
| 76 | } | ||
| 77 | u64 GetEntryCount() const override { | ||
| 78 | return 0; | ||
| 79 | } | ||
| 80 | bool Close() const override { | ||
| 81 | return false; | ||
| 82 | } | ||
| 83 | }; | ||
| 84 | |||
| 85 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 3ad37b28c..6a53b2b10 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "core/core.h" | 8 | #include "core/core.h" |
| 9 | #include "core/file_sys/disk_filesystem.h" | ||
| 10 | #include "core/file_sys/savedata_factory.h" | 9 | #include "core/file_sys/savedata_factory.h" |
| 11 | #include "core/hle/kernel/process.h" | 10 | #include "core/hle/kernel/process.h" |
| 12 | 11 | ||
| @@ -17,11 +16,9 @@ std::string SaveDataDescriptor::DebugInfo() { | |||
| 17 | static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id); | 16 | static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id); |
| 18 | } | 17 | } |
| 19 | 18 | ||
| 20 | SaveDataFactory::SaveDataFactory(std::string nand_directory) | 19 | SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {} |
| 21 | : nand_directory(std::move(nand_directory)) {} | ||
| 22 | 20 | ||
| 23 | ResultVal<std::unique_ptr<FileSystemBackend>> SaveDataFactory::Open(SaveDataSpaceId space, | 21 | ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) { |
| 24 | SaveDataDescriptor meta) { | ||
| 25 | if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { | 22 | if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { |
| 26 | if (meta.zero_1 != 0) { | 23 | if (meta.zero_1 != 0) { |
| 27 | LOG_WARNING(Service_FS, | 24 | LOG_WARNING(Service_FS, |
| @@ -56,28 +53,23 @@ ResultVal<std::unique_ptr<FileSystemBackend>> SaveDataFactory::Open(SaveDataSpac | |||
| 56 | // TODO(DarkLordZach): Try to not create when opening, there are dedicated create save methods. | 53 | // TODO(DarkLordZach): Try to not create when opening, there are dedicated create save methods. |
| 57 | // But, user_ids don't match so this works for now. | 54 | // But, user_ids don't match so this works for now. |
| 58 | 55 | ||
| 59 | if (!FileUtil::Exists(save_directory)) { | 56 | auto out = dir->GetDirectoryRelative(save_directory); |
| 57 | |||
| 58 | if (out == nullptr) { | ||
| 60 | // TODO(bunnei): This is a work-around to always create a save data directory if it does not | 59 | // TODO(bunnei): This is a work-around to always create a save data directory if it does not |
| 61 | // already exist. This is a hack, as we do not understand yet how this works on hardware. | 60 | // already exist. This is a hack, as we do not understand yet how this works on hardware. |
| 62 | // Without a save data directory, many games will assert on boot. This should not have any | 61 | // Without a save data directory, many games will assert on boot. This should not have any |
| 63 | // bad side-effects. | 62 | // bad side-effects. |
| 64 | FileUtil::CreateFullPath(save_directory); | 63 | out = dir->CreateDirectoryRelative(save_directory); |
| 65 | } | ||
| 66 | |||
| 67 | // TODO(DarkLordZach): For some reason, CreateFullPath doesn't create the last bit. Should be | ||
| 68 | // fixed with VFS. | ||
| 69 | if (!FileUtil::IsDirectory(save_directory)) { | ||
| 70 | FileUtil::CreateDir(save_directory); | ||
| 71 | } | 64 | } |
| 72 | 65 | ||
| 73 | // Return an error if the save data doesn't actually exist. | 66 | // Return an error if the save data doesn't actually exist. |
| 74 | if (!FileUtil::IsDirectory(save_directory)) { | 67 | if (out == nullptr) { |
| 75 | // TODO(Subv): Find out correct error code. | 68 | // TODO(Subv): Find out correct error code. |
| 76 | return ResultCode(-1); | 69 | return ResultCode(-1); |
| 77 | } | 70 | } |
| 78 | 71 | ||
| 79 | auto archive = std::make_unique<Disk_FileSystem>(save_directory); | 72 | return MakeResult<VirtualDir>(std::move(out)); |
| 80 | return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive)); | ||
| 81 | } | 73 | } |
| 82 | 74 | ||
| 83 | std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, | 75 | std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, |
| @@ -87,14 +79,14 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ | |||
| 87 | if (type == SaveDataType::SaveData && title_id == 0) | 79 | if (type == SaveDataType::SaveData && title_id == 0) |
| 88 | title_id = Core::CurrentProcess()->program_id; | 80 | title_id = Core::CurrentProcess()->program_id; |
| 89 | 81 | ||
| 90 | std::string prefix; | 82 | std::string out; |
| 91 | 83 | ||
| 92 | switch (space) { | 84 | switch (space) { |
| 93 | case SaveDataSpaceId::NandSystem: | 85 | case SaveDataSpaceId::NandSystem: |
| 94 | prefix = nand_directory + "system/save/"; | 86 | out = "/system/save/"; |
| 95 | break; | 87 | break; |
| 96 | case SaveDataSpaceId::NandUser: | 88 | case SaveDataSpaceId::NandUser: |
| 97 | prefix = nand_directory + "user/save/"; | 89 | out = "/user/save/"; |
| 98 | break; | 90 | break; |
| 99 | default: | 91 | default: |
| 100 | ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); | 92 | ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); |
| @@ -102,9 +94,9 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ | |||
| 102 | 94 | ||
| 103 | switch (type) { | 95 | switch (type) { |
| 104 | case SaveDataType::SystemSaveData: | 96 | case SaveDataType::SystemSaveData: |
| 105 | return fmt::format("{}{:016X}/{:016X}{:016X}", prefix, save_id, user_id[1], user_id[0]); | 97 | return fmt::format("{}{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]); |
| 106 | case SaveDataType::SaveData: | 98 | case SaveDataType::SaveData: |
| 107 | return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", prefix, 0, user_id[1], user_id[0], | 99 | return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], |
| 108 | title_id); | 100 | title_id); |
| 109 | default: | 101 | default: |
| 110 | ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); | 102 | ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); |
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index b96721ac0..53c69876f 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h | |||
| @@ -7,7 +7,6 @@ | |||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "core/file_sys/filesystem.h" | ||
| 11 | #include "core/hle/result.h" | 10 | #include "core/hle/result.h" |
| 12 | 11 | ||
| 13 | namespace FileSys { | 12 | namespace FileSys { |
| @@ -45,14 +44,12 @@ static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorr | |||
| 45 | /// File system interface to the SaveData archive | 44 | /// File system interface to the SaveData archive |
| 46 | class SaveDataFactory { | 45 | class SaveDataFactory { |
| 47 | public: | 46 | public: |
| 48 | explicit SaveDataFactory(std::string nand_directory); | 47 | explicit SaveDataFactory(VirtualDir dir); |
| 49 | 48 | ||
| 50 | ResultVal<std::unique_ptr<FileSystemBackend>> Open(SaveDataSpaceId space, | 49 | ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta); |
| 51 | SaveDataDescriptor meta); | ||
| 52 | 50 | ||
| 53 | private: | 51 | private: |
| 54 | std::string nand_directory; | 52 | VirtualDir dir; |
| 55 | std::string sd_directory; | ||
| 56 | 53 | ||
| 57 | std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, | 54 | std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, |
| 58 | u64 save_id) const; | 55 | u64 save_id) const; |
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp index ac6f2f971..c1edfcef3 100644 --- a/src/core/file_sys/sdmc_factory.cpp +++ b/src/core/file_sys/sdmc_factory.cpp | |||
| @@ -3,25 +3,15 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include "common/common_types.h" | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "common/string_util.h" | ||
| 9 | #include "core/core.h" | 6 | #include "core/core.h" |
| 10 | #include "core/file_sys/disk_filesystem.h" | ||
| 11 | #include "core/file_sys/sdmc_factory.h" | 7 | #include "core/file_sys/sdmc_factory.h" |
| 12 | 8 | ||
| 13 | namespace FileSys { | 9 | namespace FileSys { |
| 14 | 10 | ||
| 15 | SDMCFactory::SDMCFactory(std::string sd_directory) : sd_directory(std::move(sd_directory)) {} | 11 | SDMCFactory::SDMCFactory(VirtualDir dir) : dir(std::move(dir)) {} |
| 16 | 12 | ||
| 17 | ResultVal<std::unique_ptr<FileSystemBackend>> SDMCFactory::Open() { | 13 | ResultVal<VirtualDir> SDMCFactory::Open() { |
| 18 | // Create the SD Card directory if it doesn't already exist. | 14 | return MakeResult<VirtualDir>(dir); |
| 19 | if (!FileUtil::IsDirectory(sd_directory)) { | ||
| 20 | FileUtil::CreateFullPath(sd_directory); | ||
| 21 | } | ||
| 22 | |||
| 23 | auto archive = std::make_unique<Disk_FileSystem>(sd_directory); | ||
| 24 | return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive)); | ||
| 25 | } | 15 | } |
| 26 | 16 | ||
| 27 | } // namespace FileSys | 17 | } // namespace FileSys |
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h index 09bec7fce..9f0c75e84 100644 --- a/src/core/file_sys/sdmc_factory.h +++ b/src/core/file_sys/sdmc_factory.h | |||
| @@ -4,10 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | ||
| 8 | #include <string> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "core/file_sys/filesystem.h" | ||
| 11 | #include "core/hle/result.h" | 7 | #include "core/hle/result.h" |
| 12 | 8 | ||
| 13 | namespace FileSys { | 9 | namespace FileSys { |
| @@ -15,12 +11,12 @@ namespace FileSys { | |||
| 15 | /// File system interface to the SDCard archive | 11 | /// File system interface to the SDCard archive |
| 16 | class SDMCFactory { | 12 | class SDMCFactory { |
| 17 | public: | 13 | public: |
| 18 | explicit SDMCFactory(std::string sd_directory); | 14 | explicit SDMCFactory(VirtualDir dir); |
| 19 | 15 | ||
| 20 | ResultVal<std::unique_ptr<FileSystemBackend>> Open(); | 16 | ResultVal<VirtualDir> Open(); |
| 21 | 17 | ||
| 22 | private: | 18 | private: |
| 23 | std::string sd_directory; | 19 | VirtualDir dir; |
| 24 | }; | 20 | }; |
| 25 | 21 | ||
| 26 | } // namespace FileSys | 22 | } // namespace FileSys |
diff --git a/src/core/file_sys/storage.h b/src/core/file_sys/storage.h deleted file mode 100644 index 2a6811831..000000000 --- a/src/core/file_sys/storage.h +++ /dev/null | |||
| @@ -1,63 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstddef> | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "core/hle/result.h" | ||
| 10 | |||
| 11 | namespace FileSys { | ||
| 12 | |||
| 13 | class StorageBackend : NonCopyable { | ||
| 14 | public: | ||
| 15 | StorageBackend() {} | ||
| 16 | virtual ~StorageBackend() {} | ||
| 17 | |||
| 18 | /** | ||
| 19 | * Read data from the file | ||
| 20 | * @param offset Offset in bytes to start reading data from | ||
| 21 | * @param length Length in bytes of data to read from file | ||
| 22 | * @param buffer Buffer to read data into | ||
| 23 | * @return Number of bytes read, or error code | ||
| 24 | */ | ||
| 25 | virtual ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const = 0; | ||
| 26 | |||
| 27 | /** | ||
| 28 | * Write data to the file | ||
| 29 | * @param offset Offset in bytes to start writing data to | ||
| 30 | * @param length Length in bytes of data to write to file | ||
| 31 | * @param flush The flush parameters (0 == do not flush) | ||
| 32 | * @param buffer Buffer to read data from | ||
| 33 | * @return Number of bytes written, or error code | ||
| 34 | */ | ||
| 35 | virtual ResultVal<size_t> Write(u64 offset, size_t length, bool flush, | ||
| 36 | const u8* buffer) const = 0; | ||
| 37 | |||
| 38 | /** | ||
| 39 | * Flushes the file | ||
| 40 | */ | ||
| 41 | virtual void Flush() const = 0; | ||
| 42 | |||
| 43 | /** | ||
| 44 | * Set the size of the file in bytes | ||
| 45 | * @param size New size of the file | ||
| 46 | * @return true if successful | ||
| 47 | */ | ||
| 48 | virtual bool SetSize(u64 size) const = 0; | ||
| 49 | |||
| 50 | /** | ||
| 51 | * Get the size of the file in bytes | ||
| 52 | * @return Size of the file in bytes | ||
| 53 | */ | ||
| 54 | virtual u64 GetSize() const = 0; | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Close the file | ||
| 58 | * @return true if the file closed correctly | ||
| 59 | */ | ||
| 60 | virtual bool Close() const = 0; | ||
| 61 | }; | ||
| 62 | |||
| 63 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp new file mode 100644 index 000000000..f859ef33f --- /dev/null +++ b/src/core/file_sys/vfs.cpp | |||
| @@ -0,0 +1,238 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <numeric> | ||
| 7 | #include "common/file_util.h" | ||
| 8 | #include "common/logging/backend.h" | ||
| 9 | #include "core/file_sys/vfs.h" | ||
| 10 | |||
| 11 | namespace FileSys { | ||
| 12 | |||
| 13 | VfsFile::~VfsFile() = default; | ||
| 14 | |||
| 15 | std::string VfsFile::GetExtension() const { | ||
| 16 | return FileUtil::GetExtensionFromFilename(GetName()); | ||
| 17 | } | ||
| 18 | |||
| 19 | VfsDirectory::~VfsDirectory() = default; | ||
| 20 | |||
| 21 | boost::optional<u8> VfsFile::ReadByte(size_t offset) const { | ||
| 22 | u8 out{}; | ||
| 23 | size_t size = Read(&out, 1, offset); | ||
| 24 | if (size == 1) | ||
| 25 | return out; | ||
| 26 | |||
| 27 | return boost::none; | ||
| 28 | } | ||
| 29 | |||
| 30 | std::vector<u8> VfsFile::ReadBytes(size_t size, size_t offset) const { | ||
| 31 | std::vector<u8> out(size); | ||
| 32 | size_t read_size = Read(out.data(), size, offset); | ||
| 33 | out.resize(read_size); | ||
| 34 | return out; | ||
| 35 | } | ||
| 36 | |||
| 37 | std::vector<u8> VfsFile::ReadAllBytes() const { | ||
| 38 | return ReadBytes(GetSize()); | ||
| 39 | } | ||
| 40 | |||
| 41 | bool VfsFile::WriteByte(u8 data, size_t offset) { | ||
| 42 | return Write(&data, 1, offset) == 1; | ||
| 43 | } | ||
| 44 | |||
| 45 | size_t VfsFile::WriteBytes(std::vector<u8> data, size_t offset) { | ||
| 46 | return Write(data.data(), data.size(), offset); | ||
| 47 | } | ||
| 48 | |||
| 49 | std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(const std::string& path) const { | ||
| 50 | auto vec = FileUtil::SplitPathComponents(path); | ||
| 51 | vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), | ||
| 52 | vec.end()); | ||
| 53 | if (vec.empty()) | ||
| 54 | return nullptr; | ||
| 55 | if (vec.size() == 1) | ||
| 56 | return GetFile(vec[0]); | ||
| 57 | auto dir = GetSubdirectory(vec[0]); | ||
| 58 | for (size_t component = 1; component < vec.size() - 1; ++component) { | ||
| 59 | if (dir == nullptr) | ||
| 60 | return nullptr; | ||
| 61 | dir = dir->GetSubdirectory(vec[component]); | ||
| 62 | } | ||
| 63 | if (dir == nullptr) | ||
| 64 | return nullptr; | ||
| 65 | return dir->GetFile(vec.back()); | ||
| 66 | } | ||
| 67 | |||
| 68 | std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(const std::string& path) const { | ||
| 69 | if (IsRoot()) | ||
| 70 | return GetFileRelative(path); | ||
| 71 | |||
| 72 | return GetParentDirectory()->GetFileAbsolute(path); | ||
| 73 | } | ||
| 74 | |||
| 75 | std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(const std::string& path) const { | ||
| 76 | auto vec = FileUtil::SplitPathComponents(path); | ||
| 77 | vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), | ||
| 78 | vec.end()); | ||
| 79 | if (vec.empty()) | ||
| 80 | // TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently | ||
| 81 | // because of const-ness | ||
| 82 | return nullptr; | ||
| 83 | auto dir = GetSubdirectory(vec[0]); | ||
| 84 | for (size_t component = 1; component < vec.size(); ++component) { | ||
| 85 | if (dir == nullptr) | ||
| 86 | return nullptr; | ||
| 87 | dir = dir->GetSubdirectory(vec[component]); | ||
| 88 | } | ||
| 89 | return dir; | ||
| 90 | } | ||
| 91 | |||
| 92 | std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryAbsolute(const std::string& path) const { | ||
| 93 | if (IsRoot()) | ||
| 94 | return GetDirectoryRelative(path); | ||
| 95 | |||
| 96 | return GetParentDirectory()->GetDirectoryAbsolute(path); | ||
| 97 | } | ||
| 98 | |||
| 99 | std::shared_ptr<VfsFile> VfsDirectory::GetFile(const std::string& name) const { | ||
| 100 | const auto& files = GetFiles(); | ||
| 101 | const auto iter = std::find_if(files.begin(), files.end(), | ||
| 102 | [&name](const auto& file1) { return name == file1->GetName(); }); | ||
| 103 | return iter == files.end() ? nullptr : *iter; | ||
| 104 | } | ||
| 105 | |||
| 106 | std::shared_ptr<VfsDirectory> VfsDirectory::GetSubdirectory(const std::string& name) const { | ||
| 107 | const auto& subs = GetSubdirectories(); | ||
| 108 | const auto iter = std::find_if(subs.begin(), subs.end(), | ||
| 109 | [&name](const auto& file1) { return name == file1->GetName(); }); | ||
| 110 | return iter == subs.end() ? nullptr : *iter; | ||
| 111 | } | ||
| 112 | |||
| 113 | bool VfsDirectory::IsRoot() const { | ||
| 114 | return GetParentDirectory() == nullptr; | ||
| 115 | } | ||
| 116 | |||
| 117 | size_t VfsDirectory::GetSize() const { | ||
| 118 | const auto& files = GetFiles(); | ||
| 119 | const auto file_total = | ||
| 120 | std::accumulate(files.begin(), files.end(), 0ull, | ||
| 121 | [](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); }); | ||
| 122 | |||
| 123 | const auto& sub_dir = GetSubdirectories(); | ||
| 124 | const auto subdir_total = | ||
| 125 | std::accumulate(sub_dir.begin(), sub_dir.end(), 0ull, | ||
| 126 | [](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); }); | ||
| 127 | |||
| 128 | return file_total + subdir_total; | ||
| 129 | } | ||
| 130 | |||
| 131 | std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(const std::string& path) { | ||
| 132 | auto vec = FileUtil::SplitPathComponents(path); | ||
| 133 | vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), | ||
| 134 | vec.end()); | ||
| 135 | if (vec.empty()) | ||
| 136 | return nullptr; | ||
| 137 | if (vec.size() == 1) | ||
| 138 | return CreateFile(vec[0]); | ||
| 139 | auto dir = GetSubdirectory(vec[0]); | ||
| 140 | if (dir == nullptr) { | ||
| 141 | dir = CreateSubdirectory(vec[0]); | ||
| 142 | if (dir == nullptr) | ||
| 143 | return nullptr; | ||
| 144 | } | ||
| 145 | |||
| 146 | return dir->CreateFileRelative(FileUtil::GetPathWithoutTop(path)); | ||
| 147 | } | ||
| 148 | |||
| 149 | std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(const std::string& path) { | ||
| 150 | if (IsRoot()) | ||
| 151 | return CreateFileRelative(path); | ||
| 152 | return GetParentDirectory()->CreateFileAbsolute(path); | ||
| 153 | } | ||
| 154 | |||
| 155 | std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(const std::string& path) { | ||
| 156 | auto vec = FileUtil::SplitPathComponents(path); | ||
| 157 | vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), | ||
| 158 | vec.end()); | ||
| 159 | if (vec.empty()) | ||
| 160 | return nullptr; | ||
| 161 | if (vec.size() == 1) | ||
| 162 | return CreateSubdirectory(vec[0]); | ||
| 163 | auto dir = GetSubdirectory(vec[0]); | ||
| 164 | if (dir == nullptr) { | ||
| 165 | dir = CreateSubdirectory(vec[0]); | ||
| 166 | if (dir == nullptr) | ||
| 167 | return nullptr; | ||
| 168 | } | ||
| 169 | return dir->CreateDirectoryRelative(FileUtil::GetPathWithoutTop(path)); | ||
| 170 | } | ||
| 171 | |||
| 172 | std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(const std::string& path) { | ||
| 173 | if (IsRoot()) | ||
| 174 | return CreateDirectoryRelative(path); | ||
| 175 | return GetParentDirectory()->CreateDirectoryAbsolute(path); | ||
| 176 | } | ||
| 177 | |||
| 178 | bool VfsDirectory::DeleteSubdirectoryRecursive(const std::string& name) { | ||
| 179 | auto dir = GetSubdirectory(name); | ||
| 180 | if (dir == nullptr) | ||
| 181 | return false; | ||
| 182 | |||
| 183 | bool success = true; | ||
| 184 | for (const auto& file : dir->GetFiles()) { | ||
| 185 | if (!DeleteFile(file->GetName())) | ||
| 186 | success = false; | ||
| 187 | } | ||
| 188 | |||
| 189 | for (const auto& sdir : dir->GetSubdirectories()) { | ||
| 190 | if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) | ||
| 191 | success = false; | ||
| 192 | } | ||
| 193 | |||
| 194 | return success; | ||
| 195 | } | ||
| 196 | |||
| 197 | bool VfsDirectory::Copy(const std::string& src, const std::string& dest) { | ||
| 198 | const auto f1 = GetFile(src); | ||
| 199 | auto f2 = CreateFile(dest); | ||
| 200 | if (f1 == nullptr || f2 == nullptr) | ||
| 201 | return false; | ||
| 202 | |||
| 203 | if (!f2->Resize(f1->GetSize())) { | ||
| 204 | DeleteFile(dest); | ||
| 205 | return false; | ||
| 206 | } | ||
| 207 | |||
| 208 | return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize(); | ||
| 209 | } | ||
| 210 | |||
| 211 | bool ReadOnlyVfsDirectory::IsWritable() const { | ||
| 212 | return false; | ||
| 213 | } | ||
| 214 | |||
| 215 | bool ReadOnlyVfsDirectory::IsReadable() const { | ||
| 216 | return true; | ||
| 217 | } | ||
| 218 | |||
| 219 | std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateSubdirectory(const std::string& name) { | ||
| 220 | return nullptr; | ||
| 221 | } | ||
| 222 | |||
| 223 | std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFile(const std::string& name) { | ||
| 224 | return nullptr; | ||
| 225 | } | ||
| 226 | |||
| 227 | bool ReadOnlyVfsDirectory::DeleteSubdirectory(const std::string& name) { | ||
| 228 | return false; | ||
| 229 | } | ||
| 230 | |||
| 231 | bool ReadOnlyVfsDirectory::DeleteFile(const std::string& name) { | ||
| 232 | return false; | ||
| 233 | } | ||
| 234 | |||
| 235 | bool ReadOnlyVfsDirectory::Rename(const std::string& name) { | ||
| 236 | return false; | ||
| 237 | } | ||
| 238 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h new file mode 100644 index 000000000..a5213e0cc --- /dev/null +++ b/src/core/file_sys/vfs.h | |||
| @@ -0,0 +1,237 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <string> | ||
| 9 | #include <type_traits> | ||
| 10 | #include <vector> | ||
| 11 | #include "boost/optional.hpp" | ||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "common/file_util.h" | ||
| 14 | |||
| 15 | namespace FileSys { | ||
| 16 | struct VfsFile; | ||
| 17 | struct VfsDirectory; | ||
| 18 | |||
| 19 | // Convenience typedefs to use VfsDirectory and VfsFile | ||
| 20 | using VirtualDir = std::shared_ptr<FileSys::VfsDirectory>; | ||
| 21 | using VirtualFile = std::shared_ptr<FileSys::VfsFile>; | ||
| 22 | |||
| 23 | // A class representing a file in an abstract filesystem. | ||
| 24 | struct VfsFile : NonCopyable { | ||
| 25 | virtual ~VfsFile(); | ||
| 26 | |||
| 27 | // Retrieves the file name. | ||
| 28 | virtual std::string GetName() const = 0; | ||
| 29 | // Retrieves the extension of the file name. | ||
| 30 | virtual std::string GetExtension() const; | ||
| 31 | // Retrieves the size of the file. | ||
| 32 | virtual size_t GetSize() const = 0; | ||
| 33 | // Resizes the file to new_size. Returns whether or not the operation was successful. | ||
| 34 | virtual bool Resize(size_t new_size) = 0; | ||
| 35 | // Gets a pointer to the directory containing this file, returning nullptr if there is none. | ||
| 36 | virtual std::shared_ptr<VfsDirectory> GetContainingDirectory() const = 0; | ||
| 37 | |||
| 38 | // Returns whether or not the file can be written to. | ||
| 39 | virtual bool IsWritable() const = 0; | ||
| 40 | // Returns whether or not the file can be read from. | ||
| 41 | virtual bool IsReadable() const = 0; | ||
| 42 | |||
| 43 | // The primary method of reading from the file. Reads length bytes into data starting at offset | ||
| 44 | // into file. Returns number of bytes successfully read. | ||
| 45 | virtual size_t Read(u8* data, size_t length, size_t offset = 0) const = 0; | ||
| 46 | // The primary method of writing to the file. Writes length bytes from data starting at offset | ||
| 47 | // into file. Returns number of bytes successfully written. | ||
| 48 | virtual size_t Write(const u8* data, size_t length, size_t offset = 0) = 0; | ||
| 49 | |||
| 50 | // Reads exactly one byte at the offset provided, returning boost::none on error. | ||
| 51 | virtual boost::optional<u8> ReadByte(size_t offset = 0) const; | ||
| 52 | // Reads size bytes starting at offset in file into a vector. | ||
| 53 | virtual std::vector<u8> ReadBytes(size_t size, size_t offset = 0) const; | ||
| 54 | // Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(), | ||
| 55 | // 0)' | ||
| 56 | virtual std::vector<u8> ReadAllBytes() const; | ||
| 57 | |||
| 58 | // Reads an array of type T, size number_elements starting at offset. | ||
| 59 | // Returns the number of bytes (sizeof(T)*number_elements) read successfully. | ||
| 60 | template <typename T> | ||
| 61 | size_t ReadArray(T* data, size_t number_elements, size_t offset = 0) const { | ||
| 62 | static_assert(std::is_trivially_copyable<T>::value, | ||
| 63 | "Data type must be trivially copyable."); | ||
| 64 | |||
| 65 | return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset); | ||
| 66 | } | ||
| 67 | |||
| 68 | // Reads size bytes into the memory starting at data starting at offset into the file. | ||
| 69 | // Returns the number of bytes read successfully. | ||
| 70 | template <typename T> | ||
| 71 | size_t ReadBytes(T* data, size_t size, size_t offset = 0) const { | ||
| 72 | static_assert(std::is_trivially_copyable<T>::value, | ||
| 73 | "Data type must be trivially copyable."); | ||
| 74 | return Read(reinterpret_cast<u8*>(data), size, offset); | ||
| 75 | } | ||
| 76 | |||
| 77 | // Reads one object of type T starting at offset in file. | ||
| 78 | // Returns the number of bytes read successfully (sizeof(T)). | ||
| 79 | template <typename T> | ||
| 80 | size_t ReadObject(T* data, size_t offset = 0) const { | ||
| 81 | static_assert(std::is_trivially_copyable<T>::value, | ||
| 82 | "Data type must be trivially copyable."); | ||
| 83 | return Read(reinterpret_cast<u8*>(data), sizeof(T), offset); | ||
| 84 | } | ||
| 85 | |||
| 86 | // Writes exactly one byte to offset in file and retuns whether or not the byte was written | ||
| 87 | // successfully. | ||
| 88 | virtual bool WriteByte(u8 data, size_t offset = 0); | ||
| 89 | // Writes a vector of bytes to offset in file and returns the number of bytes successfully | ||
| 90 | // written. | ||
| 91 | virtual size_t WriteBytes(std::vector<u8> data, size_t offset = 0); | ||
| 92 | |||
| 93 | // Writes an array of type T, size number_elements to offset in file. | ||
| 94 | // Returns the number of bytes (sizeof(T)*number_elements) written successfully. | ||
| 95 | template <typename T> | ||
| 96 | size_t WriteArray(T* data, size_t number_elements, size_t offset = 0) { | ||
| 97 | static_assert(std::is_trivially_copyable<T>::value, | ||
| 98 | "Data type must be trivially copyable."); | ||
| 99 | |||
| 100 | return Write(data, number_elements * sizeof(T), offset); | ||
| 101 | } | ||
| 102 | |||
| 103 | // Writes size bytes starting at memory location data to offset in file. | ||
| 104 | // Returns the number of bytes written successfully. | ||
| 105 | template <typename T> | ||
| 106 | size_t WriteBytes(T* data, size_t size, size_t offset = 0) { | ||
| 107 | static_assert(std::is_trivially_copyable<T>::value, | ||
| 108 | "Data type must be trivially copyable."); | ||
| 109 | return Write(reinterpret_cast<u8*>(data), size, offset); | ||
| 110 | } | ||
| 111 | |||
| 112 | // Writes one object of type T to offset in file. | ||
| 113 | // Returns the number of bytes written successfully (sizeof(T)). | ||
| 114 | template <typename T> | ||
| 115 | size_t WriteObject(const T& data, size_t offset = 0) { | ||
| 116 | static_assert(std::is_trivially_copyable<T>::value, | ||
| 117 | "Data type must be trivially copyable."); | ||
| 118 | return Write(&data, sizeof(T), offset); | ||
| 119 | } | ||
| 120 | |||
| 121 | // Renames the file to name. Returns whether or not the operation was successsful. | ||
| 122 | virtual bool Rename(const std::string& name) = 0; | ||
| 123 | }; | ||
| 124 | |||
| 125 | // A class representing a directory in an abstract filesystem. | ||
| 126 | struct VfsDirectory : NonCopyable { | ||
| 127 | virtual ~VfsDirectory(); | ||
| 128 | |||
| 129 | // Retrives the file located at path as if the current directory was root. Returns nullptr if | ||
| 130 | // not found. | ||
| 131 | virtual std::shared_ptr<VfsFile> GetFileRelative(const std::string& path) const; | ||
| 132 | // Calls GetFileRelative(path) on the root of the current directory. | ||
| 133 | virtual std::shared_ptr<VfsFile> GetFileAbsolute(const std::string& path) const; | ||
| 134 | |||
| 135 | // Retrives the directory located at path as if the current directory was root. Returns nullptr | ||
| 136 | // if not found. | ||
| 137 | virtual std::shared_ptr<VfsDirectory> GetDirectoryRelative(const std::string& path) const; | ||
| 138 | // Calls GetDirectoryRelative(path) on the root of the current directory. | ||
| 139 | virtual std::shared_ptr<VfsDirectory> GetDirectoryAbsolute(const std::string& path) const; | ||
| 140 | |||
| 141 | // Returns a vector containing all of the files in this directory. | ||
| 142 | virtual std::vector<std::shared_ptr<VfsFile>> GetFiles() const = 0; | ||
| 143 | // Returns the file with filename matching name. Returns nullptr if directory dosen't have a | ||
| 144 | // file with name. | ||
| 145 | virtual std::shared_ptr<VfsFile> GetFile(const std::string& name) const; | ||
| 146 | |||
| 147 | // Returns a vector containing all of the subdirectories in this directory. | ||
| 148 | virtual std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const = 0; | ||
| 149 | // Returns the directory with name matching name. Returns nullptr if directory dosen't have a | ||
| 150 | // directory with name. | ||
| 151 | virtual std::shared_ptr<VfsDirectory> GetSubdirectory(const std::string& name) const; | ||
| 152 | |||
| 153 | // Returns whether or not the directory can be written to. | ||
| 154 | virtual bool IsWritable() const = 0; | ||
| 155 | // Returns whether of not the directory can be read from. | ||
| 156 | virtual bool IsReadable() const = 0; | ||
| 157 | |||
| 158 | // Returns whether or not the directory is the root of the current file tree. | ||
| 159 | virtual bool IsRoot() const; | ||
| 160 | |||
| 161 | // Returns the name of the directory. | ||
| 162 | virtual std::string GetName() const = 0; | ||
| 163 | // Returns the total size of all files and subdirectories in this directory. | ||
| 164 | virtual size_t GetSize() const; | ||
| 165 | // Returns the parent directory of this directory. Returns nullptr if this directory is root or | ||
| 166 | // has no parent. | ||
| 167 | virtual std::shared_ptr<VfsDirectory> GetParentDirectory() const = 0; | ||
| 168 | |||
| 169 | // Creates a new subdirectory with name name. Returns a pointer to the new directory or nullptr | ||
| 170 | // if the operation failed. | ||
| 171 | virtual std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) = 0; | ||
| 172 | // Creates a new file with name name. Returns a pointer to the new file or nullptr if the | ||
| 173 | // operation failed. | ||
| 174 | virtual std::shared_ptr<VfsFile> CreateFile(const std::string& name) = 0; | ||
| 175 | |||
| 176 | // Creates a new file at the path relative to this directory. Also creates directories if | ||
| 177 | // they do not exist and is supported by this implementation. Returns nullptr on any failure. | ||
| 178 | virtual std::shared_ptr<VfsFile> CreateFileRelative(const std::string& path); | ||
| 179 | |||
| 180 | // Creates a new file at the path relative to root of this directory. Also creates directories | ||
| 181 | // if they do not exist and is supported by this implementation. Returns nullptr on any failure. | ||
| 182 | virtual std::shared_ptr<VfsFile> CreateFileAbsolute(const std::string& path); | ||
| 183 | |||
| 184 | // Creates a new directory at the path relative to this directory. Also creates directories if | ||
| 185 | // they do not exist and is supported by this implementation. Returns nullptr on any failure. | ||
| 186 | virtual std::shared_ptr<VfsDirectory> CreateDirectoryRelative(const std::string& path); | ||
| 187 | |||
| 188 | // Creates a new directory at the path relative to root of this directory. Also creates | ||
| 189 | // directories if they do not exist and is supported by this implementation. Returns nullptr on | ||
| 190 | // any failure. | ||
| 191 | virtual std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(const std::string& path); | ||
| 192 | |||
| 193 | // Deletes the subdirectory with name and returns true on success. | ||
| 194 | virtual bool DeleteSubdirectory(const std::string& name) = 0; | ||
| 195 | // Deletes all subdirectories and files of subdirectory with name recirsively and then deletes | ||
| 196 | // the subdirectory. Returns true on success. | ||
| 197 | virtual bool DeleteSubdirectoryRecursive(const std::string& name); | ||
| 198 | // Returnes whether or not the file with name name was deleted successfully. | ||
| 199 | virtual bool DeleteFile(const std::string& name) = 0; | ||
| 200 | |||
| 201 | // Returns whether or not this directory was renamed to name. | ||
| 202 | virtual bool Rename(const std::string& name) = 0; | ||
| 203 | |||
| 204 | // Returns whether or not the file with name src was successfully copied to a new file with name | ||
| 205 | // dest. | ||
| 206 | virtual bool Copy(const std::string& src, const std::string& dest); | ||
| 207 | |||
| 208 | // Interprets the file with name file instead as a directory of type directory. | ||
| 209 | // The directory must have a constructor that takes a single argument of type | ||
| 210 | // std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a | ||
| 211 | // subdirectory in one call. | ||
| 212 | template <typename Directory> | ||
| 213 | bool InterpretAsDirectory(const std::string& file) { | ||
| 214 | auto file_p = GetFile(file); | ||
| 215 | if (file_p == nullptr) | ||
| 216 | return false; | ||
| 217 | return ReplaceFileWithSubdirectory(file, std::make_shared<Directory>(file_p)); | ||
| 218 | } | ||
| 219 | |||
| 220 | protected: | ||
| 221 | // Backend for InterpretAsDirectory. | ||
| 222 | // Removes all references to file and adds a reference to dir in the directory's implementation. | ||
| 223 | virtual bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) = 0; | ||
| 224 | }; | ||
| 225 | |||
| 226 | // A convenience partial-implementation of VfsDirectory that stubs out methods that should only work | ||
| 227 | // if writable. This is to avoid redundant empty methods everywhere. | ||
| 228 | struct ReadOnlyVfsDirectory : public VfsDirectory { | ||
| 229 | bool IsWritable() const override; | ||
| 230 | bool IsReadable() const override; | ||
| 231 | std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override; | ||
| 232 | std::shared_ptr<VfsFile> CreateFile(const std::string& name) override; | ||
| 233 | bool DeleteSubdirectory(const std::string& name) override; | ||
| 234 | bool DeleteFile(const std::string& name) override; | ||
| 235 | bool Rename(const std::string& name) override; | ||
| 236 | }; | ||
| 237 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp new file mode 100644 index 000000000..288499cb5 --- /dev/null +++ b/src/core/file_sys/vfs_offset.cpp | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/file_sys/vfs_offset.h" | ||
| 6 | |||
| 7 | namespace FileSys { | ||
| 8 | |||
| 9 | OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, size_t size_, size_t offset_, | ||
| 10 | const std::string& name_) | ||
| 11 | : file(file_), offset(offset_), size(size_), name(name_) {} | ||
| 12 | |||
| 13 | std::string OffsetVfsFile::GetName() const { | ||
| 14 | return name.empty() ? file->GetName() : name; | ||
| 15 | } | ||
| 16 | |||
| 17 | size_t OffsetVfsFile::GetSize() const { | ||
| 18 | return size; | ||
| 19 | } | ||
| 20 | |||
| 21 | bool OffsetVfsFile::Resize(size_t new_size) { | ||
| 22 | if (offset + new_size < file->GetSize()) { | ||
| 23 | size = new_size; | ||
| 24 | } else { | ||
| 25 | auto res = file->Resize(offset + new_size); | ||
| 26 | if (!res) | ||
| 27 | return false; | ||
| 28 | size = new_size; | ||
| 29 | } | ||
| 30 | |||
| 31 | return true; | ||
| 32 | } | ||
| 33 | |||
| 34 | std::shared_ptr<VfsDirectory> OffsetVfsFile::GetContainingDirectory() const { | ||
| 35 | return file->GetContainingDirectory(); | ||
| 36 | } | ||
| 37 | |||
| 38 | bool OffsetVfsFile::IsWritable() const { | ||
| 39 | return file->IsWritable(); | ||
| 40 | } | ||
| 41 | |||
| 42 | bool OffsetVfsFile::IsReadable() const { | ||
| 43 | return file->IsReadable(); | ||
| 44 | } | ||
| 45 | |||
| 46 | size_t OffsetVfsFile::Read(u8* data, size_t length, size_t r_offset) const { | ||
| 47 | return file->Read(data, TrimToFit(length, r_offset), offset + r_offset); | ||
| 48 | } | ||
| 49 | |||
| 50 | size_t OffsetVfsFile::Write(const u8* data, size_t length, size_t r_offset) { | ||
| 51 | return file->Write(data, TrimToFit(length, r_offset), offset + r_offset); | ||
| 52 | } | ||
| 53 | |||
| 54 | boost::optional<u8> OffsetVfsFile::ReadByte(size_t r_offset) const { | ||
| 55 | if (r_offset < size) | ||
| 56 | return file->ReadByte(offset + r_offset); | ||
| 57 | |||
| 58 | return boost::none; | ||
| 59 | } | ||
| 60 | |||
| 61 | std::vector<u8> OffsetVfsFile::ReadBytes(size_t r_size, size_t r_offset) const { | ||
| 62 | return file->ReadBytes(TrimToFit(r_size, r_offset), offset + r_offset); | ||
| 63 | } | ||
| 64 | |||
| 65 | std::vector<u8> OffsetVfsFile::ReadAllBytes() const { | ||
| 66 | return file->ReadBytes(size, offset); | ||
| 67 | } | ||
| 68 | |||
| 69 | bool OffsetVfsFile::WriteByte(u8 data, size_t r_offset) { | ||
| 70 | if (r_offset < size) | ||
| 71 | return file->WriteByte(data, offset + r_offset); | ||
| 72 | |||
| 73 | return false; | ||
| 74 | } | ||
| 75 | |||
| 76 | size_t OffsetVfsFile::WriteBytes(std::vector<u8> data, size_t r_offset) { | ||
| 77 | return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset); | ||
| 78 | } | ||
| 79 | |||
| 80 | bool OffsetVfsFile::Rename(const std::string& name) { | ||
| 81 | return file->Rename(name); | ||
| 82 | } | ||
| 83 | |||
| 84 | size_t OffsetVfsFile::GetOffset() const { | ||
| 85 | return offset; | ||
| 86 | } | ||
| 87 | |||
| 88 | size_t OffsetVfsFile::TrimToFit(size_t r_size, size_t r_offset) const { | ||
| 89 | return std::max<size_t>(std::min<size_t>(size - r_offset, r_size), 0); | ||
| 90 | } | ||
| 91 | |||
| 92 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h new file mode 100644 index 000000000..adc615b38 --- /dev/null +++ b/src/core/file_sys/vfs_offset.h | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/file_sys/vfs.h" | ||
| 8 | |||
| 9 | namespace FileSys { | ||
| 10 | |||
| 11 | // An implementation of VfsFile that wraps around another VfsFile at a certain offset. | ||
| 12 | // Similar to seeking to an offset. | ||
| 13 | // If the file is writable, operations that would write past the end of the offset file will expand | ||
| 14 | // the size of this wrapper. | ||
| 15 | struct OffsetVfsFile : public VfsFile { | ||
| 16 | OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0, | ||
| 17 | const std::string& new_name = ""); | ||
| 18 | |||
| 19 | std::string GetName() const override; | ||
| 20 | size_t GetSize() const override; | ||
| 21 | bool Resize(size_t new_size) override; | ||
| 22 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; | ||
| 23 | bool IsWritable() const override; | ||
| 24 | bool IsReadable() const override; | ||
| 25 | size_t Read(u8* data, size_t length, size_t offset) const override; | ||
| 26 | size_t Write(const u8* data, size_t length, size_t offset) override; | ||
| 27 | boost::optional<u8> ReadByte(size_t offset) const override; | ||
| 28 | std::vector<u8> ReadBytes(size_t size, size_t offset) const override; | ||
| 29 | std::vector<u8> ReadAllBytes() const override; | ||
| 30 | bool WriteByte(u8 data, size_t offset) override; | ||
| 31 | size_t WriteBytes(std::vector<u8> data, size_t offset) override; | ||
| 32 | |||
| 33 | bool Rename(const std::string& name) override; | ||
| 34 | |||
| 35 | size_t GetOffset() const; | ||
| 36 | |||
| 37 | private: | ||
| 38 | size_t TrimToFit(size_t r_size, size_t r_offset) const; | ||
| 39 | |||
| 40 | std::shared_ptr<VfsFile> file; | ||
| 41 | size_t offset; | ||
| 42 | size_t size; | ||
| 43 | std::string name; | ||
| 44 | }; | ||
| 45 | |||
| 46 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp new file mode 100644 index 000000000..22c858e0d --- /dev/null +++ b/src/core/file_sys/vfs_real.cpp | |||
| @@ -0,0 +1,177 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/common_paths.h" | ||
| 6 | #include "common/logging/log.h" | ||
| 7 | #include "core/file_sys/vfs_real.h" | ||
| 8 | |||
| 9 | namespace FileSys { | ||
| 10 | |||
| 11 | static std::string PermissionsToCharArray(Mode perms) { | ||
| 12 | std::string out; | ||
| 13 | switch (perms) { | ||
| 14 | case Mode::Read: | ||
| 15 | out += "r"; | ||
| 16 | break; | ||
| 17 | case Mode::Write: | ||
| 18 | out += "r+"; | ||
| 19 | break; | ||
| 20 | case Mode::Append: | ||
| 21 | out += "a"; | ||
| 22 | break; | ||
| 23 | } | ||
| 24 | return out + "b"; | ||
| 25 | } | ||
| 26 | |||
| 27 | RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_) | ||
| 28 | : backing(path_, PermissionsToCharArray(perms_).c_str()), path(path_), | ||
| 29 | parent_path(FileUtil::GetParentPath(path_)), | ||
| 30 | path_components(FileUtil::SplitPathComponents(path_)), | ||
| 31 | parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), | ||
| 32 | perms(perms_) {} | ||
| 33 | |||
| 34 | std::string RealVfsFile::GetName() const { | ||
| 35 | return path_components.back(); | ||
| 36 | } | ||
| 37 | |||
| 38 | size_t RealVfsFile::GetSize() const { | ||
| 39 | return backing.GetSize(); | ||
| 40 | } | ||
| 41 | |||
| 42 | bool RealVfsFile::Resize(size_t new_size) { | ||
| 43 | return backing.Resize(new_size); | ||
| 44 | } | ||
| 45 | |||
| 46 | std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const { | ||
| 47 | return std::make_shared<RealVfsDirectory>(parent_path, perms); | ||
| 48 | } | ||
| 49 | |||
| 50 | bool RealVfsFile::IsWritable() const { | ||
| 51 | return perms == Mode::Append || perms == Mode::Write; | ||
| 52 | } | ||
| 53 | |||
| 54 | bool RealVfsFile::IsReadable() const { | ||
| 55 | return perms == Mode::Read || perms == Mode::Write; | ||
| 56 | } | ||
| 57 | |||
| 58 | size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const { | ||
| 59 | if (!backing.Seek(offset, SEEK_SET)) | ||
| 60 | return 0; | ||
| 61 | return backing.ReadBytes(data, length); | ||
| 62 | } | ||
| 63 | |||
| 64 | size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) { | ||
| 65 | if (!backing.Seek(offset, SEEK_SET)) | ||
| 66 | return 0; | ||
| 67 | return backing.WriteBytes(data, length); | ||
| 68 | } | ||
| 69 | |||
| 70 | bool RealVfsFile::Rename(const std::string& name) { | ||
| 71 | const auto out = FileUtil::Rename(GetName(), name); | ||
| 72 | path = parent_path + DIR_SEP + name; | ||
| 73 | path_components = parent_components; | ||
| 74 | path_components.push_back(name); | ||
| 75 | backing = FileUtil::IOFile(path, PermissionsToCharArray(perms).c_str()); | ||
| 76 | return out; | ||
| 77 | } | ||
| 78 | |||
| 79 | bool RealVfsFile::Close() { | ||
| 80 | return backing.Close(); | ||
| 81 | } | ||
| 82 | |||
| 83 | RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_) | ||
| 84 | : path(FileUtil::RemoveTrailingSlash(path_)), parent_path(FileUtil::GetParentPath(path)), | ||
| 85 | path_components(FileUtil::SplitPathComponents(path)), | ||
| 86 | parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), | ||
| 87 | perms(perms_) { | ||
| 88 | if (!FileUtil::Exists(path) && (perms == Mode::Write || perms == Mode::Append)) | ||
| 89 | FileUtil::CreateDir(path); | ||
| 90 | unsigned size; | ||
| 91 | if (perms == Mode::Append) | ||
| 92 | return; | ||
| 93 | |||
| 94 | FileUtil::ForeachDirectoryEntry( | ||
| 95 | &size, path, | ||
| 96 | [this](unsigned* entries_out, const std::string& directory, const std::string& filename) { | ||
| 97 | std::string full_path = directory + DIR_SEP + filename; | ||
| 98 | if (FileUtil::IsDirectory(full_path)) | ||
| 99 | subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(full_path, perms)); | ||
| 100 | else | ||
| 101 | files.emplace_back(std::make_shared<RealVfsFile>(full_path, perms)); | ||
| 102 | return true; | ||
| 103 | }); | ||
| 104 | } | ||
| 105 | |||
| 106 | std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const { | ||
| 107 | return std::vector<std::shared_ptr<VfsFile>>(files); | ||
| 108 | } | ||
| 109 | |||
| 110 | std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const { | ||
| 111 | return std::vector<std::shared_ptr<VfsDirectory>>(subdirectories); | ||
| 112 | } | ||
| 113 | |||
| 114 | bool RealVfsDirectory::IsWritable() const { | ||
| 115 | return perms == Mode::Write || perms == Mode::Append; | ||
| 116 | } | ||
| 117 | |||
| 118 | bool RealVfsDirectory::IsReadable() const { | ||
| 119 | return perms == Mode::Read || perms == Mode::Write; | ||
| 120 | } | ||
| 121 | |||
| 122 | std::string RealVfsDirectory::GetName() const { | ||
| 123 | return path_components.back(); | ||
| 124 | } | ||
| 125 | |||
| 126 | std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const { | ||
| 127 | if (path_components.size() <= 1) | ||
| 128 | return nullptr; | ||
| 129 | |||
| 130 | return std::make_shared<RealVfsDirectory>(parent_path, perms); | ||
| 131 | } | ||
| 132 | |||
| 133 | std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(const std::string& name) { | ||
| 134 | if (!FileUtil::CreateDir(path + DIR_SEP + name)) | ||
| 135 | return nullptr; | ||
| 136 | subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(path + DIR_SEP + name, perms)); | ||
| 137 | return subdirectories.back(); | ||
| 138 | } | ||
| 139 | |||
| 140 | std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(const std::string& name) { | ||
| 141 | if (!FileUtil::CreateEmptyFile(path + DIR_SEP + name)) | ||
| 142 | return nullptr; | ||
| 143 | files.emplace_back(std::make_shared<RealVfsFile>(path + DIR_SEP + name, perms)); | ||
| 144 | return files.back(); | ||
| 145 | } | ||
| 146 | |||
| 147 | bool RealVfsDirectory::DeleteSubdirectory(const std::string& name) { | ||
| 148 | return FileUtil::DeleteDirRecursively(path + DIR_SEP + name); | ||
| 149 | } | ||
| 150 | |||
| 151 | bool RealVfsDirectory::DeleteFile(const std::string& name) { | ||
| 152 | auto file = GetFile(name); | ||
| 153 | if (file == nullptr) | ||
| 154 | return false; | ||
| 155 | files.erase(std::find(files.begin(), files.end(), file)); | ||
| 156 | auto real_file = std::static_pointer_cast<RealVfsFile>(file); | ||
| 157 | real_file->Close(); | ||
| 158 | return FileUtil::Delete(path + DIR_SEP + name); | ||
| 159 | } | ||
| 160 | |||
| 161 | bool RealVfsDirectory::Rename(const std::string& name) { | ||
| 162 | return FileUtil::Rename(path, parent_path + DIR_SEP + name); | ||
| 163 | } | ||
| 164 | |||
| 165 | bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | ||
| 166 | auto iter = std::find(files.begin(), files.end(), file); | ||
| 167 | if (iter == files.end()) | ||
| 168 | return false; | ||
| 169 | |||
| 170 | files[iter - files.begin()] = files.back(); | ||
| 171 | files.pop_back(); | ||
| 172 | |||
| 173 | subdirectories.emplace_back(dir); | ||
| 174 | |||
| 175 | return true; | ||
| 176 | } | ||
| 177 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h new file mode 100644 index 000000000..5b765a552 --- /dev/null +++ b/src/core/file_sys/vfs_real.h | |||
| @@ -0,0 +1,69 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/file_util.h" | ||
| 8 | #include "core/file_sys/mode.h" | ||
| 9 | #include "core/file_sys/vfs.h" | ||
| 10 | |||
| 11 | namespace FileSys { | ||
| 12 | |||
| 13 | // An implmentation of VfsFile that represents a file on the user's computer. | ||
| 14 | struct RealVfsFile : public VfsFile { | ||
| 15 | friend struct RealVfsDirectory; | ||
| 16 | |||
| 17 | RealVfsFile(const std::string& name, Mode perms = Mode::Read); | ||
| 18 | |||
| 19 | std::string GetName() const override; | ||
| 20 | size_t GetSize() const override; | ||
| 21 | bool Resize(size_t new_size) override; | ||
| 22 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; | ||
| 23 | bool IsWritable() const override; | ||
| 24 | bool IsReadable() const override; | ||
| 25 | size_t Read(u8* data, size_t length, size_t offset) const override; | ||
| 26 | size_t Write(const u8* data, size_t length, size_t offset) override; | ||
| 27 | bool Rename(const std::string& name) override; | ||
| 28 | |||
| 29 | private: | ||
| 30 | bool Close(); | ||
| 31 | |||
| 32 | FileUtil::IOFile backing; | ||
| 33 | std::string path; | ||
| 34 | std::string parent_path; | ||
| 35 | std::vector<std::string> path_components; | ||
| 36 | std::vector<std::string> parent_components; | ||
| 37 | Mode perms; | ||
| 38 | }; | ||
| 39 | |||
| 40 | // An implementation of VfsDirectory that represents a directory on the user's computer. | ||
| 41 | struct RealVfsDirectory : public VfsDirectory { | ||
| 42 | RealVfsDirectory(const std::string& path, Mode perms); | ||
| 43 | |||
| 44 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; | ||
| 45 | std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; | ||
| 46 | bool IsWritable() const override; | ||
| 47 | bool IsReadable() const override; | ||
| 48 | std::string GetName() const override; | ||
| 49 | std::shared_ptr<VfsDirectory> GetParentDirectory() const override; | ||
| 50 | std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override; | ||
| 51 | std::shared_ptr<VfsFile> CreateFile(const std::string& name) override; | ||
| 52 | bool DeleteSubdirectory(const std::string& name) override; | ||
| 53 | bool DeleteFile(const std::string& name) override; | ||
| 54 | bool Rename(const std::string& name) override; | ||
| 55 | |||
| 56 | protected: | ||
| 57 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 58 | |||
| 59 | private: | ||
| 60 | std::string path; | ||
| 61 | std::string parent_path; | ||
| 62 | std::vector<std::string> path_components; | ||
| 63 | std::vector<std::string> parent_components; | ||
| 64 | Mode perms; | ||
| 65 | std::vector<std::shared_ptr<VfsFile>> files; | ||
| 66 | std::vector<std::shared_ptr<VfsDirectory>> subdirectories; | ||
| 67 | }; | ||
| 68 | |||
| 69 | } // namespace FileSys | ||