From b36dee364e37c4b3295288d206f81dd6f1a03480 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Fri, 3 Aug 2018 11:44:40 -0400
Subject: [PATCH 01/12] filesystem: Remove unnecessary if conditions

---
 src/core/hle/service/filesystem/filesystem.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index e17d637e43..9b87e34845 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -59,7 +59,7 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64
 ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
     std::string path(FileUtil::SanitizePath(path_));
     auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
-    if (path == "/" || path == "\\") {
+    if (path.empty()) {
         // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
         return RESULT_SUCCESS;
     }

From 3bf488ce520a81811bf6e949e2153aabf4b713ea Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Fri, 3 Aug 2018 11:46:30 -0400
Subject: [PATCH 02/12] vfs: Add VfsFilesystem interface and default
 implementation

---
 src/core/file_sys/vfs.cpp | 148 ++++++++++++++++++++++++++++++++++++++
 src/core/file_sys/vfs.h   |  66 ++++++++++++++++-
 2 files changed, 211 insertions(+), 3 deletions(-)

diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index dae1c16ef0..24e1589626 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -4,12 +4,160 @@
 
 #include <algorithm>
 #include <numeric>
+#include <string>
+#include "common/common_paths.h"
 #include "common/file_util.h"
 #include "common/logging/backend.h"
 #include "core/file_sys/vfs.h"
 
 namespace FileSys {
 
+VfsFilesystem::VfsFilesystem(VirtualDir root_) : root(std::move(root_)) {}
+
+VfsFilesystem::~VfsFilesystem() = default;
+
+std::string VfsFilesystem::GetName() const {
+    return root->GetName();
+}
+
+bool VfsFilesystem::IsReadable() const {
+    return root->IsReadable();
+}
+
+bool VfsFilesystem::IsWritable() const {
+    return root->IsWritable();
+}
+
+VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
+    const auto path = FileUtil::SanitizePath(path_);
+    if (root->GetFileRelative(path) != nullptr)
+        return VfsEntryType::File;
+    if (root->GetDirectoryRelative(path) != nullptr)
+        return VfsEntryType::Directory;
+
+    return VfsEntryType::None;
+}
+
+VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
+    const auto path = FileUtil::SanitizePath(path_);
+    return root->GetFileRelative(path);
+}
+
+VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
+    const auto path = FileUtil::SanitizePath(path_);
+    return root->CreateFileRelative(path);
+}
+
+VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
+    const auto old_path = FileUtil::SanitizePath(old_path_);
+    const auto new_path = FileUtil::SanitizePath(new_path_);
+
+    // VfsDirectory impls are only required to implement copy across the current directory.
+    if (FileUtil::GetParentPath(old_path) == FileUtil::GetParentPath(new_path)) {
+        if (!root->Copy(FileUtil::GetFilename(old_path), FileUtil::GetFilename(new_path)))
+            return nullptr;
+        return OpenFile(new_path, Mode::ReadWrite);
+    }
+
+    // Do it using RawCopy. Non-default impls are encouraged to optimize this.
+    const auto old_file = OpenFile(old_path, Mode::Read);
+    if (old_file == nullptr)
+        return nullptr;
+    auto new_file = OpenFile(new_path, Mode::Read);
+    if (new_file != nullptr)
+        return nullptr;
+    new_file = CreateFile(new_path, Mode::Write);
+    if (new_file == nullptr)
+        return nullptr;
+    if (!VfsRawCopy(old_file, new_file))
+        return nullptr;
+    return new_file;
+}
+
+VirtualFile VfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
+    const auto old_path = FileUtil::SanitizePath(old_path_);
+    const auto new_path = FileUtil::SanitizePath(new_path_);
+
+    // Again, non-default impls are highly encouraged to provide a more optimized version of this.
+    auto out = CopyFile(old_path_, new_path_);
+    if (out == nullptr)
+        return nullptr;
+    if (DeleteFile(old_path))
+        return out;
+    return nullptr;
+}
+
+bool VfsFilesystem::DeleteFile(std::string_view path_) {
+    const auto path = FileUtil::SanitizePath(path_);
+    auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write);
+    if (parent == nullptr)
+        return false;
+    return parent->DeleteFile(FileUtil::GetFilename(path));
+}
+
+VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
+    const auto path = FileUtil::SanitizePath(path_);
+    return root->GetDirectoryRelative(path);
+}
+
+VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
+    const auto path = FileUtil::SanitizePath(path_);
+    return root->CreateDirectoryRelative(path);
+}
+
+VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) {
+    const auto old_path = FileUtil::SanitizePath(old_path_);
+    const auto new_path = FileUtil::SanitizePath(new_path_);
+
+    // Non-default impls are highly encouraged to provide a more optimized version of this.
+    auto old_dir = OpenDirectory(old_path, Mode::Read);
+    if (old_dir == nullptr)
+        return nullptr;
+    auto new_dir = OpenDirectory(new_path, Mode::Read);
+    if (new_dir != nullptr)
+        return nullptr;
+    new_dir = CreateDirectory(new_path, Mode::Write);
+    if (new_dir == nullptr)
+        return nullptr;
+
+    for (const auto& file : old_dir->GetFiles()) {
+        const auto x =
+            CopyFile(old_path + DIR_SEP + file->GetName(), new_path + DIR_SEP + file->GetName());
+        if (x == nullptr)
+            return nullptr;
+    }
+
+    for (const auto& dir : old_dir->GetSubdirectories()) {
+        const auto x =
+            CopyDirectory(old_path + DIR_SEP + dir->GetName(), new_path + DIR_SEP + dir->GetName());
+        if (x == nullptr)
+            return nullptr;
+    }
+
+    return new_dir;
+}
+
+VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path_, std::string_view new_path_) {
+    const auto old_path = FileUtil::SanitizePath(old_path_);
+    const auto new_path = FileUtil::SanitizePath(new_path_);
+
+    // Non-default impls are highly encouraged to provide a more optimized version of this.
+    auto out = CopyDirectory(old_path_, new_path_);
+    if (out == nullptr)
+        return nullptr;
+    if (DeleteDirectory(old_path))
+        return out;
+    return nullptr;
+}
+
+bool VfsFilesystem::DeleteDirectory(std::string_view path_) {
+    const auto path = FileUtil::SanitizePath(path_);
+    auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write);
+    if (parent == nullptr)
+        return false;
+    return parent->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path));
+}
+
 VfsFile::~VfsFile() = default;
 
 std::string VfsFile::GetExtension() const {
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index fab9e2b45f..9c7ef93b81 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -11,14 +11,74 @@
 #include <vector>
 #include "boost/optional.hpp"
 #include "common/common_types.h"
+#include "core/file_sys/mode.h"
 
 namespace FileSys {
+
+struct VfsFilesystem;
 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>;
+// Convenience typedefs to use Vfs* interfaces
+using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
+using VirtualDir = std::shared_ptr<VfsDirectory>;
+using VirtualFile = std::shared_ptr<VfsFile>;
+
+// An enumeration representing what can be at the end of a path in a VfsFilesystem
+enum class VfsEntryType {
+    None,
+    File,
+    Directory,
+};
+
+// A class represnting an abstract filesystem. A default implementation given the root VirtualDir is
+// provided for convenience, but if the Vfs implementation has any additional state or
+// functionality, they will need to override.
+struct VfsFilesystem : NonCopyable {
+    VfsFilesystem(VirtualDir root);
+    virtual ~VfsFilesystem();
+
+    // Gets the friendly name for the filesystem.
+    virtual std::string GetName() const;
+
+    // Return whether or not the user has read permissions on this filesystem.
+    virtual bool IsReadable() const;
+    // Return whether or not the user has write permission on this filesystem.
+    virtual bool IsWritable() const;
+
+    // Determine if the entry at path is non-existant, a file, or a directory.
+    virtual VfsEntryType GetEntryType(std::string_view path) const;
+
+    // Opens the file with path relative to root. If it doesn't exist, returns nullptr.
+    virtual VirtualFile OpenFile(std::string_view path, Mode perms);
+    // Creates a new, empty file at path
+    virtual VirtualFile CreateFile(std::string_view path, Mode perms);
+    // Copies the file from old_path to new_path, returning the new file on success and nullptr on
+    // failure.
+    virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path);
+    // Moves the file from old_path to new_path, returning the moved file on success and nullptr on
+    // failure.
+    virtual VirtualFile MoveFile(std::string_view old_path, std::string_view new_path);
+    // Deletes the file with path relative to root, returing true on success.
+    virtual bool DeleteFile(std::string_view path);
+
+    // Opens the directory with path relative to root. If it doesn't exist, returns nullptr.
+    virtual VirtualDir OpenDirectory(std::string_view path, Mode perms);
+    // Creates a new, empty directory at path
+    virtual VirtualDir CreateDirectory(std::string_view path, Mode perms);
+    // Copies the directory from old_path to new_path, returning the new directory on success and
+    // nullptr on failure.
+    virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path);
+    // Moves the directory from old_path to new_path, returning the moved directory on success and
+    // nullptr on failure.
+    virtual VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path);
+    // Deletes the directory with path relative to root, returing true on success.
+    virtual bool DeleteDirectory(std::string_view path);
+
+protected:
+    // Root directory in default implementation.
+    VirtualDir root;
+};
 
 // A class representing a file in an abstract filesystem.
 struct VfsFile : NonCopyable {

From 3f82dad1e411130e309074a1547fb2104257f95d Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Fri, 3 Aug 2018 11:47:35 -0400
Subject: [PATCH 03/12] file_util: Add platform-specific slash option to
 SanitizePath

---
 src/common/file_util.cpp | 16 +++++++++++++---
 src/common/file_util.h   |  5 +++--
 2 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 7aeda737f4..190cac6d9d 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -884,11 +884,21 @@ std::string_view RemoveTrailingSlash(std::string_view path) {
     return path;
 }
 
-std::string SanitizePath(std::string_view path_) {
+std::string SanitizePath(std::string_view path_, bool with_platform_slashes) {
     std::string path(path_);
-    std::replace(path.begin(), path.end(), '\\', '/');
+    char type1 = '\\';
+    char type2 = '/';
+
+    if (with_platform_slashes) {
+#ifdef _WIN32
+        type1 = '/';
+        type2 = '\\';
+#endif
+    }
+
+    std::replace(path.begin(), path.end(), type1, type2);
     path.erase(std::unique(path.begin(), path.end(),
-                           [](char c1, char c2) { return c1 == '/' && c2 == '/'; }),
+                           [type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
                path.end());
     return std::string(RemoveTrailingSlash(path));
 }
diff --git a/src/common/file_util.h b/src/common/file_util.h
index d0987fb572..ca63d74661 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -182,8 +182,9 @@ std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t la
     return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
 }
 
-// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'.
-std::string SanitizePath(std::string_view path);
+// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
+// if windows and with_platform_slashes is true.
+std::string SanitizePath(std::string_view path, bool with_platform_slashes = false);
 
 // simple wrapper for cstdlib file functions to
 // hopefully will make error checking easier

From 2de2ec25d6e8971809f98ebe36996c00e0f89f2d Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Fri, 3 Aug 2018 11:50:00 -0400
Subject: [PATCH 04/12] vfs: Add RealVfsFilesystem implementation

---
 src/core/file_sys/vfs_real.cpp | 327 +++++++++++++++++++++++++--------
 src/core/file_sys/vfs_real.h   |  44 ++++-
 2 files changed, 290 insertions(+), 81 deletions(-)

diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 82d54da4a3..2923a8e6a3 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -36,8 +36,164 @@ static std::string ModeFlagsToString(Mode mode) {
     return mode_str;
 }
 
-RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_)
-    : backing(path_, ModeFlagsToString(perms_).c_str()), path(path_),
+RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {}
+
+std::string RealVfsFilesystem::GetName() const {
+    return "Real";
+}
+
+bool RealVfsFilesystem::IsReadable() const {
+    return true;
+}
+
+bool RealVfsFilesystem::IsWritable() const {
+    return true;
+}
+
+VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
+    const auto path = FileUtil::SanitizePath(path_, true);
+    if (!FileUtil::Exists(path))
+        return VfsEntryType::None;
+    if (FileUtil::IsDirectory(path))
+        return VfsEntryType::Directory;
+
+    return VfsEntryType::File;
+}
+
+VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
+    const auto path = FileUtil::SanitizePath(path_, true);
+    if (cache.find(path) != cache.end()) {
+        auto weak = cache[path];
+        if (!weak.expired()) {
+            return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms));
+        }
+    }
+
+    if (!FileUtil::Exists(path) && (perms & Mode::WriteAppend) != 0)
+        FileUtil::CreateEmptyFile(path);
+
+    auto backing = std::make_shared<FileUtil::IOFile>(path, ModeFlagsToString(perms).c_str());
+    cache[path] = backing;
+
+    // Cannot use make_shared as RealVfsFile constructor is private
+    return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms));
+}
+
+VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
+    const auto path = FileUtil::SanitizePath(path_, true);
+    if (!FileUtil::Exists(path) && !FileUtil::CreateEmptyFile(path))
+        return nullptr;
+    return OpenFile(path, perms);
+}
+
+VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
+    const auto old_path = FileUtil::SanitizePath(old_path_, true);
+    const auto new_path = FileUtil::SanitizePath(new_path_, true);
+
+    if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
+        FileUtil::IsDirectory(old_path) || !FileUtil::Copy(old_path, new_path))
+        return nullptr;
+    return OpenFile(new_path, Mode::ReadWrite);
+}
+
+VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
+    const auto old_path = FileUtil::SanitizePath(old_path_, true);
+    const auto new_path = FileUtil::SanitizePath(new_path_, true);
+
+    if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
+        FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
+        return nullptr;
+
+    if (cache.find(old_path) != cache.end()) {
+        auto cached = cache[old_path];
+        if (!cached.expired()) {
+            auto file = cached.lock();
+            file->Open(new_path, "r+b");
+            cache.erase(old_path);
+            cache[new_path] = file;
+        }
+    }
+    return OpenFile(new_path, Mode::ReadWrite);
+}
+
+bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
+    const auto path = FileUtil::SanitizePath(path_, true);
+    if (cache.find(path) != cache.end()) {
+        if (!cache[path].expired())
+            cache[path].lock()->Close();
+        cache.erase(path);
+    }
+    return FileUtil::Delete(path);
+}
+
+VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
+    const auto path = FileUtil::SanitizePath(path_, true);
+    // Cannot use make_shared as RealVfsDirectory constructor is private
+    return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
+}
+
+VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
+    const auto path = FileUtil::SanitizePath(path_, true);
+    if (!FileUtil::Exists(path) && !FileUtil::CreateDir(path))
+        return nullptr;
+    // Cannot use make_shared as RealVfsDirectory constructor is private
+    return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
+}
+
+VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
+                                            std::string_view new_path_) {
+    const auto old_path = FileUtil::SanitizePath(old_path_, true);
+    const auto new_path = FileUtil::SanitizePath(new_path_, true);
+    if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
+        !FileUtil::IsDirectory(old_path))
+        return nullptr;
+    FileUtil::CopyDir(old_path, new_path);
+    return OpenDirectory(new_path, Mode::ReadWrite);
+}
+
+VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
+                                            std::string_view new_path_) {
+    const auto old_path = FileUtil::SanitizePath(old_path_, true);
+    const auto new_path = FileUtil::SanitizePath(new_path_, true);
+    if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
+        FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
+        return nullptr;
+
+    for (auto& kv : cache) {
+        // Path in cache starts with old_path
+        if (kv.first.rfind(old_path, 0) == 0) {
+            const auto file_old_path = FileUtil::SanitizePath(kv.first, true);
+            const auto file_new_path =
+                FileUtil::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()), true);
+            auto cached = cache[file_old_path];
+            if (!cached.expired()) {
+                auto file = cached.lock();
+                file->Open(file_new_path, "r+b");
+                cache.erase(file_old_path);
+                cache[file_new_path] = file;
+            }
+        }
+    }
+
+    return OpenDirectory(new_path, Mode::ReadWrite);
+}
+
+bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
+    const auto path = FileUtil::SanitizePath(path_, true);
+    for (auto& kv : cache) {
+        // Path in cache starts with old_path
+        if (kv.first.rfind(path, 0) == 0) {
+            if (!cache[kv.first].expired())
+                cache[kv.first].lock()->Close();
+            cache.erase(kv.first);
+        }
+    }
+    return FileUtil::DeleteDirRecursively(path);
+}
+
+RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOFile> backing_,
+                         const std::string& path_, Mode perms_)
+    : base(base_), backing(std::move(backing_)), path(path_),
       parent_path(FileUtil::GetParentPath(path_)),
       path_components(FileUtil::SplitPathComponents(path_)),
       parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
@@ -48,15 +204,15 @@ std::string RealVfsFile::GetName() const {
 }
 
 size_t RealVfsFile::GetSize() const {
-    return backing.GetSize();
+    return backing->GetSize();
 }
 
 bool RealVfsFile::Resize(size_t new_size) {
-    return backing.Resize(new_size);
+    return backing->Resize(new_size);
 }
 
 std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const {
-    return std::make_shared<RealVfsDirectory>(parent_path, perms);
+    return base.OpenDirectory(parent_path, perms);
 }
 
 bool RealVfsFile::IsWritable() const {
@@ -68,62 +224,118 @@ bool RealVfsFile::IsReadable() const {
 }
 
 size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const {
-    if (!backing.Seek(offset, SEEK_SET))
+    if (!backing->Seek(offset, SEEK_SET))
         return 0;
-    return backing.ReadBytes(data, length);
+    return backing->ReadBytes(data, length);
 }
 
 size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) {
-    if (!backing.Seek(offset, SEEK_SET))
+    if (!backing->Seek(offset, SEEK_SET))
         return 0;
-    return backing.WriteBytes(data, length);
+    return backing->WriteBytes(data, length);
 }
 
 bool RealVfsFile::Rename(std::string_view name) {
-    std::string name_str(name.begin(), name.end());
-    const auto out = FileUtil::Rename(GetName(), name_str);
+    return base.MoveFile(path, parent_path + DIR_SEP + std::string(name)) != nullptr;
+}
 
-    path = (parent_path + DIR_SEP).append(name);
-    path_components = parent_components;
-    path_components.push_back(std::move(name_str));
-    backing = FileUtil::IOFile(path, ModeFlagsToString(perms).c_str());
+bool RealVfsFile::Close() {
+    return backing->Close();
+}
+
+// TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if
+// constexpr' because there is a compile error in the branch not used.
+
+template <>
+std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const {
+    if (perms == Mode::Append)
+        return {};
+
+    std::vector<VirtualFile> out;
+    FileUtil::ForeachDirectoryEntry(
+        nullptr, path,
+        [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
+            const std::string full_path = directory + DIR_SEP + filename;
+            if (!FileUtil::IsDirectory(full_path))
+                out.emplace_back(base.OpenFile(full_path, perms));
+            return true;
+        });
 
     return out;
 }
 
-bool RealVfsFile::Close() {
-    return backing.Close();
+template <>
+std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const {
+    if (perms == Mode::Append)
+        return {};
+
+    std::vector<VirtualDir> out;
+    FileUtil::ForeachDirectoryEntry(
+        nullptr, path,
+        [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
+            const std::string full_path = directory + DIR_SEP + filename;
+            if (FileUtil::IsDirectory(full_path))
+                out.emplace_back(base.OpenDirectory(full_path, perms));
+            return true;
+        });
+
+    return out;
 }
 
-RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_)
-    : path(FileUtil::RemoveTrailingSlash(path_)), parent_path(FileUtil::GetParentPath(path)),
+RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_)
+    : base(base_), 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::WriteAppend)
         FileUtil::CreateDir(path);
+}
 
-    if (perms == Mode::Append)
-        return;
+std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const {
+    const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
+    if (!FileUtil::Exists(full_path))
+        return nullptr;
+    return base.OpenFile(full_path, perms);
+}
 
-    FileUtil::ForeachDirectoryEntry(
-        nullptr, path,
-        [this](u64* 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::shared_ptr<VfsDirectory> RealVfsDirectory::GetDirectoryRelative(std::string_view path) const {
+    const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
+    if (!FileUtil::Exists(full_path))
+        return nullptr;
+    return base.OpenDirectory(full_path, perms);
+}
+
+std::shared_ptr<VfsFile> RealVfsDirectory::GetFile(std::string_view name) const {
+    return GetFileRelative(name);
+}
+
+std::shared_ptr<VfsDirectory> RealVfsDirectory::GetSubdirectory(std::string_view name) const {
+    return GetDirectoryRelative(name);
+}
+
+std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view path) {
+    const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
+    return base.CreateFile(full_path, perms);
+}
+
+std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) {
+    const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
+    auto parent = std::string(FileUtil::GetParentPath(full_path));
+    return base.CreateDirectory(full_path, perms);
+}
+
+bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
+    auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(name));
+    return base.DeleteDirectory(full_path);
 }
 
 std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const {
-    return files;
+    return IterateEntries<RealVfsFile, VfsFile>();
 }
 
 std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const {
-    return subdirectories;
+    return IterateEntries<RealVfsDirectory, VfsDirectory>();
 }
 
 bool RealVfsDirectory::IsWritable() const {
@@ -142,57 +354,32 @@ std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const {
     if (path_components.size() <= 1)
         return nullptr;
 
-    return std::make_shared<RealVfsDirectory>(parent_path, perms);
+    return base.OpenDirectory(parent_path, perms);
 }
 
 std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(std::string_view name) {
     const std::string subdir_path = (path + DIR_SEP).append(name);
-
-    if (!FileUtil::CreateDir(subdir_path)) {
-        return nullptr;
-    }
-
-    subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(subdir_path, perms));
-    return subdirectories.back();
+    return base.CreateDirectory(subdir_path, perms);
 }
 
 std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(std::string_view name) {
     const std::string file_path = (path + DIR_SEP).append(name);
-
-    if (!FileUtil::CreateEmptyFile(file_path)) {
-        return nullptr;
-    }
-
-    files.emplace_back(std::make_shared<RealVfsFile>(file_path, perms));
-    return files.back();
+    return base.CreateFile(file_path, perms);
 }
 
 bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) {
     const std::string subdir_path = (path + DIR_SEP).append(name);
-
-    return FileUtil::DeleteDirRecursively(subdir_path);
+    return base.DeleteDirectory(subdir_path);
 }
 
 bool RealVfsDirectory::DeleteFile(std::string_view name) {
-    const auto file = GetFile(name);
-
-    if (file == nullptr) {
-        return false;
-    }
-
-    files.erase(std::find(files.begin(), files.end(), file));
-
-    auto real_file = std::static_pointer_cast<RealVfsFile>(file);
-    real_file->Close();
-
     const std::string file_path = (path + DIR_SEP).append(name);
-    return FileUtil::Delete(file_path);
+    return base.DeleteFile(file_path);
 }
 
 bool RealVfsDirectory::Rename(std::string_view name) {
     const std::string new_name = (parent_path + DIR_SEP).append(name);
-
-    return FileUtil::Rename(path, new_name);
+    return base.MoveFile(path, new_name) != nullptr;
 }
 
 std::string RealVfsDirectory::GetFullPath() const {
@@ -202,16 +389,6 @@ std::string RealVfsDirectory::GetFullPath() const {
 }
 
 bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
-    const auto iter = std::find(files.begin(), files.end(), file);
-    if (iter == files.end())
-        return false;
-
-    const std::ptrdiff_t offset = std::distance(files.begin(), iter);
-    files[offset] = files.back();
-    files.pop_back();
-
-    subdirectories.emplace_back(std::move(dir));
-
-    return true;
+    return false;
 }
 } // namespace FileSys
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index 243d585767..dbb381a2a1 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -6,18 +6,45 @@
 
 #include <string_view>
 
+#include <boost/container/flat_map.hpp>
 #include "common/file_util.h"
 #include "core/file_sys/mode.h"
 #include "core/file_sys/vfs.h"
 
 namespace FileSys {
 
+class RealVfsFilesystem : public VfsFilesystem {
+public:
+    RealVfsFilesystem();
+
+    std::string GetName() const override;
+    bool IsReadable() const override;
+    bool IsWritable() const override;
+    VfsEntryType GetEntryType(std::string_view path) const override;
+    VirtualFile OpenFile(std::string_view path, Mode perms = Mode::Read) override;
+    VirtualFile CreateFile(std::string_view path, Mode perms = Mode::ReadWrite) override;
+    VirtualFile CopyFile(std::string_view old_path, std::string_view new_path) override;
+    VirtualFile MoveFile(std::string_view old_path, std::string_view new_path) override;
+    bool DeleteFile(std::string_view path) override;
+    VirtualDir OpenDirectory(std::string_view path, Mode perms = Mode::Read) override;
+    VirtualDir CreateDirectory(std::string_view path, Mode perms = Mode::ReadWrite) override;
+    VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path) override;
+    VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path) override;
+    bool DeleteDirectory(std::string_view path) override;
+
+private:
+    boost::container::flat_map<std::string, std::weak_ptr<FileUtil::IOFile>> cache;
+};
+
 // An implmentation of VfsFile that represents a file on the user's computer.
-struct RealVfsFile : public VfsFile {
-    friend struct RealVfsDirectory;
+class RealVfsFile : public VfsFile {
+    friend class RealVfsDirectory;
+    friend class RealVfsFilesystem;
 
-    RealVfsFile(const std::string& name, Mode perms = Mode::Read);
+    RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing,
+                const std::string& path, Mode perms = Mode::Read);
 
+public:
     std::string GetName() const override;
     size_t GetSize() const override;
     bool Resize(size_t new_size) override;
@@ -31,7 +58,8 @@ struct RealVfsFile : public VfsFile {
 private:
     bool Close();
 
-    FileUtil::IOFile backing;
+    RealVfsFilesystem& base;
+    std::shared_ptr<FileUtil::IOFile> backing;
     std::string path;
     std::string parent_path;
     std::vector<std::string> path_components;
@@ -40,9 +68,12 @@ private:
 };
 
 // An implementation of VfsDirectory that represents a directory on the user's computer.
-struct RealVfsDirectory : public VfsDirectory {
-    RealVfsDirectory(const std::string& path, Mode perms = Mode::Read);
+class RealVfsDirectory : public VfsDirectory {
+    friend class RealVfsFilesystem;
 
+    RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
+
+public:
     std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
     std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
     bool IsWritable() const override;
@@ -60,6 +91,7 @@ protected:
     bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
 
 private:
+    RealVfsFilesystem& base;
     std::string path;
     std::string parent_path;
     std::vector<std::string> path_components;

From aaa8fdea527d635e6503a1411a06938325cba216 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Fri, 3 Aug 2018 11:50:27 -0400
Subject: [PATCH 05/12] vfs: Add unreachable assert to file permissions
 converter

---
 src/core/file_sys/vfs_real.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 2923a8e6a3..21ea35aaf5 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -6,7 +6,7 @@
 #include <cstddef>
 #include <iterator>
 #include <utility>
-
+#include "common/assert.h"
 #include "common/common_paths.h"
 #include "common/logging/log.h"
 #include "core/file_sys/vfs_real.h"
@@ -29,6 +29,8 @@ static std::string ModeFlagsToString(Mode mode) {
             mode_str = "a";
         else if (mode & Mode::Write)
             mode_str = "w";
+        else
+            UNREACHABLE_MSG("Invalid file open mode: {:02X}", static_cast<u8>(mode));
     }
 
     mode_str += "b";

From 4b471f0554146463f3b82eed14ff3922a5584e9f Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Fri, 3 Aug 2018 11:51:48 -0400
Subject: [PATCH 06/12] core: Port core to VfsFilesystem for file access

---
 src/core/core.cpp                              |  8 ++++++--
 src/core/core.h                                | 12 ++++++++++++
 src/core/hle/service/filesystem/filesystem.cpp | 14 +++++++-------
 src/core/hle/service/filesystem/filesystem.h   |  2 +-
 src/core/hle/service/service.cpp               |  4 ++--
 src/core/hle/service/service.h                 |  7 ++++++-
 src/yuzu/game_list.cpp                         |  7 ++++---
 src/yuzu/game_list.h                           |  3 ++-
 src/yuzu/game_list_p.h                         |  3 ++-
 src/yuzu/main.cpp                              | 10 ++++++----
 src/yuzu/main.h                                |  3 +++
 src/yuzu_cmd/yuzu.cpp                          |  1 +
 12 files changed, 52 insertions(+), 22 deletions(-)

diff --git a/src/core/core.cpp b/src/core/core.cpp
index 085ba68d0f..69c45c0265 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -89,7 +89,7 @@ System::ResultStatus System::SingleStep() {
 }
 
 System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& filepath) {
-    app_loader = Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(filepath));
+    app_loader = Loader::GetLoader(virtual_filesystem->OpenFile(filepath, FileSys::Mode::Read));
 
     if (!app_loader) {
         LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
@@ -174,6 +174,10 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {
 
     CoreTiming::Init();
 
+    // Create a default fs if one doesn't already exist.
+    if (virtual_filesystem == nullptr)
+        virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
+
     current_process = Kernel::Process::Create("main");
 
     cpu_barrier = std::make_shared<CpuBarrier>();
@@ -186,7 +190,7 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {
     service_manager = std::make_shared<Service::SM::ServiceManager>();
 
     Kernel::Init();
-    Service::Init(service_manager);
+    Service::Init(service_manager, virtual_filesystem);
     GDBStub::Init();
 
     renderer = VideoCore::CreateRenderer(emu_window);
diff --git a/src/core/core.h b/src/core/core.h
index c8ca4b247e..7cf7ea4e1f 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -17,6 +17,8 @@
 #include "core/memory.h"
 #include "core/perf_stats.h"
 #include "core/telemetry_session.h"
+#include "file_sys/vfs_real.h"
+#include "hle/service/filesystem/filesystem.h"
 #include "video_core/debug_utils/debug_utils.h"
 #include "video_core/gpu.h"
 
@@ -211,6 +213,14 @@ public:
         return debug_context;
     }
 
+    void SetFilesystem(FileSys::VirtualFilesystem vfs) {
+        virtual_filesystem = std::move(vfs);
+    }
+
+    FileSys::VirtualFilesystem GetFilesystem() const {
+        return virtual_filesystem;
+    }
+
 private:
     System();
 
@@ -225,6 +235,8 @@ private:
      */
     ResultStatus Init(EmuWindow& emu_window);
 
+    /// RealVfsFilesystem instance
+    FileSys::VirtualFilesystem virtual_filesystem;
     /// AppLoader used to load the current executing application
     std::unique_ptr<Loader::AppLoader> app_loader;
     std::unique_ptr<VideoCore::RendererBase> renderer;
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 9b87e34845..5e416cde2d 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -281,15 +281,15 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
     return sdmc_factory->Open();
 }
 
-void RegisterFileSystems() {
+void RegisterFileSystems(const FileSys::VirtualFilesystem& vfs) {
     romfs_factory = nullptr;
     save_data_factory = nullptr;
     sdmc_factory = nullptr;
 
-    auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>(
-        FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), FileSys::Mode::ReadWrite);
-    auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>(
-        FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::Mode::ReadWrite);
+    auto nand_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir),
+                                             FileSys::Mode::ReadWrite);
+    auto sd_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir),
+                                           FileSys::Mode::ReadWrite);
 
     auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
     save_data_factory = std::move(savedata);
@@ -298,8 +298,8 @@ void RegisterFileSystems() {
     sdmc_factory = std::move(sdcard);
 }
 
-void InstallInterfaces(SM::ServiceManager& service_manager) {
-    RegisterFileSystems();
+void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs) {
+    RegisterFileSystems(vfs);
     std::make_shared<FSP_LDR>()->InstallAsService(service_manager);
     std::make_shared<FSP_PR>()->InstallAsService(service_manager);
     std::make_shared<FSP_SRV>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index d4483daa5f..462c13f20c 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -36,7 +36,7 @@ ResultVal<FileSys::VirtualDir> OpenSDMC();
 // ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenBIS();
 
 /// Registers all Filesystem services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& service_manager);
+void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs);
 
 // A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of
 // pointers and booleans. This makes using a VfsDirectory with switch services much easier and
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 6f286ea747..11951adaf4 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -198,7 +198,7 @@ void AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
 }
 
 /// Initialize ServiceManager
-void Init(std::shared_ptr<SM::ServiceManager>& sm) {
+void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesystem& rfs) {
     // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
     // here and pass it into the respective InstallInterfaces functions.
     auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>();
@@ -221,7 +221,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
     EUPLD::InstallInterfaces(*sm);
     Fatal::InstallInterfaces(*sm);
     FGM::InstallInterfaces(*sm);
-    FileSystem::InstallInterfaces(*sm);
+    FileSystem::InstallInterfaces(*sm, rfs);
     Friend::InstallInterfaces(*sm);
     GRC::InstallInterfaces(*sm);
     HID::InstallInterfaces(*sm);
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 046c5e18da..8a294c0f23 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -22,6 +22,10 @@ class ServerSession;
 class HLERequestContext;
 } // namespace Kernel
 
+namespace FileSys {
+struct VfsFilesystem;
+}
+
 namespace Service {
 
 namespace SM {
@@ -177,7 +181,8 @@ private:
 };
 
 /// Initialize ServiceManager
-void Init(std::shared_ptr<SM::ServiceManager>& sm);
+void Init(std::shared_ptr<SM::ServiceManager>& sm,
+          const std::shared_ptr<FileSys::VfsFilesystem>& vfs);
 
 /// Shutdown ServiceManager
 void Shutdown();
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 5f47f5a2ba..e150a0684c 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -197,7 +197,8 @@ void GameList::onFilterCloseClicked() {
     main_window->filterBarSetChecked(false);
 }
 
-GameList::GameList(GMainWindow* parent) : QWidget{parent} {
+GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent)
+    : QWidget{parent}, vfs(std::move(vfs)) {
     watcher = new QFileSystemWatcher(this);
     connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory);
 
@@ -341,7 +342,7 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
 
     emit ShouldCancelWorker();
 
-    GameListWorker* worker = new GameListWorker(dir_path, deep_scan);
+    GameListWorker* worker = new GameListWorker(vfs, dir_path, deep_scan);
 
     connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection);
     connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating,
@@ -436,7 +437,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
         if (!is_dir &&
             (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
             std::unique_ptr<Loader::AppLoader> loader =
-                Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(physical_name));
+                Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read));
             if (!loader || ((loader->GetFileType() == Loader::FileType::Unknown ||
                              loader->GetFileType() == Loader::FileType::Error) &&
                             !UISettings::values.show_unknown))
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 3bc14f07fe..afe624b328 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -59,7 +59,7 @@ public:
         QToolButton* button_filter_close = nullptr;
     };
 
-    explicit GameList(GMainWindow* parent = nullptr);
+    explicit GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent = nullptr);
     ~GameList() override;
 
     void clearFilter();
@@ -90,6 +90,7 @@ private:
     void PopupContextMenu(const QPoint& menu_location);
     void RefreshGameDirectory();
 
+    FileSys::VirtualFilesystem vfs;
     SearchField* search_field;
     GMainWindow* main_window = nullptr;
     QVBoxLayout* layout = nullptr;
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index a22025e676..49a3f61814 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -139,7 +139,7 @@ class GameListWorker : public QObject, public QRunnable {
     Q_OBJECT
 
 public:
-    GameListWorker(QString dir_path, bool deep_scan)
+    GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan)
         : dir_path(std::move(dir_path)), deep_scan(deep_scan) {}
 
 public slots:
@@ -163,6 +163,7 @@ signals:
     void Finished(QStringList watch_list);
 
 private:
+    FileSys::VirtualFilesystem vfs;
     QStringList watch_list;
     QString dir_path;
     bool deep_scan;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index a6241e63e4..f7812a3928 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -24,6 +24,7 @@
 #include "common/string_util.h"
 #include "core/core.h"
 #include "core/crypto/key_manager.h"
+#include "core/file_sys/vfs_real.h"
 #include "core/gdbstub/gdbstub.h"
 #include "core/loader/loader.h"
 #include "core/settings.h"
@@ -81,9 +82,9 @@ static void ShowCalloutMessage(const QString& message, CalloutFlag flag) {
 
 void GMainWindow::ShowCallouts() {}
 
-const int GMainWindow::max_recent_files_item;
-
-GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
+GMainWindow::GMainWindow()
+    : config(new Config()), emu_thread(nullptr),
+      vfs(std::make_shared<FileSys::RealVfsFilesystem>()) {
 
     debug_context = Tegra::DebugContext::Construct();
 
@@ -132,7 +133,7 @@ void GMainWindow::InitializeWidgets() {
     render_window = new GRenderWindow(this, emu_thread.get());
     render_window->hide();
 
-    game_list = new GameList(this);
+    game_list = new GameList(vfs, this);
     ui.horizontalLayout->addWidget(game_list);
 
     // Create status bar
@@ -406,6 +407,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
     }
 
     Core::System& system{Core::System::GetInstance()};
+    system.SetFilesystem(vfs);
 
     system.SetGPUDebugContext(debug_context);
 
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 6e335b8f80..74487c58ca 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -161,6 +161,9 @@ private:
     bool emulation_running = false;
     std::unique_ptr<EmuThread> emu_thread;
 
+    // FS
+    FileSys::VirtualFilesystem vfs;
+
     // Debugger panes
     ProfilerWidget* profilerWidget;
     MicroProfileDialog* microProfileDialog;
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index d637dbd0c5..0605c92e37 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -161,6 +161,7 @@ int main(int argc, char** argv) {
     }
 
     Core::System& system{Core::System::GetInstance()};
+    system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
 
     SCOPE_EXIT({ system.Shutdown(); });
 

From 52a2e42cb93d7ebfd329aafb04b8bd0aaa67e19e Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Fri, 3 Aug 2018 11:52:12 -0400
Subject: [PATCH 07/12] file_sys: Add missing include in savedata_factory

---
 src/core/file_sys/savedata_factory.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index e3a578c0f4..f3cf50d5ac 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -7,6 +7,7 @@
 #include <memory>
 #include <string>
 #include "common/common_types.h"
+#include "common/swap.h"
 #include "core/hle/result.h"
 
 namespace FileSys {

From 656e97df16944dd997e18a58829001008760bf01 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Fri, 3 Aug 2018 11:52:42 -0400
Subject: [PATCH 08/12] vfs: Use RealVfsFilesystem for fs-operations in
 RealVfsDirectory

---
 src/core/file_sys/vfs_real.h | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index dbb381a2a1..8a1e79ef60 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -74,6 +74,13 @@ class RealVfsDirectory : public VfsDirectory {
     RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
 
 public:
+    std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override;
+    std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override;
+    std::shared_ptr<VfsFile> GetFile(std::string_view name) const override;
+    std::shared_ptr<VfsDirectory> GetSubdirectory(std::string_view name) const override;
+    std::shared_ptr<VfsFile> CreateFileRelative(std::string_view path) override;
+    std::shared_ptr<VfsDirectory> CreateDirectoryRelative(std::string_view path) override;
+    bool DeleteSubdirectoryRecursive(std::string_view name) override;
     std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
     std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
     bool IsWritable() const override;
@@ -91,14 +98,15 @@ protected:
     bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
 
 private:
+    template <typename T, typename R>
+    std::vector<std::shared_ptr<R>> IterateEntries() const;
+
     RealVfsFilesystem& base;
     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

From dad2ae1ee01b42bb6bb8a903c856712fc2bffa37 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Fri, 3 Aug 2018 11:56:44 -0400
Subject: [PATCH 09/12] loader: Remove unused IdentifyFile overload

---
 src/core/loader/loader.cpp | 4 ----
 src/core/loader/loader.h   | 8 --------
 2 files changed, 12 deletions(-)

diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 0781fb8c1e..a288654dfc 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -43,10 +43,6 @@ FileType IdentifyFile(FileSys::VirtualFile file) {
     return FileType::Unknown;
 }
 
-FileType IdentifyFile(const std::string& file_name) {
-    return IdentifyFile(std::make_shared<FileSys::RealVfsFile>(file_name));
-}
-
 FileType GuessFromFilename(const std::string& name) {
     if (name == "main")
         return FileType::DeconstructedRomDirectory;
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 7bd0adedb5..6a9e5a68be 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -42,14 +42,6 @@ enum class FileType {
  */
 FileType IdentifyFile(FileSys::VirtualFile file);
 
-/**
- * Identifies the type of a bootable file based on the magic value in its header.
- * @param file_name path to file
- * @return FileType of file. Note: this will return FileType::Unknown if it is unable to determine
- * a filetype, and will never return FileType::Error.
- */
-FileType IdentifyFile(const std::string& file_name);
-
 /**
  * Guess the type of a bootable file from its name
  * @param name String name of bootable file

From 2b6128fe0b8788318a4bbe1fc55ea14aed2981e4 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Mon, 6 Aug 2018 23:21:37 -0400
Subject: [PATCH 10/12] file_util: Use enum instead of bool for specifing path
 behavior

---
 src/common/file_util.cpp       |  8 +++----
 src/common/file_util.h         |  7 ++++--
 src/core/file_sys/vfs_real.cpp | 44 +++++++++++++++++++++-------------
 src/yuzu/game_list_p.h         |  2 +-
 4 files changed, 37 insertions(+), 24 deletions(-)

diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 190cac6d9d..3ce590062a 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -884,12 +884,12 @@ std::string_view RemoveTrailingSlash(std::string_view path) {
     return path;
 }
 
-std::string SanitizePath(std::string_view path_, bool with_platform_slashes) {
+std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {
     std::string path(path_);
-    char type1 = '\\';
-    char type2 = '/';
+    char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\';
+    char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/';
 
-    if (with_platform_slashes) {
+    if (directory_separator == DirectorySeparator::PlatformDefault) {
 #ifdef _WIN32
         type1 = '/';
         type2 = '\\';
diff --git a/src/common/file_util.h b/src/common/file_util.h
index ca63d74661..2711872ae4 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -182,9 +182,12 @@ std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t la
     return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
 }
 
+enum class DirectorySeparator { ForwardSlash, BackwardSlash, PlatformDefault };
+
 // Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
-// if windows and with_platform_slashes is true.
-std::string SanitizePath(std::string_view path, bool with_platform_slashes = false);
+// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
+std::string SanitizePath(std::string_view path,
+                         DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
 
 // simple wrapper for cstdlib file functions to
 // hopefully will make error checking easier
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 21ea35aaf5..1b59197374 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -53,7 +53,7 @@ bool RealVfsFilesystem::IsWritable() const {
 }
 
 VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
-    const auto path = FileUtil::SanitizePath(path_, true);
+    const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
     if (!FileUtil::Exists(path))
         return VfsEntryType::None;
     if (FileUtil::IsDirectory(path))
@@ -63,7 +63,7 @@ VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
 }
 
 VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
-    const auto path = FileUtil::SanitizePath(path_, true);
+    const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
     if (cache.find(path) != cache.end()) {
         auto weak = cache[path];
         if (!weak.expired()) {
@@ -82,15 +82,17 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
 }
 
 VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
-    const auto path = FileUtil::SanitizePath(path_, true);
+    const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
     if (!FileUtil::Exists(path) && !FileUtil::CreateEmptyFile(path))
         return nullptr;
     return OpenFile(path, perms);
 }
 
 VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
-    const auto old_path = FileUtil::SanitizePath(old_path_, true);
-    const auto new_path = FileUtil::SanitizePath(new_path_, true);
+    const auto old_path =
+        FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
+    const auto new_path =
+        FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
 
     if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
         FileUtil::IsDirectory(old_path) || !FileUtil::Copy(old_path, new_path))
@@ -99,8 +101,10 @@ VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_
 }
 
 VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
-    const auto old_path = FileUtil::SanitizePath(old_path_, true);
-    const auto new_path = FileUtil::SanitizePath(new_path_, true);
+    const auto old_path =
+        FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
+    const auto new_path =
+        FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
 
     if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
         FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
@@ -119,7 +123,7 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
 }
 
 bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
-    const auto path = FileUtil::SanitizePath(path_, true);
+    const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
     if (cache.find(path) != cache.end()) {
         if (!cache[path].expired())
             cache[path].lock()->Close();
@@ -129,13 +133,13 @@ bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
 }
 
 VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
-    const auto path = FileUtil::SanitizePath(path_, true);
+    const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
     // Cannot use make_shared as RealVfsDirectory constructor is private
     return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
 }
 
 VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
-    const auto path = FileUtil::SanitizePath(path_, true);
+    const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
     if (!FileUtil::Exists(path) && !FileUtil::CreateDir(path))
         return nullptr;
     // Cannot use make_shared as RealVfsDirectory constructor is private
@@ -144,8 +148,10 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms
 
 VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
                                             std::string_view new_path_) {
-    const auto old_path = FileUtil::SanitizePath(old_path_, true);
-    const auto new_path = FileUtil::SanitizePath(new_path_, true);
+    const auto old_path =
+        FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
+    const auto new_path =
+        FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
     if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
         !FileUtil::IsDirectory(old_path))
         return nullptr;
@@ -155,8 +161,10 @@ VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
 
 VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
                                             std::string_view new_path_) {
-    const auto old_path = FileUtil::SanitizePath(old_path_, true);
-    const auto new_path = FileUtil::SanitizePath(new_path_, true);
+    const auto old_path =
+        FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
+    const auto new_path =
+        FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
     if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
         FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
         return nullptr;
@@ -164,9 +172,11 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
     for (auto& kv : cache) {
         // Path in cache starts with old_path
         if (kv.first.rfind(old_path, 0) == 0) {
-            const auto file_old_path = FileUtil::SanitizePath(kv.first, true);
+            const auto file_old_path =
+                FileUtil::SanitizePath(kv.first, FileUtil::DirectorySeparator::PlatformDefault);
             const auto file_new_path =
-                FileUtil::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()), true);
+                FileUtil::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()),
+                                       FileUtil::DirectorySeparator::PlatformDefault);
             auto cached = cache[file_old_path];
             if (!cached.expired()) {
                 auto file = cached.lock();
@@ -181,7 +191,7 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
 }
 
 bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
-    const auto path = FileUtil::SanitizePath(path_, true);
+    const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
     for (auto& kv : cache) {
         // Path in cache starts with old_path
         if (kv.first.rfind(path, 0) == 0) {
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index 49a3f61814..114a0fc7f0 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -140,7 +140,7 @@ class GameListWorker : public QObject, public QRunnable {
 
 public:
     GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan)
-        : dir_path(std::move(dir_path)), deep_scan(deep_scan) {}
+        : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan) {}
 
 public slots:
     /// Starts the processing of directory tree information.

From 94cf327e776eff934052847346d8415d584b369b Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 8 Aug 2018 14:34:06 -0400
Subject: [PATCH 11/12] vfs: Fix typo in VfsFilesystem docs

---
 src/core/file_sys/vfs.h | 2 +-
 src/yuzu/game_list.cpp  | 7 ++++---
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index 9c7ef93b81..bf16e72861 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -31,7 +31,7 @@ enum class VfsEntryType {
     Directory,
 };
 
-// A class represnting an abstract filesystem. A default implementation given the root VirtualDir is
+// A class representing an abstract filesystem. A default implementation given the root VirtualDir is
 // provided for convenience, but if the Vfs implementation has any additional state or
 // functionality, they will need to override.
 struct VfsFilesystem : NonCopyable {
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index e150a0684c..1c738d2a4f 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -15,6 +15,7 @@
 #include "common/string_util.h"
 #include "core/file_sys/content_archive.h"
 #include "core/file_sys/control_metadata.h"
+#include "core/file_sys/romfs.h"
 #include "core/file_sys/vfs_real.h"
 #include "core/loader/loader.h"
 #include "game_list.h"
@@ -415,8 +416,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
         bool is_dir = FileUtil::IsDirectory(physical_name);
         QFileInfo file_info(physical_name.c_str());
         if (!is_dir && file_info.suffix().toStdString() == "nca") {
-            auto nca = std::make_shared<FileSys::NCA>(
-                std::make_shared<FileSys::RealVfsFile>(physical_name));
+            auto nca =
+                std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));
             if (nca->GetType() == FileSys::NCAContentType::Control)
                 nca_control_map.insert_or_assign(nca->GetTitleId(), nca);
         }
@@ -460,7 +461,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
                 // Use from metadata pool.
                 if (nca_control_map.find(program_id) != nca_control_map.end()) {
                     const auto nca = nca_control_map[program_id];
-                    const auto control_dir = nca->GetSubdirectories()[0];
+                    const auto control_dir = FileSys::ExtractRomFS(nca->GetRomFS());
 
                     const auto nacp_file = control_dir->GetFile("control.nacp");
                     FileSys::NACP nacp(nacp_file);

From 668458525ede125509ee27388221247b639f4676 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 8 Aug 2018 21:44:59 -0400
Subject: [PATCH 12/12] vfs: Fix documentation

---
 src/core/file_sys/vfs.h | 4 ++--
 src/yuzu/main.cpp       | 2 ++
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index bf16e72861..141a053ce0 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -31,8 +31,8 @@ enum class VfsEntryType {
     Directory,
 };
 
-// A class representing an abstract filesystem. A default implementation given the root VirtualDir is
-// provided for convenience, but if the Vfs implementation has any additional state or
+// A class representing an abstract filesystem. A default implementation given the root VirtualDir
+// is provided for convenience, but if the Vfs implementation has any additional state or
 // functionality, they will need to override.
 struct VfsFilesystem : NonCopyable {
     VfsFilesystem(VirtualDir root);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index f7812a3928..67e3c6549c 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -82,6 +82,8 @@ static void ShowCalloutMessage(const QString& message, CalloutFlag flag) {
 
 void GMainWindow::ShowCallouts() {}
 
+const int GMainWindow::max_recent_files_item;
+
 GMainWindow::GMainWindow()
     : config(new Config()), emu_thread(nullptr),
       vfs(std::make_shared<FileSys::RealVfsFilesystem>()) {