diff options
Diffstat (limited to 'src/core/file_sys')
| -rw-r--r-- | src/core/file_sys/directory.h | 37 | ||||
| -rw-r--r-- | src/core/file_sys/disk_filesystem.cpp | 237 | ||||
| -rw-r--r-- | src/core/file_sys/disk_filesystem.h | 84 | ||||
| -rw-r--r-- | src/core/file_sys/errors.h | 25 | ||||
| -rw-r--r-- | src/core/file_sys/filesystem.cpp | 6 | ||||
| -rw-r--r-- | src/core/file_sys/filesystem.h | 39 | ||||
| -rw-r--r-- | src/core/file_sys/partition_filesystem.cpp | 124 | ||||
| -rw-r--r-- | src/core/file_sys/partition_filesystem.h | 87 | ||||
| -rw-r--r-- | src/core/file_sys/program_metadata.cpp | 39 | ||||
| -rw-r--r-- | src/core/file_sys/romfs_factory.cpp | 8 | ||||
| -rw-r--r-- | src/core/file_sys/romfs_factory.h | 2 | ||||
| -rw-r--r-- | src/core/file_sys/romfs_filesystem.cpp | 56 | ||||
| -rw-r--r-- | src/core/file_sys/romfs_filesystem.h | 21 | ||||
| -rw-r--r-- | src/core/file_sys/savedata_factory.cpp | 54 | ||||
| -rw-r--r-- | src/core/file_sys/savedata_factory.h | 33 | ||||
| -rw-r--r-- | src/core/file_sys/sdmc_factory.cpp | 39 | ||||
| -rw-r--r-- | src/core/file_sys/sdmc_factory.h | 31 |
17 files changed, 805 insertions, 117 deletions
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h index 5a40bf472..c7639795e 100644 --- a/src/core/file_sys/directory.h +++ b/src/core/file_sys/directory.h @@ -6,34 +6,28 @@ #include <array> #include <cstddef> +#include "common/common_funcs.h" #include "common/common_types.h" +#include "core/file_sys/filesystem.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace namespace FileSys { -// Structure of a directory entry, from http://3dbrew.org/wiki/FSDir:Read#Entry_format -const size_t FILENAME_LENGTH = 0x20C / 2; +// Structure of a directory entry, from +// http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry +const size_t FILENAME_LENGTH = 0x300; struct Entry { - char16_t filename[FILENAME_LENGTH]; // Entry name (UTF-16, null-terminated) - std::array<char, 9> short_name; // 8.3 file name ('longfilename' -> 'LONGFI~1', null-terminated) - char unknown1; // unknown (observed values: 0x0A, 0x70, 0xFD) - std::array<char, 4> - extension; // 8.3 file extension (set to spaces for directories, null-terminated) - char unknown2; // unknown (always 0x01) - char unknown3; // unknown (0x00 or 0x08) - char is_directory; // directory flag - char is_hidden; // hidden flag - char is_archive; // archive flag - char is_read_only; // read-only flag - u64 file_size; // file size (for files only) + char filename[FILENAME_LENGTH]; + INSERT_PADDING_BYTES(4); + EntryType type; + INSERT_PADDING_BYTES(3); + u64 file_size; }; -static_assert(sizeof(Entry) == 0x228, "Directory Entry struct isn't exactly 0x228 bytes long!"); -static_assert(offsetof(Entry, short_name) == 0x20C, "Wrong offset for short_name in Entry."); -static_assert(offsetof(Entry, extension) == 0x216, "Wrong offset for extension in Entry."); -static_assert(offsetof(Entry, is_archive) == 0x21E, "Wrong offset for is_archive in Entry."); -static_assert(offsetof(Entry, file_size) == 0x220, "Wrong offset for file_size in Entry."); +static_assert(sizeof(Entry) == 0x310, "Directory Entry struct isn't exactly 0x310 bytes long!"); +static_assert(offsetof(Entry, type) == 0x304, "Wrong offset for type in Entry."); +static_assert(offsetof(Entry, file_size) == 0x308, "Wrong offset for file_size in Entry."); class DirectoryBackend : NonCopyable { public: @@ -46,7 +40,10 @@ public: * @param entries Buffer to read data into * @return Number of entries listed */ - virtual u32 Read(const u32 count, Entry* entries) = 0; + virtual u64 Read(const u64 count, Entry* entries) = 0; + + /// Returns the number of entries still left to read. + virtual u64 GetEntryCount() const = 0; /** * Close the directory diff --git a/src/core/file_sys/disk_filesystem.cpp b/src/core/file_sys/disk_filesystem.cpp new file mode 100644 index 000000000..8aa0e0aa4 --- /dev/null +++ b/src/core/file_sys/disk_filesystem.cpp @@ -0,0 +1,237 @@ +// 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 { + NGLOG_WARNING(Service_FS, "(STUBBED) called"); + // TODO(wwylele): Use correct error code + return ResultCode(-1); +} + +ResultCode Disk_FileSystem::DeleteDirectoryRecursively(const Path& path) const { + NGLOG_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 { + NGLOG_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; + } + + NGLOG_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; + } + + NGLOG_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 { + NGLOG_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 { + NGLOG_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 { + NGLOG_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 { + NGLOG_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]; + + NGLOG_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 diff --git a/src/core/file_sys/disk_filesystem.h b/src/core/file_sys/disk_filesystem.h new file mode 100644 index 000000000..591e39fda --- /dev/null +++ b/src/core/file_sys/disk_filesystem.h @@ -0,0 +1,84 @@ +// 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 diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h index be3224ef8..0ed7d2a0c 100644 --- a/src/core/file_sys/errors.h +++ b/src/core/file_sys/errors.h @@ -10,36 +10,17 @@ namespace FileSys { namespace ErrCodes { enum { - RomFSNotFound = 100, - ArchiveNotMounted = 101, - FileNotFound = 112, - PathNotFound = 113, - GameCardNotInserted = 141, - NotFound = 120, - FileAlreadyExists = 180, - DirectoryAlreadyExists = 185, - AlreadyExists = 190, - InvalidOpenFlags = 230, - DirectoryNotEmpty = 240, - NotAFile = 250, - NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive - ExeFSSectionNotFound = 567, - CommandNotAllowed = 630, - InvalidReadFlag = 700, - InvalidPath = 702, - WriteBeyondEnd = 705, - UnsupportedOpenFlags = 760, - IncorrectExeFSReadSize = 761, - UnexpectedFileOrDirectory = 770, + NotFound = 1, }; } +constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound); + // TODO(bunnei): Replace these with correct errors for Switch OS constexpr ResultCode ERROR_INVALID_PATH(ResultCode(-1)); constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(ResultCode(-1)); constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(ResultCode(-1)); constexpr ResultCode ERROR_FILE_NOT_FOUND(ResultCode(-1)); -constexpr ResultCode ERROR_PATH_NOT_FOUND(ResultCode(-1)); constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(ResultCode(-1)); constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(ResultCode(-1)); constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(ResultCode(-1)); diff --git a/src/core/file_sys/filesystem.cpp b/src/core/file_sys/filesystem.cpp index 82fdb3c46..87083878b 100644 --- a/src/core/file_sys/filesystem.cpp +++ b/src/core/file_sys/filesystem.cpp @@ -71,7 +71,7 @@ std::string Path::AsString() const { case Binary: default: // TODO(yuriks): Add assert - LOG_ERROR(Service_FS, "LowPathType cannot be converted to string!"); + NGLOG_ERROR(Service_FS, "LowPathType cannot be converted to string!"); return {}; } } @@ -87,7 +87,7 @@ std::u16string Path::AsU16Str() const { case Invalid: case Binary: // TODO(yuriks): Add assert - LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!"); + NGLOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!"); return {}; } @@ -115,7 +115,7 @@ std::vector<u8> Path::AsBinary() const { case Invalid: default: // TODO(yuriks): Add assert - LOG_ERROR(Service_FS, "LowPathType cannot be converted to binary!"); + NGLOG_ERROR(Service_FS, "LowPathType cannot be converted to binary!"); return {}; } } diff --git a/src/core/file_sys/filesystem.h b/src/core/file_sys/filesystem.h index 02705506b..295a3133e 100644 --- a/src/core/file_sys/filesystem.h +++ b/src/core/file_sys/filesystem.h @@ -27,11 +27,15 @@ enum LowPathType : u32 { Wchar = 4, }; -union Mode { - u32 hex; - BitField<0, 1, u32> read_flag; - BitField<1, 1, u32> write_flag; - BitField<2, 1, u32> create_flag; +enum EntryType : u8 { + Directory = 0, + File = 1, +}; + +enum class Mode : u32 { + Read = 1, + Write = 2, + Append = 4, }; class Path { @@ -86,21 +90,21 @@ public: * @param size The size of the new file, filled with zeroes * @return Result of the operation */ - virtual ResultCode CreateFile(const Path& path, u64 size) const = 0; + virtual ResultCode CreateFile(const std::string& path, u64 size) const = 0; /** * Delete a file specified by its path * @param path Path relative to the archive * @return Result of the operation */ - virtual ResultCode DeleteFile(const Path& path) const = 0; + virtual ResultCode DeleteFile(const std::string& path) const = 0; /** * Create a directory specified by its path * @param path Path relative to the archive * @return Result of the operation */ - virtual ResultCode CreateDirectory(const Path& path) const = 0; + virtual ResultCode CreateDirectory(const std::string& path) const = 0; /** * Delete a directory specified by its path @@ -122,7 +126,8 @@ public: * @param dest_path Destination path relative to the archive * @return Result of the operation */ - virtual ResultCode RenameFile(const Path& src_path, const Path& dest_path) const = 0; + virtual ResultCode RenameFile(const std::string& src_path, + const std::string& dest_path) const = 0; /** * Rename a Directory specified by its path @@ -138,21 +143,28 @@ public: * @param mode Mode to open the file with * @return Opened file, or error code */ - virtual ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const Path& path, - const Mode& mode) const = 0; + virtual ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path, + Mode mode) const = 0; /** * Open a directory specified by its path * @param path Path relative to the archive * @return Opened directory, or error code */ - virtual ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const = 0; + virtual ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory( + const std::string& path) const = 0; /** * Get the free space * @return The number of free bytes in the archive */ virtual u64 GetFreeSpaceSize() const = 0; + + /** + * Get the type of the specified path + * @return The type of the specified path or error code + */ + virtual ResultVal<EntryType> GetEntryType(const std::string& path) const = 0; }; class FileSystemFactory : NonCopyable { @@ -174,10 +186,9 @@ public: /** * Deletes the archive contents and then re-creates the base folder * @param path Path to the archive - * @param format_info Format information for the new archive * @return ResultCode of the operation, 0 on success */ - virtual ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) = 0; + virtual ResultCode Format(const Path& path) = 0; /** * Retrieves the format info about the archive with the specified path diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp new file mode 100644 index 000000000..808254ecc --- /dev/null +++ b/src/core/file_sys/partition_filesystem.cpp @@ -0,0 +1,124 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <utility> +#include "common/file_util.h" +#include "common/logging/log.h" +#include "core/file_sys/partition_filesystem.h" +#include "core/loader/loader.h" + +namespace FileSys { + +Loader::ResultStatus PartitionFilesystem::Load(const std::string& file_path, size_t offset) { + FileUtil::IOFile file(file_path, "rb"); + if (!file.IsOpen()) + return Loader::ResultStatus::Error; + + // At least be as large as the header + if (file.GetSize() < sizeof(Header)) + return Loader::ResultStatus::Error; + + // For cartridges, HFSs can get very large, so we need to calculate the size up to + // the actual content itself instead of just blindly reading in the entire file. + Header pfs_header; + if (!file.ReadBytes(&pfs_header, sizeof(Header))) + return Loader::ResultStatus::Error; + + bool is_hfs = (memcmp(pfs_header.magic.data(), "HFS", 3) == 0); + size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry); + size_t metadata_size = + sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size; + + // Actually read in now... + file.Seek(offset, SEEK_SET); + std::vector<u8> file_data(metadata_size); + + if (!file.ReadBytes(file_data.data(), metadata_size)) + return Loader::ResultStatus::Error; + + Loader::ResultStatus result = Load(file_data); + if (result != Loader::ResultStatus::Success) + NGLOG_ERROR(Service_FS, "Failed to load PFS from file {}!", file_path); + + return result; +} + +Loader::ResultStatus PartitionFilesystem::Load(const std::vector<u8>& file_data, size_t offset) { + size_t total_size = file_data.size() - offset; + if (total_size < sizeof(Header)) + return Loader::ResultStatus::Error; + + memcpy(&pfs_header, &file_data[offset], sizeof(Header)); + is_hfs = (memcmp(pfs_header.magic.data(), "HFS", 3) == 0); + + size_t entries_offset = offset + sizeof(Header); + size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry); + size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size); + for (u16 i = 0; i < pfs_header.num_entries; i++) { + FileEntry entry; + + memcpy(&entry.fs_entry, &file_data[entries_offset + (i * entry_size)], sizeof(FSEntry)); + entry.name = std::string(reinterpret_cast<const char*>( + &file_data[strtab_offset + entry.fs_entry.strtab_offset])); + pfs_entries.push_back(std::move(entry)); + } + + content_offset = strtab_offset + pfs_header.strtab_size; + + return Loader::ResultStatus::Success; +} + +u32 PartitionFilesystem::GetNumEntries() const { + return pfs_header.num_entries; +} + +u64 PartitionFilesystem::GetEntryOffset(int index) const { + if (index > GetNumEntries()) + return 0; + + return content_offset + pfs_entries[index].fs_entry.offset; +} + +u64 PartitionFilesystem::GetEntrySize(int index) const { + if (index > GetNumEntries()) + return 0; + + return pfs_entries[index].fs_entry.size; +} + +std::string PartitionFilesystem::GetEntryName(int index) const { + if (index > GetNumEntries()) + return ""; + + return pfs_entries[index].name; +} + +u64 PartitionFilesystem::GetFileOffset(const std::string& name) const { + for (u32 i = 0; i < pfs_header.num_entries; i++) { + if (pfs_entries[i].name == name) + return content_offset + pfs_entries[i].fs_entry.offset; + } + + return 0; +} + +u64 PartitionFilesystem::GetFileSize(const std::string& name) const { + for (u32 i = 0; i < pfs_header.num_entries; i++) { + if (pfs_entries[i].name == name) + return pfs_entries[i].fs_entry.size; + } + + return 0; +} + +void PartitionFilesystem::Print() const { + NGLOG_DEBUG(Service_FS, "Magic: {:.4}", pfs_header.magic.data()); + NGLOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries); + for (u32 i = 0; i < pfs_header.num_entries; i++) { + NGLOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes, at 0x{:X})", i, + pfs_entries[i].name.c_str(), pfs_entries[i].fs_entry.size, + GetFileOffset(pfs_entries[i].name)); + } +} +} // namespace FileSys diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h new file mode 100644 index 000000000..573c90057 --- /dev/null +++ b/src/core/file_sys/partition_filesystem.h @@ -0,0 +1,87 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <string> +#include <vector> +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/swap.h" + +namespace Loader { +enum class ResultStatus; +} + +namespace FileSys { + +/** + * Helper which implements an interface to parse PFS/HFS filesystems. + * Data can either be loaded from a file path or data with an offset into it. + */ +class PartitionFilesystem { +public: + Loader::ResultStatus Load(const std::string& file_path, size_t offset = 0); + Loader::ResultStatus Load(const std::vector<u8>& file_data, size_t offset = 0); + + u32 GetNumEntries() const; + u64 GetEntryOffset(int index) const; + u64 GetEntrySize(int index) const; + std::string GetEntryName(int index) const; + u64 GetFileOffset(const std::string& name) const; + u64 GetFileSize(const std::string& name) const; + + void Print() const; + +private: + struct Header { + std::array<char, 4> magic; + u32_le num_entries; + u32_le strtab_size; + INSERT_PADDING_BYTES(0x4); + }; + + static_assert(sizeof(Header) == 0x10, "PFS/HFS header structure size is wrong"); + +#pragma pack(push, 1) + struct FSEntry { + u64_le offset; + u64_le size; + u32_le strtab_offset; + }; + + static_assert(sizeof(FSEntry) == 0x14, "FS entry structure size is wrong"); + + struct PFSEntry { + FSEntry fs_entry; + INSERT_PADDING_BYTES(0x4); + }; + + static_assert(sizeof(PFSEntry) == 0x18, "PFS entry structure size is wrong"); + + struct HFSEntry { + FSEntry fs_entry; + u32_le hash_region_size; + INSERT_PADDING_BYTES(0x8); + std::array<char, 0x20> hash; + }; + + static_assert(sizeof(HFSEntry) == 0x40, "HFS entry structure size is wrong"); + +#pragma pack(pop) + + struct FileEntry { + FSEntry fs_entry; + std::string name; + }; + + Header pfs_header; + bool is_hfs; + size_t content_offset; + + std::vector<FileEntry> pfs_entries; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index a6dcebcc3..25a822891 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <cinttypes> #include "common/file_util.h" #include "common/logging/log.h" #include "core/file_sys/program_metadata.h" @@ -22,7 +21,7 @@ Loader::ResultStatus ProgramMetadata::Load(const std::string& file_path) { Loader::ResultStatus result = Load(file_data); if (result != Loader::ResultStatus::Success) - LOG_ERROR(Service_FS, "Failed to load NPDM from file %s!", file_path.c_str()); + NGLOG_ERROR(Service_FS, "Failed to load NPDM from file {}!", file_path); return result; } @@ -77,14 +76,14 @@ u64 ProgramMetadata::GetFilesystemPermissions() const { } void ProgramMetadata::Print() const { - LOG_DEBUG(Service_FS, "Magic: %.4s", npdm_header.magic.data()); - LOG_DEBUG(Service_FS, "Main thread priority: 0x%02x", npdm_header.main_thread_priority); - LOG_DEBUG(Service_FS, "Main thread core: %u", npdm_header.main_thread_cpu); - LOG_DEBUG(Service_FS, "Main thread stack size: 0x%x bytes", npdm_header.main_stack_size); - LOG_DEBUG(Service_FS, "Process category: %u", npdm_header.process_category); - LOG_DEBUG(Service_FS, "Flags: %02x", npdm_header.flags); - LOG_DEBUG(Service_FS, " > 64-bit instructions: %s", - npdm_header.has_64_bit_instructions ? "YES" : "NO"); + NGLOG_DEBUG(Service_FS, "Magic: {:.4}", npdm_header.magic.data()); + NGLOG_DEBUG(Service_FS, "Main thread priority: 0x{:02X}", npdm_header.main_thread_priority); + NGLOG_DEBUG(Service_FS, "Main thread core: {}", npdm_header.main_thread_cpu); + NGLOG_DEBUG(Service_FS, "Main thread stack size: 0x{:X} bytes", npdm_header.main_stack_size); + NGLOG_DEBUG(Service_FS, "Process category: {}", npdm_header.process_category); + NGLOG_DEBUG(Service_FS, "Flags: 0x{:02X}", npdm_header.flags); + NGLOG_DEBUG(Service_FS, " > 64-bit instructions: {}", + npdm_header.has_64_bit_instructions ? "YES" : "NO"); auto address_space = "Unknown"; switch (npdm_header.address_space_type) { @@ -96,19 +95,19 @@ void ProgramMetadata::Print() const { break; } - LOG_DEBUG(Service_FS, " > Address space: %s\n", address_space); + NGLOG_DEBUG(Service_FS, " > Address space: {}\n", address_space); // Begin ACID printing (potential perms, signed) - LOG_DEBUG(Service_FS, "Magic: %.4s", acid_header.magic.data()); - LOG_DEBUG(Service_FS, "Flags: %02x", acid_header.flags); - LOG_DEBUG(Service_FS, " > Is Retail: %s", acid_header.is_retail ? "YES" : "NO"); - LOG_DEBUG(Service_FS, "Title ID Min: %016" PRIX64, acid_header.title_id_min); - LOG_DEBUG(Service_FS, "Title ID Max: %016" PRIX64, acid_header.title_id_max); - LOG_DEBUG(Service_FS, "Filesystem Access: %016" PRIX64 "\n", acid_file_access.permissions); + NGLOG_DEBUG(Service_FS, "Magic: {:.4}", acid_header.magic.data()); + NGLOG_DEBUG(Service_FS, "Flags: 0x{:02X}", acid_header.flags); + NGLOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.is_retail ? "YES" : "NO"); + NGLOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min); + NGLOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max); + NGLOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions); // Begin ACI0 printing (actual perms, unsigned) - LOG_DEBUG(Service_FS, "Magic: %.4s", aci_header.magic.data()); - LOG_DEBUG(Service_FS, "Title ID: %016" PRIX64, aci_header.title_id); - LOG_DEBUG(Service_FS, "Filesystem Access: %016" PRIX64 "\n", aci_file_access.permissions); + NGLOG_DEBUG(Service_FS, "Magic: {:.4}", aci_header.magic.data()); + NGLOG_DEBUG(Service_FS, "Title ID: 0x{:016X}", aci_header.title_id); + NGLOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", aci_file_access.permissions); } } // namespace FileSys diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index e0de49f05..dc7591aca 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp @@ -14,7 +14,7 @@ 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!"); + NGLOG_ERROR(Service_FS, "Unable to read RomFS!"); } } @@ -23,14 +23,14 @@ ResultVal<std::unique_ptr<FileSystemBackend>> RomFS_Factory::Open(const Path& pa return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive)); } -ResultCode RomFS_Factory::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) { - LOG_ERROR(Service_FS, "Unimplemented Format archive %s", GetName().c_str()); +ResultCode RomFS_Factory::Format(const Path& path) { + NGLOG_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 %s", GetName().c_str()); + NGLOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); // TODO(bunnei): Find the right error code for this return ResultCode(-1); } diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h index 10ea13966..e0698e642 100644 --- a/src/core/file_sys/romfs_factory.h +++ b/src/core/file_sys/romfs_factory.h @@ -23,7 +23,7 @@ public: return "ArchiveFactory_RomFS"; } ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override; - ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override; + ResultCode Format(const Path& path) override; ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; private: diff --git a/src/core/file_sys/romfs_filesystem.cpp b/src/core/file_sys/romfs_filesystem.cpp index ca1463d7c..8e2bce687 100644 --- a/src/core/file_sys/romfs_filesystem.cpp +++ b/src/core/file_sys/romfs_filesystem.cpp @@ -14,73 +14,79 @@ std::string RomFS_FileSystem::GetName() const { return "RomFS"; } -ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const Path& path, - const Mode& mode) const { +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 Path& path) const { - LOG_CRITICAL(Service_FS, "Attempted to delete a file from an ROMFS archive (%s).", - GetName().c_str()); +ResultCode RomFS_FileSystem::DeleteFile(const std::string& path) const { + NGLOG_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 Path& src_path, const Path& dest_path) const { - LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive (%s).", - GetName().c_str()); +ResultCode RomFS_FileSystem::RenameFile(const std::string& src_path, + const std::string& dest_path) const { + NGLOG_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 (%s).", - GetName().c_str()); + NGLOG_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 (%s).", - GetName().c_str()); + NGLOG_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 Path& path, u64 size) const { - LOG_CRITICAL(Service_FS, "Attempted to create a file in an ROMFS archive (%s).", - GetName().c_str()); +ResultCode RomFS_FileSystem::CreateFile(const std::string& path, u64 size) const { + NGLOG_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 Path& path) const { - LOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive (%s).", - GetName().c_str()); +ResultCode RomFS_FileSystem::CreateDirectory(const std::string& path) const { + NGLOG_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 (%s).", - GetName().c_str()); + NGLOG_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 Path& path) const { + const std::string& path) const { + NGLOG_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"); + NGLOG_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 { + NGLOG_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=%llu, length=%zu", offset, length); + NGLOG_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); @@ -89,7 +95,7 @@ ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* 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"); + NGLOG_ERROR(Service_FS, "Attempted to write to ROMFS file"); // TODO(Subv): Find error code return MakeResult<size_t>(0); } @@ -99,7 +105,7 @@ u64 RomFS_Storage::GetSize() const { } bool RomFS_Storage::SetSize(const u64 size) const { - LOG_ERROR(Service_FS, "Attempted to set the size of an ROMFS file"); + NGLOG_ERROR(Service_FS, "Attempted to set the size of an ROMFS file"); return false; } diff --git a/src/core/file_sys/romfs_filesystem.h b/src/core/file_sys/romfs_filesystem.h index 900ea567a..ba9d85823 100644 --- a/src/core/file_sys/romfs_filesystem.h +++ b/src/core/file_sys/romfs_filesystem.h @@ -29,17 +29,19 @@ public: std::string GetName() const override; - ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const Path& path, - const Mode& mode) const override; - ResultCode DeleteFile(const Path& path) const override; - ResultCode RenameFile(const Path& src_path, const Path& dest_path) 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 Path& path, u64 size) const override; - ResultCode CreateDirectory(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 Path& 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; @@ -69,7 +71,10 @@ private: class ROMFSDirectory : public DirectoryBackend { public: - u32 Read(const u32 count, Entry* entries) override { + u64 Read(const u64 count, Entry* entries) override { + return 0; + } + u64 GetEntryCount() const override { return 0; } bool Close() const override { diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp new file mode 100644 index 000000000..c1be8fee4 --- /dev/null +++ b/src/core/file_sys/savedata_factory.cpp @@ -0,0 +1,54 @@ +// 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) { + NGLOG_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 { + NGLOG_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 diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h new file mode 100644 index 000000000..73a42aab6 --- /dev/null +++ b/src/core/file_sys/savedata_factory.h @@ -0,0 +1,33 @@ +// 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 diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp new file mode 100644 index 000000000..59ac3e0be --- /dev/null +++ b/src/core/file_sys/sdmc_factory.cpp @@ -0,0 +1,39 @@ +// 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) { + NGLOG_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 { + NGLOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); + // TODO(bunnei): Find the right error code for this + return ResultCode(-1); +} + +} // namespace FileSys diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h new file mode 100644 index 000000000..93becda25 --- /dev/null +++ b/src/core/file_sys/sdmc_factory.h @@ -0,0 +1,31 @@ +// 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 |
