Browse Source
Virtual Filesystem (#597)
Virtual Filesystem (#597)
* Add VfsFile and VfsDirectory classes * Finish abstract Vfs classes * Implement RealVfsFile (computer fs backend) * Finish RealVfsFile and RealVfsDirectory * Finished OffsetVfsFile * More changes * Fix import paths * Major refactor * Remove double const * Use experimental/filesystem or filesystem depending on compiler * Port partition_filesystem * More changes * More Overhaul * FSP_SRV fixes * Fixes and testing * Try to get filesystem to compile * Filesystem on linux * Remove std::filesystem and document/test * Compile fixes * Missing include * Bug fixes * Fixes * Rename v_file and v_dir * clang-format fix * Rename NGLOG_* to LOG_* * Most review changes * Fix TODO * Guess 'main' to be Directory by filenamepull/15/merge
committed by
bunnei
45 changed files with 1784 additions and 1676 deletions
-
93src/common/file_util.cpp
-
63src/common/file_util.h
-
18src/core/CMakeLists.txt
-
3src/core/core.cpp
-
164src/core/file_sys/content_archive.cpp
-
89src/core/file_sys/content_archive.h
-
237src/core/file_sys/disk_filesystem.cpp
-
84src/core/file_sys/disk_filesystem.h
-
132src/core/file_sys/filesystem.h
-
136src/core/file_sys/partition_filesystem.cpp
-
29src/core/file_sys/partition_filesystem.h
-
43src/core/file_sys/program_metadata.cpp
-
6src/core/file_sys/program_metadata.h
-
38src/core/file_sys/romfs_factory.cpp
-
35src/core/file_sys/romfs_factory.h
-
110src/core/file_sys/romfs_filesystem.cpp
-
85src/core/file_sys/romfs_filesystem.h
-
54src/core/file_sys/savedata_factory.cpp
-
33src/core/file_sys/savedata_factory.h
-
39src/core/file_sys/sdmc_factory.cpp
-
31src/core/file_sys/sdmc_factory.h
-
187src/core/file_sys/vfs.cpp
-
220src/core/file_sys/vfs.h
-
92src/core/file_sys/vfs_offset.cpp
-
46src/core/file_sys/vfs_offset.h
-
168src/core/file_sys/vfs_real.cpp
-
65src/core/file_sys/vfs_real.h
-
2src/core/hle/service/am/am.cpp
-
225src/core/hle/service/filesystem/filesystem.cpp
-
133src/core/hle/service/filesystem/filesystem.h
-
114src/core/hle/service/filesystem/fsp_srv.cpp
-
2src/core/hle/service/filesystem/fsp_srv.h
-
113src/core/loader/deconstructed_rom_directory.cpp
-
14src/core/loader/deconstructed_rom_directory.h
-
24src/core/loader/elf.cpp
-
12src/core/loader/elf.h
-
63src/core/loader/loader.cpp
-
30src/core/loader/loader.h
-
252src/core/loader/nca.cpp
-
19src/core/loader/nca.h
-
32src/core/loader/nro.cpp
-
13src/core/loader/nro.h
-
91src/core/loader/nso.cpp
-
17src/core/loader/nso.h
-
4src/yuzu/game_list.cpp
@ -0,0 +1,164 @@ |
|||
// Copyright 2018 yuzu emulator team
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include "common/logging/log.h"
|
|||
#include "core/file_sys/content_archive.h"
|
|||
#include "core/file_sys/vfs_offset.h"
|
|||
#include "core/loader/loader.h"
|
|||
|
|||
namespace FileSys { |
|||
|
|||
// Media offsets in headers are stored divided by 512. Mult. by this to get real offset.
|
|||
constexpr u64 MEDIA_OFFSET_MULTIPLIER = 0x200; |
|||
|
|||
constexpr u64 SECTION_HEADER_SIZE = 0x200; |
|||
constexpr u64 SECTION_HEADER_OFFSET = 0x400; |
|||
|
|||
constexpr u32 IVFC_MAX_LEVEL = 6; |
|||
|
|||
enum class NCASectionFilesystemType : u8 { PFS0 = 0x2, ROMFS = 0x3 }; |
|||
|
|||
struct NCASectionHeaderBlock { |
|||
INSERT_PADDING_BYTES(3); |
|||
NCASectionFilesystemType filesystem_type; |
|||
u8 crypto_type; |
|||
INSERT_PADDING_BYTES(3); |
|||
}; |
|||
static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size."); |
|||
|
|||
struct PFS0Superblock { |
|||
NCASectionHeaderBlock header_block; |
|||
std::array<u8, 0x20> hash; |
|||
u32_le size; |
|||
INSERT_PADDING_BYTES(4); |
|||
u64_le hash_table_offset; |
|||
u64_le hash_table_size; |
|||
u64_le pfs0_header_offset; |
|||
u64_le pfs0_size; |
|||
INSERT_PADDING_BYTES(432); |
|||
}; |
|||
static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size."); |
|||
|
|||
struct IVFCLevel { |
|||
u64_le offset; |
|||
u64_le size; |
|||
u32_le block_size; |
|||
u32_le reserved; |
|||
}; |
|||
static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size."); |
|||
|
|||
struct RomFSSuperblock { |
|||
NCASectionHeaderBlock header_block; |
|||
u32_le magic; |
|||
u32_le magic_number; |
|||
INSERT_PADDING_BYTES(8); |
|||
std::array<IVFCLevel, 6> levels; |
|||
INSERT_PADDING_BYTES(64); |
|||
}; |
|||
static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size."); |
|||
|
|||
NCA::NCA(VirtualFile file_) : file(file_) { |
|||
if (sizeof(NCAHeader) != file->ReadObject(&header)) |
|||
LOG_CRITICAL(Loader, "File reader errored out during header read."); |
|||
|
|||
if (!IsValidNCA(header)) { |
|||
status = Loader::ResultStatus::ErrorInvalidFormat; |
|||
return; |
|||
} |
|||
|
|||
std::ptrdiff_t number_sections = |
|||
std::count_if(std::begin(header.section_tables), std::end(header.section_tables), |
|||
[](NCASectionTableEntry entry) { return entry.media_offset > 0; }); |
|||
|
|||
for (std::ptrdiff_t i = 0; i < number_sections; ++i) { |
|||
// Seek to beginning of this section.
|
|||
NCASectionHeaderBlock block{}; |
|||
if (sizeof(NCASectionHeaderBlock) != |
|||
file->ReadObject(&block, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) |
|||
LOG_CRITICAL(Loader, "File reader errored out during header read."); |
|||
|
|||
if (block.filesystem_type == NCASectionFilesystemType::ROMFS) { |
|||
RomFSSuperblock sb{}; |
|||
if (sizeof(RomFSSuperblock) != |
|||
file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) |
|||
LOG_CRITICAL(Loader, "File reader errored out during header read."); |
|||
|
|||
const size_t romfs_offset = |
|||
header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER + |
|||
sb.levels[IVFC_MAX_LEVEL - 1].offset; |
|||
const size_t romfs_size = sb.levels[IVFC_MAX_LEVEL - 1].size; |
|||
files.emplace_back(std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset)); |
|||
romfs = files.back(); |
|||
} else if (block.filesystem_type == NCASectionFilesystemType::PFS0) { |
|||
PFS0Superblock sb{}; |
|||
// Seek back to beginning of this section.
|
|||
if (sizeof(PFS0Superblock) != |
|||
file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) |
|||
LOG_CRITICAL(Loader, "File reader errored out during header read."); |
|||
|
|||
u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) * |
|||
MEDIA_OFFSET_MULTIPLIER) + |
|||
sb.pfs0_header_offset; |
|||
u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset - |
|||
header.section_tables[i].media_offset); |
|||
auto npfs = std::make_shared<PartitionFilesystem>( |
|||
std::make_shared<OffsetVfsFile>(file, size, offset)); |
|||
|
|||
if (npfs->GetStatus() == Loader::ResultStatus::Success) { |
|||
dirs.emplace_back(npfs); |
|||
if (IsDirectoryExeFS(dirs.back())) |
|||
exefs = dirs.back(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
status = Loader::ResultStatus::Success; |
|||
} |
|||
|
|||
Loader::ResultStatus NCA::GetStatus() const { |
|||
return status; |
|||
} |
|||
|
|||
std::vector<std::shared_ptr<VfsFile>> NCA::GetFiles() const { |
|||
if (status != Loader::ResultStatus::Success) |
|||
return {}; |
|||
return files; |
|||
} |
|||
|
|||
std::vector<std::shared_ptr<VfsDirectory>> NCA::GetSubdirectories() const { |
|||
if (status != Loader::ResultStatus::Success) |
|||
return {}; |
|||
return dirs; |
|||
} |
|||
|
|||
std::string NCA::GetName() const { |
|||
return file->GetName(); |
|||
} |
|||
|
|||
std::shared_ptr<VfsDirectory> NCA::GetParentDirectory() const { |
|||
return file->GetContainingDirectory(); |
|||
} |
|||
|
|||
NCAContentType NCA::GetType() const { |
|||
return header.content_type; |
|||
} |
|||
|
|||
u64 NCA::GetTitleId() const { |
|||
if (status != Loader::ResultStatus::Success) |
|||
return {}; |
|||
return header.title_id; |
|||
} |
|||
|
|||
VirtualFile NCA::GetRomFS() const { |
|||
return romfs; |
|||
} |
|||
|
|||
VirtualDir NCA::GetExeFS() const { |
|||
return exefs; |
|||
} |
|||
|
|||
bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { |
|||
return false; |
|||
} |
|||
} // namespace FileSys
|
|||
@ -0,0 +1,89 @@ |
|||
// Copyright 2018 yuzu emulator team |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/common_funcs.h" |
|||
#include "common/common_types.h" |
|||
#include "common/swap.h" |
|||
#include "core/file_sys/partition_filesystem.h" |
|||
|
|||
namespace FileSys { |
|||
|
|||
enum class NCAContentType : u8 { Program = 0, Meta = 1, Control = 2, Manual = 3, Data = 4 }; |
|||
|
|||
struct NCASectionTableEntry { |
|||
u32_le media_offset; |
|||
u32_le media_end_offset; |
|||
INSERT_PADDING_BYTES(0x8); |
|||
}; |
|||
static_assert(sizeof(NCASectionTableEntry) == 0x10, "NCASectionTableEntry has incorrect size."); |
|||
|
|||
struct NCAHeader { |
|||
std::array<u8, 0x100> rsa_signature_1; |
|||
std::array<u8, 0x100> rsa_signature_2; |
|||
u32_le magic; |
|||
u8 is_system; |
|||
NCAContentType content_type; |
|||
u8 crypto_type; |
|||
u8 key_index; |
|||
u64_le size; |
|||
u64_le title_id; |
|||
INSERT_PADDING_BYTES(0x4); |
|||
u32_le sdk_version; |
|||
u8 crypto_type_2; |
|||
INSERT_PADDING_BYTES(15); |
|||
std::array<u8, 0x10> rights_id; |
|||
std::array<NCASectionTableEntry, 0x4> section_tables; |
|||
std::array<std::array<u8, 0x20>, 0x4> hash_tables; |
|||
std::array<std::array<u8, 0x10>, 0x4> key_area; |
|||
INSERT_PADDING_BYTES(0xC0); |
|||
}; |
|||
static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size."); |
|||
|
|||
static bool IsDirectoryExeFS(std::shared_ptr<FileSys::VfsDirectory> pfs) { |
|||
// According to switchbrew, an exefs must only contain these two files: |
|||
return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr; |
|||
} |
|||
|
|||
static bool IsValidNCA(const NCAHeader& header) { |
|||
return header.magic == Common::MakeMagic('N', 'C', 'A', '2') || |
|||
header.magic == Common::MakeMagic('N', 'C', 'A', '3'); |
|||
} |
|||
|
|||
// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner. |
|||
// After construction, use GetStatus to determine if the file is valid and ready to be used. |
|||
class NCA : public ReadOnlyVfsDirectory { |
|||
public: |
|||
explicit NCA(VirtualFile file); |
|||
Loader::ResultStatus GetStatus() const; |
|||
|
|||
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; |
|||
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; |
|||
std::string GetName() const override; |
|||
std::shared_ptr<VfsDirectory> GetParentDirectory() const override; |
|||
|
|||
NCAContentType GetType() const; |
|||
u64 GetTitleId() const; |
|||
|
|||
VirtualFile GetRomFS() const; |
|||
VirtualDir GetExeFS() const; |
|||
|
|||
protected: |
|||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; |
|||
|
|||
private: |
|||
std::vector<VirtualDir> dirs; |
|||
std::vector<VirtualFile> files; |
|||
|
|||
VirtualFile romfs = nullptr; |
|||
VirtualDir exefs = nullptr; |
|||
VirtualFile file; |
|||
|
|||
NCAHeader header{}; |
|||
|
|||
Loader::ResultStatus status{}; |
|||
}; |
|||
|
|||
} // namespace FileSys |
|||
@ -1,237 +0,0 @@ |
|||
// Copyright 2018 yuzu emulator team
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <cstring>
|
|||
#include <memory>
|
|||
#include "common/common_types.h"
|
|||
#include "common/logging/log.h"
|
|||
#include "core/file_sys/disk_filesystem.h"
|
|||
#include "core/file_sys/errors.h"
|
|||
|
|||
namespace FileSys { |
|||
|
|||
static std::string ModeFlagsToString(Mode mode) { |
|||
std::string mode_str; |
|||
u32 mode_flags = static_cast<u32>(mode); |
|||
|
|||
// Calculate the correct open mode for the file.
|
|||
if ((mode_flags & static_cast<u32>(Mode::Read)) && |
|||
(mode_flags & static_cast<u32>(Mode::Write))) { |
|||
if (mode_flags & static_cast<u32>(Mode::Append)) |
|||
mode_str = "a+"; |
|||
else |
|||
mode_str = "r+"; |
|||
} else { |
|||
if (mode_flags & static_cast<u32>(Mode::Read)) |
|||
mode_str = "r"; |
|||
else if (mode_flags & static_cast<u32>(Mode::Append)) |
|||
mode_str = "a"; |
|||
else if (mode_flags & static_cast<u32>(Mode::Write)) |
|||
mode_str = "w"; |
|||
} |
|||
|
|||
mode_str += "b"; |
|||
|
|||
return mode_str; |
|||
} |
|||
|
|||
std::string Disk_FileSystem::GetName() const { |
|||
return "Disk"; |
|||
} |
|||
|
|||
ResultVal<std::unique_ptr<StorageBackend>> Disk_FileSystem::OpenFile(const std::string& path, |
|||
Mode mode) const { |
|||
|
|||
// Calculate the correct open mode for the file.
|
|||
std::string mode_str = ModeFlagsToString(mode); |
|||
|
|||
std::string full_path = base_directory + path; |
|||
auto file = std::make_shared<FileUtil::IOFile>(full_path, mode_str.c_str()); |
|||
|
|||
if (!file->IsOpen()) { |
|||
return ERROR_PATH_NOT_FOUND; |
|||
} |
|||
|
|||
return MakeResult<std::unique_ptr<StorageBackend>>( |
|||
std::make_unique<Disk_Storage>(std::move(file))); |
|||
} |
|||
|
|||
ResultCode Disk_FileSystem::DeleteFile(const std::string& path) const { |
|||
if (!FileUtil::Exists(path)) { |
|||
return ERROR_PATH_NOT_FOUND; |
|||
} |
|||
|
|||
FileUtil::Delete(path); |
|||
|
|||
return RESULT_SUCCESS; |
|||
} |
|||
|
|||
ResultCode Disk_FileSystem::RenameFile(const std::string& src_path, |
|||
const std::string& dest_path) const { |
|||
const std::string full_src_path = base_directory + src_path; |
|||
const std::string full_dest_path = base_directory + dest_path; |
|||
|
|||
if (!FileUtil::Exists(full_src_path)) { |
|||
return ERROR_PATH_NOT_FOUND; |
|||
} |
|||
// TODO(wwylele): Use correct error code
|
|||
return FileUtil::Rename(full_src_path, full_dest_path) ? RESULT_SUCCESS : ResultCode(-1); |
|||
} |
|||
|
|||
ResultCode Disk_FileSystem::DeleteDirectory(const Path& path) const { |
|||
LOG_WARNING(Service_FS, "(STUBBED) called"); |
|||
// TODO(wwylele): Use correct error code
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
ResultCode Disk_FileSystem::DeleteDirectoryRecursively(const Path& path) const { |
|||
LOG_WARNING(Service_FS, "(STUBBED) called"); |
|||
// TODO(wwylele): Use correct error code
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
ResultCode Disk_FileSystem::CreateFile(const std::string& path, u64 size) const { |
|||
LOG_WARNING(Service_FS, "(STUBBED) called"); |
|||
|
|||
std::string full_path = base_directory + path; |
|||
if (size == 0) { |
|||
FileUtil::CreateEmptyFile(full_path); |
|||
return RESULT_SUCCESS; |
|||
} |
|||
|
|||
FileUtil::IOFile file(full_path, "wb"); |
|||
// Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
|
|||
// We do this by seeking to the right size, then writing a single null byte.
|
|||
if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) { |
|||
return RESULT_SUCCESS; |
|||
} |
|||
|
|||
LOG_ERROR(Service_FS, "Too large file"); |
|||
// TODO(Subv): Find out the correct error code
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
ResultCode Disk_FileSystem::CreateDirectory(const std::string& path) const { |
|||
// TODO(Subv): Perform path validation to prevent escaping the emulator sandbox.
|
|||
std::string full_path = base_directory + path; |
|||
|
|||
if (FileUtil::CreateDir(full_path)) { |
|||
return RESULT_SUCCESS; |
|||
} |
|||
|
|||
LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating {}", full_path); |
|||
// TODO(wwylele): Use correct error code
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
ResultCode Disk_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const { |
|||
LOG_WARNING(Service_FS, "(STUBBED) called"); |
|||
// TODO(wwylele): Use correct error code
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
ResultVal<std::unique_ptr<DirectoryBackend>> Disk_FileSystem::OpenDirectory( |
|||
const std::string& path) const { |
|||
|
|||
std::string full_path = base_directory + path; |
|||
|
|||
if (!FileUtil::IsDirectory(full_path)) { |
|||
// TODO(Subv): Find the correct error code for this.
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
auto directory = std::make_unique<Disk_Directory>(full_path); |
|||
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory)); |
|||
} |
|||
|
|||
u64 Disk_FileSystem::GetFreeSpaceSize() const { |
|||
LOG_WARNING(Service_FS, "(STUBBED) called"); |
|||
return 0; |
|||
} |
|||
|
|||
ResultVal<FileSys::EntryType> Disk_FileSystem::GetEntryType(const std::string& path) const { |
|||
std::string full_path = base_directory + path; |
|||
if (!FileUtil::Exists(full_path)) { |
|||
return ERROR_PATH_NOT_FOUND; |
|||
} |
|||
|
|||
if (FileUtil::IsDirectory(full_path)) |
|||
return MakeResult(EntryType::Directory); |
|||
|
|||
return MakeResult(EntryType::File); |
|||
} |
|||
|
|||
ResultVal<size_t> Disk_Storage::Read(const u64 offset, const size_t length, u8* buffer) const { |
|||
LOG_TRACE(Service_FS, "called offset={}, length={}", offset, length); |
|||
file->Seek(offset, SEEK_SET); |
|||
return MakeResult<size_t>(file->ReadBytes(buffer, length)); |
|||
} |
|||
|
|||
ResultVal<size_t> Disk_Storage::Write(const u64 offset, const size_t length, const bool flush, |
|||
const u8* buffer) const { |
|||
LOG_WARNING(Service_FS, "(STUBBED) called"); |
|||
file->Seek(offset, SEEK_SET); |
|||
size_t written = file->WriteBytes(buffer, length); |
|||
if (flush) { |
|||
file->Flush(); |
|||
} |
|||
return MakeResult<size_t>(written); |
|||
} |
|||
|
|||
u64 Disk_Storage::GetSize() const { |
|||
return file->GetSize(); |
|||
} |
|||
|
|||
bool Disk_Storage::SetSize(const u64 size) const { |
|||
file->Resize(size); |
|||
file->Flush(); |
|||
return true; |
|||
} |
|||
|
|||
Disk_Directory::Disk_Directory(const std::string& path) { |
|||
unsigned size = FileUtil::ScanDirectoryTree(path, directory); |
|||
directory.size = size; |
|||
directory.isDirectory = true; |
|||
children_iterator = directory.children.begin(); |
|||
} |
|||
|
|||
u64 Disk_Directory::Read(const u64 count, Entry* entries) { |
|||
u64 entries_read = 0; |
|||
|
|||
while (entries_read < count && children_iterator != directory.children.cend()) { |
|||
const FileUtil::FSTEntry& file = *children_iterator; |
|||
const std::string& filename = file.virtualName; |
|||
Entry& entry = entries[entries_read]; |
|||
|
|||
LOG_TRACE(Service_FS, "File {}: size={} dir={}", filename, file.size, file.isDirectory); |
|||
|
|||
// TODO(Link Mauve): use a proper conversion to UTF-16.
|
|||
for (size_t j = 0; j < FILENAME_LENGTH; ++j) { |
|||
entry.filename[j] = filename[j]; |
|||
if (!filename[j]) |
|||
break; |
|||
} |
|||
|
|||
if (file.isDirectory) { |
|||
entry.file_size = 0; |
|||
entry.type = EntryType::Directory; |
|||
} else { |
|||
entry.file_size = file.size; |
|||
entry.type = EntryType::File; |
|||
} |
|||
|
|||
++entries_read; |
|||
++children_iterator; |
|||
} |
|||
return entries_read; |
|||
} |
|||
|
|||
u64 Disk_Directory::GetEntryCount() const { |
|||
// We convert the children iterator into a const_iterator to allow template argument deduction
|
|||
// in std::distance.
|
|||
std::vector<FileUtil::FSTEntry>::const_iterator current = children_iterator; |
|||
return std::distance(current, directory.children.end()); |
|||
} |
|||
|
|||
} // namespace FileSys
|
|||
@ -1,84 +0,0 @@ |
|||
// Copyright 2018 yuzu emulator team |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <cstddef> |
|||
#include <memory> |
|||
#include <string> |
|||
#include "common/common_types.h" |
|||
#include "common/file_util.h" |
|||
#include "core/file_sys/directory.h" |
|||
#include "core/file_sys/filesystem.h" |
|||
#include "core/file_sys/storage.h" |
|||
#include "core/hle/result.h" |
|||
|
|||
namespace FileSys { |
|||
|
|||
class Disk_FileSystem : public FileSystemBackend { |
|||
public: |
|||
explicit Disk_FileSystem(std::string base_directory) |
|||
: base_directory(std::move(base_directory)) {} |
|||
|
|||
std::string GetName() const override; |
|||
|
|||
ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path, |
|||
Mode mode) const override; |
|||
ResultCode DeleteFile(const std::string& path) const override; |
|||
ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const override; |
|||
ResultCode DeleteDirectory(const Path& path) const override; |
|||
ResultCode DeleteDirectoryRecursively(const Path& path) const override; |
|||
ResultCode CreateFile(const std::string& path, u64 size) const override; |
|||
ResultCode CreateDirectory(const std::string& path) const override; |
|||
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override; |
|||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory( |
|||
const std::string& path) const override; |
|||
u64 GetFreeSpaceSize() const override; |
|||
ResultVal<EntryType> GetEntryType(const std::string& path) const override; |
|||
|
|||
protected: |
|||
std::string base_directory; |
|||
}; |
|||
|
|||
class Disk_Storage : public StorageBackend { |
|||
public: |
|||
explicit Disk_Storage(std::shared_ptr<FileUtil::IOFile> file) : file(std::move(file)) {} |
|||
|
|||
ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; |
|||
ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; |
|||
u64 GetSize() const override; |
|||
bool SetSize(u64 size) const override; |
|||
bool Close() const override { |
|||
return false; |
|||
} |
|||
void Flush() const override {} |
|||
|
|||
private: |
|||
std::shared_ptr<FileUtil::IOFile> file; |
|||
}; |
|||
|
|||
class Disk_Directory : public DirectoryBackend { |
|||
public: |
|||
explicit Disk_Directory(const std::string& path); |
|||
|
|||
~Disk_Directory() override { |
|||
Close(); |
|||
} |
|||
|
|||
u64 Read(const u64 count, Entry* entries) override; |
|||
u64 GetEntryCount() const override; |
|||
|
|||
bool Close() const override { |
|||
return true; |
|||
} |
|||
|
|||
protected: |
|||
FileUtil::FSTEntry directory; |
|||
|
|||
// We need to remember the last entry we returned, so a subsequent call to Read will continue |
|||
// from the next one. This iterator will always point to the next unread entry. |
|||
std::vector<FileUtil::FSTEntry>::iterator children_iterator; |
|||
}; |
|||
|
|||
} // namespace FileSys |
|||
@ -1,38 +0,0 @@ |
|||
// Copyright 2018 yuzu emulator team
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <algorithm>
|
|||
#include <memory>
|
|||
#include "common/common_types.h"
|
|||
#include "common/logging/log.h"
|
|||
#include "core/file_sys/romfs_factory.h"
|
|||
#include "core/file_sys/romfs_filesystem.h"
|
|||
|
|||
namespace FileSys { |
|||
|
|||
RomFS_Factory::RomFS_Factory(Loader::AppLoader& app_loader) { |
|||
// Load the RomFS from the app
|
|||
if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) { |
|||
LOG_ERROR(Service_FS, "Unable to read RomFS!"); |
|||
} |
|||
} |
|||
|
|||
ResultVal<std::unique_ptr<FileSystemBackend>> RomFS_Factory::Open(const Path& path) { |
|||
auto archive = std::make_unique<RomFS_FileSystem>(romfs_file, data_offset, data_size); |
|||
return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive)); |
|||
} |
|||
|
|||
ResultCode RomFS_Factory::Format(const Path& path) { |
|||
LOG_ERROR(Service_FS, "Unimplemented Format archive {}", GetName()); |
|||
// TODO(bunnei): Find the right error code for this
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
ResultVal<ArchiveFormatInfo> RomFS_Factory::GetFormatInfo(const Path& path) const { |
|||
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); |
|||
// TODO(bunnei): Find the right error code for this
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
} // namespace FileSys
|
|||
@ -1,35 +0,0 @@ |
|||
// Copyright 2018 yuzu emulator team |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <memory> |
|||
#include <string> |
|||
#include <vector> |
|||
#include "common/common_types.h" |
|||
#include "core/file_sys/filesystem.h" |
|||
#include "core/hle/result.h" |
|||
#include "core/loader/loader.h" |
|||
|
|||
namespace FileSys { |
|||
|
|||
/// File system interface to the RomFS archive |
|||
class RomFS_Factory final : public FileSystemFactory { |
|||
public: |
|||
explicit RomFS_Factory(Loader::AppLoader& app_loader); |
|||
|
|||
std::string GetName() const override { |
|||
return "ArchiveFactory_RomFS"; |
|||
} |
|||
ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override; |
|||
ResultCode Format(const Path& path) override; |
|||
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; |
|||
|
|||
private: |
|||
std::shared_ptr<FileUtil::IOFile> romfs_file; |
|||
u64 data_offset; |
|||
u64 data_size; |
|||
}; |
|||
|
|||
} // namespace FileSys |
|||
@ -1,110 +0,0 @@ |
|||
// Copyright 2018 yuzu emulator team
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <cstring>
|
|||
#include <memory>
|
|||
#include "common/common_types.h"
|
|||
#include "common/logging/log.h"
|
|||
#include "core/file_sys/romfs_filesystem.h"
|
|||
|
|||
namespace FileSys { |
|||
|
|||
std::string RomFS_FileSystem::GetName() const { |
|||
return "RomFS"; |
|||
} |
|||
|
|||
ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const std::string& path, |
|||
Mode mode) const { |
|||
return MakeResult<std::unique_ptr<StorageBackend>>( |
|||
std::make_unique<RomFS_Storage>(romfs_file, data_offset, data_size)); |
|||
} |
|||
|
|||
ResultCode RomFS_FileSystem::DeleteFile(const std::string& path) const { |
|||
LOG_CRITICAL(Service_FS, "Attempted to delete a file from an ROMFS archive ({}).", GetName()); |
|||
// TODO(bunnei): Use correct error code
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
ResultCode RomFS_FileSystem::RenameFile(const std::string& src_path, |
|||
const std::string& dest_path) const { |
|||
LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive ({}).", GetName()); |
|||
// TODO(wwylele): Use correct error code
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
ResultCode RomFS_FileSystem::DeleteDirectory(const Path& path) const { |
|||
LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive ({}).", |
|||
GetName()); |
|||
// TODO(wwylele): Use correct error code
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
ResultCode RomFS_FileSystem::DeleteDirectoryRecursively(const Path& path) const { |
|||
LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive ({}).", |
|||
GetName()); |
|||
// TODO(wwylele): Use correct error code
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
ResultCode RomFS_FileSystem::CreateFile(const std::string& path, u64 size) const { |
|||
LOG_CRITICAL(Service_FS, "Attempted to create a file in an ROMFS archive ({}).", GetName()); |
|||
// TODO(bunnei): Use correct error code
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
ResultCode RomFS_FileSystem::CreateDirectory(const std::string& path) const { |
|||
LOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive ({}).", |
|||
GetName()); |
|||
// TODO(wwylele): Use correct error code
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
ResultCode RomFS_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const { |
|||
LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive ({}).", GetName()); |
|||
// TODO(wwylele): Use correct error code
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
ResultVal<std::unique_ptr<DirectoryBackend>> RomFS_FileSystem::OpenDirectory( |
|||
const std::string& path) const { |
|||
LOG_WARNING(Service_FS, "Opening Directory in a ROMFS archive"); |
|||
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<ROMFSDirectory>()); |
|||
} |
|||
|
|||
u64 RomFS_FileSystem::GetFreeSpaceSize() const { |
|||
LOG_WARNING(Service_FS, "Attempted to get the free space in an ROMFS archive"); |
|||
return 0; |
|||
} |
|||
|
|||
ResultVal<FileSys::EntryType> RomFS_FileSystem::GetEntryType(const std::string& path) const { |
|||
LOG_CRITICAL(Service_FS, "Called within an ROMFS archive (path {}).", path); |
|||
// TODO(wwylele): Use correct error code
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* buffer) const { |
|||
LOG_TRACE(Service_FS, "called offset={}, length={}", offset, length); |
|||
romfs_file->Seek(data_offset + offset, SEEK_SET); |
|||
size_t read_length = (size_t)std::min((u64)length, data_size - offset); |
|||
|
|||
return MakeResult<size_t>(romfs_file->ReadBytes(buffer, read_length)); |
|||
} |
|||
|
|||
ResultVal<size_t> RomFS_Storage::Write(const u64 offset, const size_t length, const bool flush, |
|||
const u8* buffer) const { |
|||
LOG_ERROR(Service_FS, "Attempted to write to ROMFS file"); |
|||
// TODO(Subv): Find error code
|
|||
return MakeResult<size_t>(0); |
|||
} |
|||
|
|||
u64 RomFS_Storage::GetSize() const { |
|||
return data_size; |
|||
} |
|||
|
|||
bool RomFS_Storage::SetSize(const u64 size) const { |
|||
LOG_ERROR(Service_FS, "Attempted to set the size of an ROMFS file"); |
|||
return false; |
|||
} |
|||
|
|||
} // namespace FileSys
|
|||
@ -1,85 +0,0 @@ |
|||
// Copyright 2018 yuzu emulator team |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <cstddef> |
|||
#include <memory> |
|||
#include <string> |
|||
#include <vector> |
|||
#include "common/common_types.h" |
|||
#include "common/file_util.h" |
|||
#include "core/file_sys/directory.h" |
|||
#include "core/file_sys/filesystem.h" |
|||
#include "core/file_sys/storage.h" |
|||
#include "core/hle/result.h" |
|||
|
|||
namespace FileSys { |
|||
|
|||
/** |
|||
* Helper which implements an interface to deal with Switch .istorage ROMFS images used in some |
|||
* archives This should be subclassed by concrete archive types, which will provide the input data |
|||
* (load the raw ROMFS archive) and override any required methods |
|||
*/ |
|||
class RomFS_FileSystem : public FileSystemBackend { |
|||
public: |
|||
RomFS_FileSystem(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) |
|||
: romfs_file(file), data_offset(offset), data_size(size) {} |
|||
|
|||
std::string GetName() const override; |
|||
|
|||
ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path, |
|||
Mode mode) const override; |
|||
ResultCode DeleteFile(const std::string& path) const override; |
|||
ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const override; |
|||
ResultCode DeleteDirectory(const Path& path) const override; |
|||
ResultCode DeleteDirectoryRecursively(const Path& path) const override; |
|||
ResultCode CreateFile(const std::string& path, u64 size) const override; |
|||
ResultCode CreateDirectory(const std::string& path) const override; |
|||
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override; |
|||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory( |
|||
const std::string& path) const override; |
|||
u64 GetFreeSpaceSize() const override; |
|||
ResultVal<EntryType> GetEntryType(const std::string& path) const override; |
|||
|
|||
protected: |
|||
std::shared_ptr<FileUtil::IOFile> romfs_file; |
|||
u64 data_offset; |
|||
u64 data_size; |
|||
}; |
|||
|
|||
class RomFS_Storage : public StorageBackend { |
|||
public: |
|||
RomFS_Storage(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) |
|||
: romfs_file(file), data_offset(offset), data_size(size) {} |
|||
|
|||
ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; |
|||
ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; |
|||
u64 GetSize() const override; |
|||
bool SetSize(u64 size) const override; |
|||
bool Close() const override { |
|||
return false; |
|||
} |
|||
void Flush() const override {} |
|||
|
|||
private: |
|||
std::shared_ptr<FileUtil::IOFile> romfs_file; |
|||
u64 data_offset; |
|||
u64 data_size; |
|||
}; |
|||
|
|||
class ROMFSDirectory : public DirectoryBackend { |
|||
public: |
|||
u64 Read(const u64 count, Entry* entries) override { |
|||
return 0; |
|||
} |
|||
u64 GetEntryCount() const override { |
|||
return 0; |
|||
} |
|||
bool Close() const override { |
|||
return false; |
|||
} |
|||
}; |
|||
|
|||
} // namespace FileSys |
|||
@ -1,54 +0,0 @@ |
|||
// Copyright 2018 yuzu emulator team
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <memory>
|
|||
#include "common/common_types.h"
|
|||
#include "common/logging/log.h"
|
|||
#include "core/core.h"
|
|||
#include "core/file_sys/disk_filesystem.h"
|
|||
#include "core/file_sys/savedata_factory.h"
|
|||
#include "core/hle/kernel/process.h"
|
|||
|
|||
namespace FileSys { |
|||
|
|||
SaveData_Factory::SaveData_Factory(std::string nand_directory) |
|||
: nand_directory(std::move(nand_directory)) {} |
|||
|
|||
ResultVal<std::unique_ptr<FileSystemBackend>> SaveData_Factory::Open(const Path& path) { |
|||
std::string save_directory = GetFullPath(); |
|||
// Return an error if the save data doesn't actually exist.
|
|||
if (!FileUtil::IsDirectory(save_directory)) { |
|||
// TODO(Subv): Find out correct error code.
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
auto archive = std::make_unique<Disk_FileSystem>(save_directory); |
|||
return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive)); |
|||
} |
|||
|
|||
ResultCode SaveData_Factory::Format(const Path& path) { |
|||
LOG_WARNING(Service_FS, "Format archive {}", GetName()); |
|||
// Create the save data directory.
|
|||
if (!FileUtil::CreateFullPath(GetFullPath())) { |
|||
// TODO(Subv): Find the correct error code.
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
return RESULT_SUCCESS; |
|||
} |
|||
|
|||
ResultVal<ArchiveFormatInfo> SaveData_Factory::GetFormatInfo(const Path& path) const { |
|||
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); |
|||
// TODO(bunnei): Find the right error code for this
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
std::string SaveData_Factory::GetFullPath() const { |
|||
u64 title_id = Core::CurrentProcess()->program_id; |
|||
// TODO(Subv): Somehow obtain this value.
|
|||
u32 user = 0; |
|||
return fmt::format("{}save/{:016X}/{:08X}/", nand_directory, title_id, user); |
|||
} |
|||
|
|||
} // namespace FileSys
|
|||
@ -1,33 +0,0 @@ |
|||
// Copyright 2018 yuzu emulator team |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <memory> |
|||
#include <string> |
|||
#include "common/common_types.h" |
|||
#include "core/file_sys/filesystem.h" |
|||
#include "core/hle/result.h" |
|||
|
|||
namespace FileSys { |
|||
|
|||
/// File system interface to the SaveData archive |
|||
class SaveData_Factory final : public FileSystemFactory { |
|||
public: |
|||
explicit SaveData_Factory(std::string nand_directory); |
|||
|
|||
std::string GetName() const override { |
|||
return "SaveData_Factory"; |
|||
} |
|||
ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override; |
|||
ResultCode Format(const Path& path) override; |
|||
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; |
|||
|
|||
private: |
|||
std::string nand_directory; |
|||
|
|||
std::string GetFullPath() const; |
|||
}; |
|||
|
|||
} // namespace FileSys |
|||
@ -1,39 +0,0 @@ |
|||
// Copyright 2018 yuzu emulator team
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <memory>
|
|||
#include "common/common_types.h"
|
|||
#include "common/logging/log.h"
|
|||
#include "common/string_util.h"
|
|||
#include "core/core.h"
|
|||
#include "core/file_sys/disk_filesystem.h"
|
|||
#include "core/file_sys/sdmc_factory.h"
|
|||
|
|||
namespace FileSys { |
|||
|
|||
SDMC_Factory::SDMC_Factory(std::string sd_directory) : sd_directory(std::move(sd_directory)) {} |
|||
|
|||
ResultVal<std::unique_ptr<FileSystemBackend>> SDMC_Factory::Open(const Path& path) { |
|||
// Create the SD Card directory if it doesn't already exist.
|
|||
if (!FileUtil::IsDirectory(sd_directory)) { |
|||
FileUtil::CreateFullPath(sd_directory); |
|||
} |
|||
|
|||
auto archive = std::make_unique<Disk_FileSystem>(sd_directory); |
|||
return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive)); |
|||
} |
|||
|
|||
ResultCode SDMC_Factory::Format(const Path& path) { |
|||
LOG_ERROR(Service_FS, "Unimplemented Format archive {}", GetName()); |
|||
// TODO(Subv): Find the right error code for this
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
ResultVal<ArchiveFormatInfo> SDMC_Factory::GetFormatInfo(const Path& path) const { |
|||
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); |
|||
// TODO(bunnei): Find the right error code for this
|
|||
return ResultCode(-1); |
|||
} |
|||
|
|||
} // namespace FileSys
|
|||
@ -1,31 +0,0 @@ |
|||
// Copyright 2018 yuzu emulator team |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <memory> |
|||
#include <string> |
|||
#include "common/common_types.h" |
|||
#include "core/file_sys/filesystem.h" |
|||
#include "core/hle/result.h" |
|||
|
|||
namespace FileSys { |
|||
|
|||
/// File system interface to the SDCard archive |
|||
class SDMC_Factory final : public FileSystemFactory { |
|||
public: |
|||
explicit SDMC_Factory(std::string sd_directory); |
|||
|
|||
std::string GetName() const override { |
|||
return "SDMC_Factory"; |
|||
} |
|||
ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override; |
|||
ResultCode Format(const Path& path) override; |
|||
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; |
|||
|
|||
private: |
|||
std::string sd_directory; |
|||
}; |
|||
|
|||
} // namespace FileSys |
|||
@ -0,0 +1,187 @@ |
|||
// Copyright 2018 yuzu emulator team
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <algorithm>
|
|||
#include <numeric>
|
|||
#include "common/file_util.h"
|
|||
#include "core/file_sys/vfs.h"
|
|||
|
|||
namespace FileSys { |
|||
|
|||
VfsFile::~VfsFile() = default; |
|||
|
|||
std::string VfsFile::GetExtension() const { |
|||
return FileUtil::GetExtensionFromFilename(GetName()); |
|||
} |
|||
|
|||
VfsDirectory::~VfsDirectory() = default; |
|||
|
|||
boost::optional<u8> VfsFile::ReadByte(size_t offset) const { |
|||
u8 out{}; |
|||
size_t size = Read(&out, 1, offset); |
|||
if (size == 1) |
|||
return out; |
|||
|
|||
return boost::none; |
|||
} |
|||
|
|||
std::vector<u8> VfsFile::ReadBytes(size_t size, size_t offset) const { |
|||
std::vector<u8> out(size); |
|||
size_t read_size = Read(out.data(), size, offset); |
|||
out.resize(read_size); |
|||
return out; |
|||
} |
|||
|
|||
std::vector<u8> VfsFile::ReadAllBytes() const { |
|||
return ReadBytes(GetSize()); |
|||
} |
|||
|
|||
bool VfsFile::WriteByte(u8 data, size_t offset) { |
|||
return Write(&data, 1, offset) == 1; |
|||
} |
|||
|
|||
size_t VfsFile::WriteBytes(std::vector<u8> data, size_t offset) { |
|||
return Write(data.data(), data.size(), offset); |
|||
} |
|||
|
|||
std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(const std::string& path) const { |
|||
auto vec = FileUtil::SplitPathComponents(path); |
|||
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), |
|||
vec.end()); |
|||
if (vec.empty()) |
|||
return nullptr; |
|||
if (vec.size() == 1) |
|||
return GetFile(vec[0]); |
|||
auto dir = GetSubdirectory(vec[0]); |
|||
for (size_t i = 1; i < vec.size() - 1; ++i) { |
|||
if (dir == nullptr) |
|||
return nullptr; |
|||
dir = dir->GetSubdirectory(vec[i]); |
|||
} |
|||
if (dir == nullptr) |
|||
return nullptr; |
|||
return dir->GetFile(vec.back()); |
|||
} |
|||
|
|||
std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(const std::string& path) const { |
|||
if (IsRoot()) |
|||
return GetFileRelative(path); |
|||
|
|||
return GetParentDirectory()->GetFileAbsolute(path); |
|||
} |
|||
|
|||
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(const std::string& path) const { |
|||
auto vec = FileUtil::SplitPathComponents(path); |
|||
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), |
|||
vec.end()); |
|||
if (vec.empty()) |
|||
// return std::shared_ptr<VfsDirectory>(this);
|
|||
return nullptr; |
|||
auto dir = GetSubdirectory(vec[0]); |
|||
for (size_t i = 1; i < vec.size(); ++i) { |
|||
dir = dir->GetSubdirectory(vec[i]); |
|||
} |
|||
return dir; |
|||
} |
|||
|
|||
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryAbsolute(const std::string& path) const { |
|||
if (IsRoot()) |
|||
return GetDirectoryRelative(path); |
|||
|
|||
return GetParentDirectory()->GetDirectoryAbsolute(path); |
|||
} |
|||
|
|||
std::shared_ptr<VfsFile> VfsDirectory::GetFile(const std::string& name) const { |
|||
const auto& files = GetFiles(); |
|||
const auto iter = std::find_if(files.begin(), files.end(), |
|||
[&name](const auto& file1) { return name == file1->GetName(); }); |
|||
return iter == files.end() ? nullptr : *iter; |
|||
} |
|||
|
|||
std::shared_ptr<VfsDirectory> VfsDirectory::GetSubdirectory(const std::string& name) const { |
|||
const auto& subs = GetSubdirectories(); |
|||
const auto iter = std::find_if(subs.begin(), subs.end(), |
|||
[&name](const auto& file1) { return name == file1->GetName(); }); |
|||
return iter == subs.end() ? nullptr : *iter; |
|||
} |
|||
|
|||
bool VfsDirectory::IsRoot() const { |
|||
return GetParentDirectory() == nullptr; |
|||
} |
|||
|
|||
size_t VfsDirectory::GetSize() const { |
|||
const auto& files = GetFiles(); |
|||
const auto file_total = |
|||
std::accumulate(files.begin(), files.end(), 0ull, |
|||
[](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); }); |
|||
|
|||
const auto& sub_dir = GetSubdirectories(); |
|||
const auto subdir_total = |
|||
std::accumulate(sub_dir.begin(), sub_dir.end(), 0ull, |
|||
[](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); }); |
|||
|
|||
return file_total + subdir_total; |
|||
} |
|||
|
|||
bool VfsDirectory::DeleteSubdirectoryRecursive(const std::string& name) { |
|||
auto dir = GetSubdirectory(name); |
|||
if (dir == nullptr) |
|||
return false; |
|||
|
|||
bool success = true; |
|||
for (const auto& file : dir->GetFiles()) { |
|||
if (!DeleteFile(file->GetName())) |
|||
success = false; |
|||
} |
|||
|
|||
for (const auto& sdir : dir->GetSubdirectories()) { |
|||
if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) |
|||
success = false; |
|||
} |
|||
|
|||
return success; |
|||
} |
|||
|
|||
bool VfsDirectory::Copy(const std::string& src, const std::string& dest) { |
|||
const auto f1 = GetFile(src); |
|||
auto f2 = CreateFile(dest); |
|||
if (f1 == nullptr || f2 == nullptr) |
|||
return false; |
|||
|
|||
if (!f2->Resize(f1->GetSize())) { |
|||
DeleteFile(dest); |
|||
return false; |
|||
} |
|||
|
|||
return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize(); |
|||
} |
|||
|
|||
bool ReadOnlyVfsDirectory::IsWritable() const { |
|||
return false; |
|||
} |
|||
|
|||
bool ReadOnlyVfsDirectory::IsReadable() const { |
|||
return true; |
|||
} |
|||
|
|||
std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateSubdirectory(const std::string& name) { |
|||
return nullptr; |
|||
} |
|||
|
|||
std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFile(const std::string& name) { |
|||
return nullptr; |
|||
} |
|||
|
|||
bool ReadOnlyVfsDirectory::DeleteSubdirectory(const std::string& name) { |
|||
return false; |
|||
} |
|||
|
|||
bool ReadOnlyVfsDirectory::DeleteFile(const std::string& name) { |
|||
return false; |
|||
} |
|||
|
|||
bool ReadOnlyVfsDirectory::Rename(const std::string& name) { |
|||
return false; |
|||
} |
|||
} // namespace FileSys
|
|||
@ -0,0 +1,220 @@ |
|||
// Copyright 2018 yuzu emulator team |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <memory> |
|||
#include <string> |
|||
#include <type_traits> |
|||
#include <vector> |
|||
#include "boost/optional.hpp" |
|||
#include "common/common_types.h" |
|||
#include "common/file_util.h" |
|||
|
|||
namespace FileSys { |
|||
struct VfsFile; |
|||
struct VfsDirectory; |
|||
|
|||
// Convenience typedefs to use VfsDirectory and VfsFile |
|||
using VirtualDir = std::shared_ptr<FileSys::VfsDirectory>; |
|||
using VirtualFile = std::shared_ptr<FileSys::VfsFile>; |
|||
|
|||
// A class representing a file in an abstract filesystem. |
|||
struct VfsFile : NonCopyable { |
|||
virtual ~VfsFile(); |
|||
|
|||
// Retrieves the file name. |
|||
virtual std::string GetName() const = 0; |
|||
// Retrieves the extension of the file name. |
|||
virtual std::string GetExtension() const; |
|||
// Retrieves the size of the file. |
|||
virtual size_t GetSize() const = 0; |
|||
// Resizes the file to new_size. Returns whether or not the operation was successful. |
|||
virtual bool Resize(size_t new_size) = 0; |
|||
// Gets a pointer to the directory containing this file, returning nullptr if there is none. |
|||
virtual std::shared_ptr<VfsDirectory> GetContainingDirectory() const = 0; |
|||
|
|||
// Returns whether or not the file can be written to. |
|||
virtual bool IsWritable() const = 0; |
|||
// Returns whether or not the file can be read from. |
|||
virtual bool IsReadable() const = 0; |
|||
|
|||
// The primary method of reading from the file. Reads length bytes into data starting at offset |
|||
// into file. Returns number of bytes successfully read. |
|||
virtual size_t Read(u8* data, size_t length, size_t offset = 0) const = 0; |
|||
// The primary method of writing to the file. Writes length bytes from data starting at offset |
|||
// into file. Returns number of bytes successfully written. |
|||
virtual size_t Write(const u8* data, size_t length, size_t offset = 0) = 0; |
|||
|
|||
// Reads exactly one byte at the offset provided, returning boost::none on error. |
|||
virtual boost::optional<u8> ReadByte(size_t offset = 0) const; |
|||
// Reads size bytes starting at offset in file into a vector. |
|||
virtual std::vector<u8> ReadBytes(size_t size, size_t offset = 0) const; |
|||
// Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(), |
|||
// 0)' |
|||
virtual std::vector<u8> ReadAllBytes() const; |
|||
|
|||
// Reads an array of type T, size number_elements starting at offset. |
|||
// Returns the number of bytes (sizeof(T)*number_elements) read successfully. |
|||
template <typename T> |
|||
size_t ReadArray(T* data, size_t number_elements, size_t offset = 0) const { |
|||
static_assert(std::is_trivially_copyable<T>::value, |
|||
"Data type must be trivially copyable."); |
|||
|
|||
return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset); |
|||
} |
|||
|
|||
// Reads size bytes into the memory starting at data starting at offset into the file. |
|||
// Returns the number of bytes read successfully. |
|||
template <typename T> |
|||
size_t ReadBytes(T* data, size_t size, size_t offset = 0) const { |
|||
static_assert(std::is_trivially_copyable<T>::value, |
|||
"Data type must be trivially copyable."); |
|||
return Read(reinterpret_cast<u8*>(data), size, offset); |
|||
} |
|||
|
|||
// Reads one object of type T starting at offset in file. |
|||
// Returns the number of bytes read successfully (sizeof(T)). |
|||
template <typename T> |
|||
size_t ReadObject(T* data, size_t offset = 0) const { |
|||
static_assert(std::is_trivially_copyable<T>::value, |
|||
"Data type must be trivially copyable."); |
|||
return Read(reinterpret_cast<u8*>(data), sizeof(T), offset); |
|||
} |
|||
|
|||
// Writes exactly one byte to offset in file and retuns whether or not the byte was written |
|||
// successfully. |
|||
virtual bool WriteByte(u8 data, size_t offset = 0); |
|||
// Writes a vector of bytes to offset in file and returns the number of bytes successfully |
|||
// written. |
|||
virtual size_t WriteBytes(std::vector<u8> data, size_t offset = 0); |
|||
|
|||
// Writes an array of type T, size number_elements to offset in file. |
|||
// Returns the number of bytes (sizeof(T)*number_elements) written successfully. |
|||
template <typename T> |
|||
size_t WriteArray(T* data, size_t number_elements, size_t offset = 0) { |
|||
static_assert(std::is_trivially_copyable<T>::value, |
|||
"Data type must be trivially copyable."); |
|||
|
|||
return Write(data, number_elements * sizeof(T), offset); |
|||
} |
|||
|
|||
// Writes size bytes starting at memory location data to offset in file. |
|||
// Returns the number of bytes written successfully. |
|||
template <typename T> |
|||
size_t WriteBytes(T* data, size_t size, size_t offset = 0) { |
|||
static_assert(std::is_trivially_copyable<T>::value, |
|||
"Data type must be trivially copyable."); |
|||
return Write(reinterpret_cast<u8*>(data), size, offset); |
|||
} |
|||
|
|||
// Writes one object of type T to offset in file. |
|||
// Returns the number of bytes written successfully (sizeof(T)). |
|||
template <typename T> |
|||
size_t WriteObject(const T& data, size_t offset = 0) { |
|||
static_assert(std::is_trivially_copyable<T>::value, |
|||
"Data type must be trivially copyable."); |
|||
return Write(&data, sizeof(T), offset); |
|||
} |
|||
|
|||
// Renames the file to name. Returns whether or not the operation was successsful. |
|||
virtual bool Rename(const std::string& name) = 0; |
|||
}; |
|||
|
|||
// A class representing a directory in an abstract filesystem. |
|||
struct VfsDirectory : NonCopyable { |
|||
virtual ~VfsDirectory(); |
|||
|
|||
// Retrives the file located at path as if the current directory was root. Returns nullptr if |
|||
// not found. |
|||
virtual std::shared_ptr<VfsFile> GetFileRelative(const std::string& path) const; |
|||
// Calls GetFileRelative(path) on the root of the current directory. |
|||
virtual std::shared_ptr<VfsFile> GetFileAbsolute(const std::string& path) const; |
|||
|
|||
// Retrives the directory located at path as if the current directory was root. Returns nullptr |
|||
// if not found. |
|||
virtual std::shared_ptr<VfsDirectory> GetDirectoryRelative(const std::string& path) const; |
|||
// Calls GetDirectoryRelative(path) on the root of the current directory. |
|||
virtual std::shared_ptr<VfsDirectory> GetDirectoryAbsolute(const std::string& path) const; |
|||
|
|||
// Returns a vector containing all of the files in this directory. |
|||
virtual std::vector<std::shared_ptr<VfsFile>> GetFiles() const = 0; |
|||
// Returns the file with filename matching name. Returns nullptr if directory dosen't have a |
|||
// file with name. |
|||
virtual std::shared_ptr<VfsFile> GetFile(const std::string& name) const; |
|||
|
|||
// Returns a vector containing all of the subdirectories in this directory. |
|||
virtual std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const = 0; |
|||
// Returns the directory with name matching name. Returns nullptr if directory dosen't have a |
|||
// directory with name. |
|||
virtual std::shared_ptr<VfsDirectory> GetSubdirectory(const std::string& name) const; |
|||
|
|||
// Returns whether or not the directory can be written to. |
|||
virtual bool IsWritable() const = 0; |
|||
// Returns whether of not the directory can be read from. |
|||
virtual bool IsReadable() const = 0; |
|||
|
|||
// Returns whether or not the directory is the root of the current file tree. |
|||
virtual bool IsRoot() const; |
|||
|
|||
// Returns the name of the directory. |
|||
virtual std::string GetName() const = 0; |
|||
// Returns the total size of all files and subdirectories in this directory. |
|||
virtual size_t GetSize() const; |
|||
// Returns the parent directory of this directory. Returns nullptr if this directory is root or |
|||
// has no parent. |
|||
virtual std::shared_ptr<VfsDirectory> GetParentDirectory() const = 0; |
|||
|
|||
// Creates a new subdirectory with name name. Returns a pointer to the new directory or nullptr |
|||
// if the operation failed. |
|||
virtual std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) = 0; |
|||
// Creates a new file with name name. Returns a pointer to the new file or nullptr if the |
|||
// operation failed. |
|||
virtual std::shared_ptr<VfsFile> CreateFile(const std::string& name) = 0; |
|||
|
|||
// Deletes the subdirectory with name and returns true on success. |
|||
virtual bool DeleteSubdirectory(const std::string& name) = 0; |
|||
// Deletes all subdirectories and files of subdirectory with name recirsively and then deletes |
|||
// the subdirectory. Returns true on success. |
|||
virtual bool DeleteSubdirectoryRecursive(const std::string& name); |
|||
// Returnes whether or not the file with name name was deleted successfully. |
|||
virtual bool DeleteFile(const std::string& name) = 0; |
|||
|
|||
// Returns whether or not this directory was renamed to name. |
|||
virtual bool Rename(const std::string& name) = 0; |
|||
|
|||
// Returns whether or not the file with name src was successfully copied to a new file with name |
|||
// dest. |
|||
virtual bool Copy(const std::string& src, const std::string& dest); |
|||
|
|||
// Interprets the file with name file instead as a directory of type directory. |
|||
// The directory must have a constructor that takes a single argument of type |
|||
// std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a |
|||
// subdirectory in one call. |
|||
template <typename Directory> |
|||
bool InterpretAsDirectory(const std::string& file) { |
|||
auto file_p = GetFile(file); |
|||
if (file_p == nullptr) |
|||
return false; |
|||
return ReplaceFileWithSubdirectory(file, std::make_shared<Directory>(file_p)); |
|||
} |
|||
|
|||
protected: |
|||
// Backend for InterpretAsDirectory. |
|||
// Removes all references to file and adds a reference to dir in the directory's implementation. |
|||
virtual bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) = 0; |
|||
}; |
|||
|
|||
// A convenience partial-implementation of VfsDirectory that stubs out methods that should only work |
|||
// if writable. This is to avoid redundant empty methods everywhere. |
|||
struct ReadOnlyVfsDirectory : public VfsDirectory { |
|||
bool IsWritable() const override; |
|||
bool IsReadable() const override; |
|||
std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override; |
|||
std::shared_ptr<VfsFile> CreateFile(const std::string& name) override; |
|||
bool DeleteSubdirectory(const std::string& name) override; |
|||
bool DeleteFile(const std::string& name) override; |
|||
bool Rename(const std::string& name) override; |
|||
}; |
|||
} // namespace FileSys |
|||
@ -0,0 +1,92 @@ |
|||
// Copyright 2018 yuzu emulator team
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include "core/file_sys/vfs_offset.h"
|
|||
|
|||
namespace FileSys { |
|||
|
|||
OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, size_t size_, size_t offset_, |
|||
const std::string& name_) |
|||
: file(file_), offset(offset_), size(size_), name(name_) {} |
|||
|
|||
std::string OffsetVfsFile::GetName() const { |
|||
return name.empty() ? file->GetName() : name; |
|||
} |
|||
|
|||
size_t OffsetVfsFile::GetSize() const { |
|||
return size; |
|||
} |
|||
|
|||
bool OffsetVfsFile::Resize(size_t new_size) { |
|||
if (offset + new_size < file->GetSize()) { |
|||
size = new_size; |
|||
} else { |
|||
auto res = file->Resize(offset + new_size); |
|||
if (!res) |
|||
return false; |
|||
size = new_size; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
std::shared_ptr<VfsDirectory> OffsetVfsFile::GetContainingDirectory() const { |
|||
return file->GetContainingDirectory(); |
|||
} |
|||
|
|||
bool OffsetVfsFile::IsWritable() const { |
|||
return file->IsWritable(); |
|||
} |
|||
|
|||
bool OffsetVfsFile::IsReadable() const { |
|||
return file->IsReadable(); |
|||
} |
|||
|
|||
size_t OffsetVfsFile::Read(u8* data, size_t length, size_t r_offset) const { |
|||
return file->Read(data, TrimToFit(length, r_offset), offset + r_offset); |
|||
} |
|||
|
|||
size_t OffsetVfsFile::Write(const u8* data, size_t length, size_t r_offset) { |
|||
return file->Write(data, TrimToFit(length, r_offset), offset + r_offset); |
|||
} |
|||
|
|||
boost::optional<u8> OffsetVfsFile::ReadByte(size_t r_offset) const { |
|||
if (r_offset < size) |
|||
return file->ReadByte(offset + r_offset); |
|||
|
|||
return boost::none; |
|||
} |
|||
|
|||
std::vector<u8> OffsetVfsFile::ReadBytes(size_t r_size, size_t r_offset) const { |
|||
return file->ReadBytes(TrimToFit(r_size, r_offset), offset + r_offset); |
|||
} |
|||
|
|||
std::vector<u8> OffsetVfsFile::ReadAllBytes() const { |
|||
return file->ReadBytes(size, offset); |
|||
} |
|||
|
|||
bool OffsetVfsFile::WriteByte(u8 data, size_t r_offset) { |
|||
if (r_offset < size) |
|||
return file->WriteByte(data, offset + r_offset); |
|||
|
|||
return false; |
|||
} |
|||
|
|||
size_t OffsetVfsFile::WriteBytes(std::vector<u8> data, size_t r_offset) { |
|||
return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset); |
|||
} |
|||
|
|||
bool OffsetVfsFile::Rename(const std::string& name) { |
|||
return file->Rename(name); |
|||
} |
|||
|
|||
size_t OffsetVfsFile::GetOffset() const { |
|||
return offset; |
|||
} |
|||
|
|||
size_t OffsetVfsFile::TrimToFit(size_t r_size, size_t r_offset) const { |
|||
return std::max<size_t>(std::min<size_t>(size - r_offset, r_size), 0); |
|||
} |
|||
|
|||
} // namespace FileSys
|
|||
@ -0,0 +1,46 @@ |
|||
// Copyright 2018 yuzu emulator team |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include "core/file_sys/vfs.h" |
|||
|
|||
namespace FileSys { |
|||
|
|||
// An implementation of VfsFile that wraps around another VfsFile at a certain offset. |
|||
// Similar to seeking to an offset. |
|||
// If the file is writable, operations that would write past the end of the offset file will expand |
|||
// the size of this wrapper. |
|||
struct OffsetVfsFile : public VfsFile { |
|||
OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0, |
|||
const std::string& new_name = ""); |
|||
|
|||
std::string GetName() const override; |
|||
size_t GetSize() const override; |
|||
bool Resize(size_t new_size) override; |
|||
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; |
|||
bool IsWritable() const override; |
|||
bool IsReadable() const override; |
|||
size_t Read(u8* data, size_t length, size_t offset) const override; |
|||
size_t Write(const u8* data, size_t length, size_t offset) override; |
|||
boost::optional<u8> ReadByte(size_t offset) const override; |
|||
std::vector<u8> ReadBytes(size_t size, size_t offset) const override; |
|||
std::vector<u8> ReadAllBytes() const override; |
|||
bool WriteByte(u8 data, size_t offset) override; |
|||
size_t WriteBytes(std::vector<u8> data, size_t offset) override; |
|||
|
|||
bool Rename(const std::string& name) override; |
|||
|
|||
size_t GetOffset() const; |
|||
|
|||
private: |
|||
size_t TrimToFit(size_t r_size, size_t r_offset) const; |
|||
|
|||
std::shared_ptr<VfsFile> file; |
|||
size_t offset; |
|||
size_t size; |
|||
std::string name; |
|||
}; |
|||
|
|||
} // namespace FileSys |
|||
@ -0,0 +1,168 @@ |
|||
// Copyright 2018 yuzu emulator team
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include "common/common_paths.h"
|
|||
#include "common/logging/log.h"
|
|||
#include "core/file_sys/vfs_real.h"
|
|||
|
|||
namespace FileSys { |
|||
|
|||
static std::string PermissionsToCharArray(Mode perms) { |
|||
std::string out; |
|||
switch (perms) { |
|||
case Mode::Read: |
|||
out += "r"; |
|||
break; |
|||
case Mode::Write: |
|||
out += "r+"; |
|||
break; |
|||
case Mode::Append: |
|||
out += "a"; |
|||
break; |
|||
} |
|||
return out + "b"; |
|||
} |
|||
|
|||
RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_) |
|||
: backing(path_, PermissionsToCharArray(perms_).c_str()), path(path_), |
|||
parent_path(FileUtil::GetParentPath(path_)), |
|||
path_components(FileUtil::SplitPathComponents(path_)), |
|||
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), |
|||
perms(perms_) {} |
|||
|
|||
std::string RealVfsFile::GetName() const { |
|||
return path_components.back(); |
|||
} |
|||
|
|||
size_t RealVfsFile::GetSize() const { |
|||
return backing.GetSize(); |
|||
} |
|||
|
|||
bool RealVfsFile::Resize(size_t new_size) { |
|||
return backing.Resize(new_size); |
|||
} |
|||
|
|||
std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const { |
|||
return std::make_shared<RealVfsDirectory>(parent_path, perms); |
|||
} |
|||
|
|||
bool RealVfsFile::IsWritable() const { |
|||
return perms == Mode::Append || perms == Mode::Write; |
|||
} |
|||
|
|||
bool RealVfsFile::IsReadable() const { |
|||
return perms == Mode::Read || perms == Mode::Write; |
|||
} |
|||
|
|||
size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const { |
|||
if (!backing.Seek(offset, SEEK_SET)) |
|||
return 0; |
|||
return backing.ReadBytes(data, length); |
|||
} |
|||
|
|||
size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) { |
|||
if (!backing.Seek(offset, SEEK_SET)) |
|||
return 0; |
|||
return backing.WriteBytes(data, length); |
|||
} |
|||
|
|||
bool RealVfsFile::Rename(const std::string& name) { |
|||
const auto out = FileUtil::Rename(GetName(), name); |
|||
path = parent_path + DIR_SEP + name; |
|||
path_components = parent_components; |
|||
path_components.push_back(name); |
|||
backing = FileUtil::IOFile(path, PermissionsToCharArray(perms).c_str()); |
|||
return out; |
|||
} |
|||
|
|||
RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_) |
|||
: path(FileUtil::RemoveTrailingSlash(path_)), parent_path(FileUtil::GetParentPath(path)), |
|||
path_components(FileUtil::SplitPathComponents(path)), |
|||
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), |
|||
perms(perms_) { |
|||
if (!FileUtil::Exists(path) && (perms == Mode::Write || perms == Mode::Append)) |
|||
FileUtil::CreateDir(path); |
|||
unsigned size; |
|||
if (perms != Mode::Append) { |
|||
FileUtil::ForeachDirectoryEntry( |
|||
&size, path, |
|||
[this](unsigned* entries_out, const std::string& directory, |
|||
const std::string& filename) { |
|||
std::string full_path = directory + DIR_SEP + filename; |
|||
if (FileUtil::IsDirectory(full_path)) |
|||
subdirectories.emplace_back( |
|||
std::make_shared<RealVfsDirectory>(full_path, perms)); |
|||
else |
|||
files.emplace_back(std::make_shared<RealVfsFile>(full_path, perms)); |
|||
return true; |
|||
}); |
|||
} |
|||
} |
|||
|
|||
std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const { |
|||
return std::vector<std::shared_ptr<VfsFile>>(files); |
|||
} |
|||
|
|||
std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const { |
|||
return std::vector<std::shared_ptr<VfsDirectory>>(subdirectories); |
|||
} |
|||
|
|||
bool RealVfsDirectory::IsWritable() const { |
|||
return perms == Mode::Write || perms == Mode::Append; |
|||
} |
|||
|
|||
bool RealVfsDirectory::IsReadable() const { |
|||
return perms == Mode::Read || perms == Mode::Write; |
|||
} |
|||
|
|||
std::string RealVfsDirectory::GetName() const { |
|||
return path_components.back(); |
|||
} |
|||
|
|||
std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const { |
|||
if (path_components.size() <= 1) |
|||
return nullptr; |
|||
|
|||
return std::make_shared<RealVfsDirectory>(parent_path, perms); |
|||
} |
|||
|
|||
std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(const std::string& name) { |
|||
if (!FileUtil::CreateDir(path + DIR_SEP + name)) |
|||
return nullptr; |
|||
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(path + DIR_SEP + name, perms)); |
|||
return subdirectories.back(); |
|||
} |
|||
|
|||
std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(const std::string& name) { |
|||
if (!FileUtil::CreateEmptyFile(path + DIR_SEP + name)) |
|||
return nullptr; |
|||
files.emplace_back(std::make_shared<RealVfsFile>(path + DIR_SEP + name, perms)); |
|||
return files.back(); |
|||
} |
|||
|
|||
bool RealVfsDirectory::DeleteSubdirectory(const std::string& name) { |
|||
return FileUtil::DeleteDirRecursively(path + DIR_SEP + name); |
|||
} |
|||
|
|||
bool RealVfsDirectory::DeleteFile(const std::string& name) { |
|||
return FileUtil::Delete(path + DIR_SEP + name); |
|||
} |
|||
|
|||
bool RealVfsDirectory::Rename(const std::string& name) { |
|||
return FileUtil::Rename(path, parent_path + DIR_SEP + name); |
|||
} |
|||
|
|||
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { |
|||
auto iter = std::find(files.begin(), files.end(), file); |
|||
if (iter == files.end()) |
|||
return false; |
|||
|
|||
files[iter - files.begin()] = files.back(); |
|||
files.pop_back(); |
|||
|
|||
subdirectories.emplace_back(dir); |
|||
|
|||
return true; |
|||
} |
|||
} // namespace FileSys
|
|||
@ -0,0 +1,65 @@ |
|||
// Copyright 2018 yuzu emulator team |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/file_util.h" |
|||
#include "core/file_sys/filesystem.h" |
|||
#include "core/file_sys/vfs.h" |
|||
|
|||
namespace FileSys { |
|||
|
|||
// An implmentation of VfsFile that represents a file on the user's computer. |
|||
struct RealVfsFile : public VfsFile { |
|||
RealVfsFile(const std::string& name, Mode perms = Mode::Read); |
|||
|
|||
std::string GetName() const override; |
|||
size_t GetSize() const override; |
|||
bool Resize(size_t new_size) override; |
|||
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; |
|||
bool IsWritable() const override; |
|||
bool IsReadable() const override; |
|||
size_t Read(u8* data, size_t length, size_t offset) const override; |
|||
size_t Write(const u8* data, size_t length, size_t offset) override; |
|||
bool Rename(const std::string& name) override; |
|||
|
|||
private: |
|||
FileUtil::IOFile backing; |
|||
std::string path; |
|||
std::string parent_path; |
|||
std::vector<std::string> path_components; |
|||
std::vector<std::string> parent_components; |
|||
Mode perms; |
|||
}; |
|||
|
|||
// An implementation of VfsDirectory that represents a directory on the user's computer. |
|||
struct RealVfsDirectory : public VfsDirectory { |
|||
RealVfsDirectory(const std::string& path, Mode perms); |
|||
|
|||
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; |
|||
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; |
|||
bool IsWritable() const override; |
|||
bool IsReadable() const override; |
|||
std::string GetName() const override; |
|||
std::shared_ptr<VfsDirectory> GetParentDirectory() const override; |
|||
std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override; |
|||
std::shared_ptr<VfsFile> CreateFile(const std::string& name) override; |
|||
bool DeleteSubdirectory(const std::string& name) override; |
|||
bool DeleteFile(const std::string& name) override; |
|||
bool Rename(const std::string& name) override; |
|||
|
|||
protected: |
|||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; |
|||
|
|||
private: |
|||
std::string path; |
|||
std::string parent_path; |
|||
std::vector<std::string> path_components; |
|||
std::vector<std::string> parent_components; |
|||
Mode perms; |
|||
std::vector<std::shared_ptr<VfsFile>> files; |
|||
std::vector<std::shared_ptr<VfsDirectory>> subdirectories; |
|||
}; |
|||
|
|||
} // namespace FileSys |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue