summaryrefslogtreecommitdiffstats
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/card_image.cpp1
-rw-r--r--src/core/file_sys/content_archive.cpp4
-rw-r--r--src/core/file_sys/content_archive.h1
-rw-r--r--src/core/file_sys/control_metadata.h7
-rw-r--r--src/core/file_sys/directory.h12
-rw-r--r--src/core/file_sys/savedata_factory.h1
-rw-r--r--src/core/file_sys/vfs.cpp148
-rw-r--r--src/core/file_sys/vfs.h66
-rw-r--r--src/core/file_sys/vfs_real.cpp341
-rw-r--r--src/core/file_sys/vfs_real.h56
10 files changed, 547 insertions, 90 deletions
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 395eea8ae..e897d9913 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -5,6 +5,7 @@
#include <array>
#include <string>
#include <core/loader/loader.h>
+#include "common/logging/log.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/vfs_offset.h"
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 3529166ac..d3007d981 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -170,6 +170,10 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
}
NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
+ if (file == nullptr) {
+ status = Loader::ResultStatus::ErrorInvalidFormat;
+ return;
+ }
if (sizeof(NCAHeader) != file->ReadObject(&header))
LOG_ERROR(Loader, "File reader errored out during header read.");
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index a8879d9a8..5cfd5031a 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -12,6 +12,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
+#include "control_metadata.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/loader/loader.h"
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index cc3b745f7..6582cc240 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -62,6 +62,13 @@ enum class Language : u8 {
Chinese = 14,
};
+static constexpr std::array<const char*, 15> LANGUAGE_NAMES = {
+ "AmericanEnglish", "BritishEnglish", "Japanese",
+ "French", "German", "LatinAmericanSpanish",
+ "Spanish", "Italian", "Dutch",
+ "CanadianFrench", "Portugese", "Russian",
+ "Korean", "Taiwanese", "Chinese"};
+
// A class representing the format used by NX metadata files, typically named Control.nacp.
// These store application name, dev name, title id, and other miscellaneous data.
class NACP {
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h
index 213ce1826..3759e743a 100644
--- a/src/core/file_sys/directory.h
+++ b/src/core/file_sys/directory.h
@@ -4,8 +4,9 @@
#pragma once
-#include <array>
#include <cstddef>
+#include <iterator>
+#include <string_view>
#include "common/common_funcs.h"
#include "common/common_types.h"
@@ -21,9 +22,14 @@ enum EntryType : u8 {
// Structure of a directory entry, from
// http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry
-const size_t FILENAME_LENGTH = 0x300;
struct Entry {
- char filename[FILENAME_LENGTH];
+ Entry(std::string_view view, EntryType entry_type, u64 entry_size)
+ : type{entry_type}, file_size{entry_size} {
+ const size_t copy_size = view.copy(filename, std::size(filename) - 1);
+ filename[copy_size] = '\0';
+ }
+
+ char filename[0x300];
INSERT_PADDING_BYTES(4);
EntryType type;
INSERT_PADDING_BYTES(3);
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index e3a578c0f..f3cf50d5a 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 {
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index dae1c16ef..24e158962 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 fab9e2b45..141a053ce 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 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);
+ 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 {
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 82d54da4a..1b5919737 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";
@@ -36,8 +38,174 @@ 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_, FileUtil::DirectorySeparator::PlatformDefault);
+ 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_, FileUtil::DirectorySeparator::PlatformDefault);
+ 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_, 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_, 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))
+ 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_, 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;
+
+ 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_, FileUtil::DirectorySeparator::PlatformDefault);
+ 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_, 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_, FileUtil::DirectorySeparator::PlatformDefault);
+ 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_, 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;
+ 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_, 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;
+
+ 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, FileUtil::DirectorySeparator::PlatformDefault);
+ const auto file_new_path =
+ 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();
+ 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_, FileUtil::DirectorySeparator::PlatformDefault);
+ 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 +216,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 +236,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;
+}
+
+bool RealVfsFile::Close() {
+ return backing->Close();
+}
- 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());
+// 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 +366,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 +401,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 243d58576..8a1e79ef6 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,19 @@ 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::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;
@@ -60,13 +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