aboutsummaryrefslogtreecommitdiff
path: root/src/core/file_sys
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/file_sys')
-rw-r--r--src/core/file_sys/directory.h37
-rw-r--r--src/core/file_sys/disk_filesystem.cpp237
-rw-r--r--src/core/file_sys/disk_filesystem.h84
-rw-r--r--src/core/file_sys/errors.h25
-rw-r--r--src/core/file_sys/filesystem.cpp6
-rw-r--r--src/core/file_sys/filesystem.h39
-rw-r--r--src/core/file_sys/partition_filesystem.cpp124
-rw-r--r--src/core/file_sys/partition_filesystem.h87
-rw-r--r--src/core/file_sys/program_metadata.cpp39
-rw-r--r--src/core/file_sys/romfs_factory.cpp8
-rw-r--r--src/core/file_sys/romfs_factory.h2
-rw-r--r--src/core/file_sys/romfs_filesystem.cpp56
-rw-r--r--src/core/file_sys/romfs_filesystem.h21
-rw-r--r--src/core/file_sys/savedata_factory.cpp54
-rw-r--r--src/core/file_sys/savedata_factory.h33
-rw-r--r--src/core/file_sys/sdmc_factory.cpp39
-rw-r--r--src/core/file_sys/sdmc_factory.h31
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