Remove std::filesystem and document/test

This commit is contained in:
Zach Hilman
2018-06-30 18:16:14 -04:00
parent e1e8b65b6e
commit 2251e0d406
14 changed files with 301 additions and 148 deletions

View File

@@ -60,7 +60,6 @@ add_library(common STATIC
scm_rev.cpp
scm_rev.h
scope_exit.h
std_filesystem.h
string_util.cpp
string_util.h
swap.h

View File

@@ -799,6 +799,48 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
}
}
std::vector<std::string> SplitPathComponents(const std::string& filename) {
auto copy(filename);
std::replace(copy.begin(), copy.end(), '\\', '/');
std::vector<std::string> out{};
std::stringstream stream(filename);
std::string item;
while (std::getline(stream, item, '/'))
out.push_back(item);
return out;
}
std::string GetParentPath(const std::string& path) {
auto out = path;
const auto name_bck_index = out.find_last_of('\\');
const auto name_fwd_index = out.find_last_of('/');
size_t name_index;
if (name_bck_index == std::string::npos || name_fwd_index == std::string::npos)
name_index = std::min<size_t>(name_bck_index, name_fwd_index);
else
name_index = std::max<size_t>(name_bck_index, name_fwd_index);
return out.erase(name_index);
}
std::string GetFilename(const std::string& path) {
auto out = path;
std::replace(out.begin(), out.end(), '\\', '/');
auto name_index = out.find_last_of('/');
if (name_index == std::string::npos)
return "";
return out.substr(name_index + 1);
}
std::string RemoveTrailingSlash(const std::string& path) {
if (path.back() == '\\' || path.back() == '/')
return path.substr(0, path.size() - 1);
return path;
}
IOFile::IOFile() {}
IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {

View File

@@ -150,6 +150,28 @@ size_t ReadFileToString(bool text_file, const char* filename, std::string& str);
void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
std::array<char, 4>& extension);
// Splits the path on '/' or '\' and put the components into a vector
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
std::vector<std::string> SplitPathComponents(const std::string& filename);
// Gets all of the text prior to the last '/' or '\' in the path.
std::string GetParentPath(const std::string& path);
// Gets the filename of the path
std::string GetFilename(const std::string& path);
// Removes the final '/' or '\' if one exists
std::string RemoveTrailingSlash(const std::string& path);
// Creates a new vector containing indices [first, last) from the original.
template <typename T>
std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t last) {
if (first >= last)
return {};
last = std::min<size_t>(last, vector.size());
return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
}
// simple wrapper for cstdlib file functions to
// hopefully will make error checking easier
// and make forgetting an fclose() harder

View File

@@ -1,13 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#if _MSC_VER || (__GNUC__ > 8)
#include <filesystem>
namespace filesystem = std::filesystem;
#else
#include <experimental/filesystem>
namespace filesystem = std::experimental::filesystem;
#endif

View File

@@ -52,6 +52,8 @@ static bool IsValidNCA(const NCAHeader& header) {
header.magic == Common::MakeMagic('N', 'C', 'A', '3');
}
// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner.
// After construction, use GetStatus to determine if the file is valid and ready to be used.
class NCA : public ReadOnlyVfsDirectory {
std::vector<v_dir> dirs{};
std::vector<v_file> files{};

View File

@@ -48,44 +48,43 @@ size_t VfsFile::WriteBytes(std::vector<u8> data, size_t offset) {
return Write(data.data(), data.size(), offset);
}
std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(const filesystem::path& path) const {
if (path.parent_path() == path.root_path())
return GetFile(path.filename().string());
auto parent = path.parent_path().string();
parent.replace(path.root_path().string().begin(), path.root_path().string().end(), "");
const auto index = parent.find_first_of('/');
const auto first_dir = parent.substr(0, index), rest = parent.substr(index + 1);
const auto sub = GetSubdirectory(first_dir);
if (sub == nullptr)
std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(const std::string& path) const {
auto vec = FileUtil::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty())
return nullptr;
return sub->GetFileRelative(path.root_path().string() + rest);
if (vec.size() == 1)
return GetFile(vec[0]);
auto dir = GetSubdirectory(vec[0]);
for (auto i = 1u; i < vec.size() - 1; ++i) {
dir = dir->GetSubdirectory(vec[i]);
}
return dir->GetFile(vec.back());
}
std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(const filesystem::path& path) const {
std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(const std::string& path) const {
if (IsRoot())
return GetFileRelative(path);
return GetParentDirectory()->GetFileAbsolute(path);
}
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(
const filesystem::path& path) const {
if (path.parent_path() == path.root_path())
return GetSubdirectory(path.filename().string());
auto parent = path.parent_path().string();
parent.replace(path.root_path().string().begin(), path.root_path().string().end(), "");
const auto index = parent.find_first_of('/');
const auto first_dir = parent.substr(0, index), rest = parent.substr(index + 1);
const auto sub = GetSubdirectory(first_dir);
if (sub == nullptr)
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(const std::string& path) const {
auto vec = FileUtil::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty())
// return std::shared_ptr<VfsDirectory>(this);
return nullptr;
return sub->GetDirectoryRelative(path.root_path().string() + rest);
auto dir = GetSubdirectory(vec[0]);
for (auto i = 1u; i < vec.size(); ++i) {
dir = dir->GetSubdirectory(vec[i]);
}
return dir;
}
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryAbsolute(
const filesystem::path& path) const {
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryAbsolute(const std::string& path) const {
if (IsRoot())
return GetDirectoryRelative(path);
@@ -101,6 +100,8 @@ std::shared_ptr<VfsFile> VfsDirectory::GetFile(const std::string& name) const {
}
std::shared_ptr<VfsDirectory> VfsDirectory::GetSubdirectory(const std::string& name) const {
// if (name == "" || name == "." || name == "/" || name == "\\")
// return std::shared_ptr<VfsDirectory>(const_cast<VfsDirectory*>(this));
const auto& subs = GetSubdirectories();
const auto& iter = std::find_if(
subs.begin(), subs.end(), [&name](const auto& file1) { return name == file1->GetName(); });
@@ -143,7 +144,7 @@ bool VfsDirectory::DeleteSubdirectoryRecursive(const std::string& name) {
}
bool VfsDirectory::Copy(const std::string& src, const std::string& dest) {
const auto f1 = CreateFile(src);
const auto f1 = GetFile(src);
auto f2 = CreateFile(dest);
if (f1 == nullptr || f2 == nullptr)
return false;

View File

@@ -10,37 +10,55 @@
#include "boost/optional.hpp"
#include "common/common_types.h"
#include "common/file_util.h"
#include "common/std_filesystem.h"
namespace FileSys {
struct VfsFile;
struct VfsDirectory;
} // namespace FileSys
// Convenience typedefs to use VfsDirectory and VfsFile
using v_dir = std::shared_ptr<FileSys::VfsDirectory>;
using v_file = std::shared_ptr<FileSys::VfsFile>;
namespace FileSys {
// A class representing a file in an abstract filesystem.
struct VfsFile : NonCopyable {
virtual ~VfsFile();
// Retrieves the file name.
virtual std::string GetName() const = 0;
// Retrieves the extension of the file name.
virtual std::string GetExtension() const;
// Retrieves the size of the file.
virtual size_t GetSize() const = 0;
// Resizes the file to new_size. Returns whether or not the operation was successful.
virtual bool Resize(size_t new_size) = 0;
// Gets a pointer to the directory containing this file, returning nullptr if there is none.
virtual std::shared_ptr<VfsDirectory> GetContainingDirectory() const = 0;
// Returns whether or not the file can be written to.
virtual bool IsWritable() const = 0;
// Returns whether or not the file can be read from.
virtual bool IsReadable() const = 0;
// The primary method of reading from the file. Reads length bytes into data starting at offset
// into file. Returns number of bytes successfully read.
virtual size_t Read(u8* data, size_t length, size_t offset = 0) const = 0;
// The primary method of writing to the file. Writes length bytes from data starting at offset
// into file. Returns number of bytes successfully written.
virtual size_t Write(const u8* data, size_t length, size_t offset = 0) = 0;
// Reads exactly one byte at the offset provided, returning boost::none on error.
virtual boost::optional<u8> ReadByte(size_t offset = 0) const;
// Reads size bytes starting at offset in file into a vector.
virtual std::vector<u8> ReadBytes(size_t size, size_t offset = 0) const;
// Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(),
// 0)'
virtual std::vector<u8> ReadAllBytes() const;
// Reads an array of type T, size number_elements starting at offset.
// Returns the number of bytes (sizeof(T)*number_elements) read successfully.
template <typename T>
size_t ReadArray(T* data, size_t number_elements, size_t offset = 0) const {
static_assert(std::is_trivially_copyable<T>::value,
@@ -49,6 +67,8 @@ struct VfsFile : NonCopyable {
return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset);
}
// Reads size bytes into the memory starting at data starting at offset into the file.
// Returns the number of bytes read successfully.
template <typename T>
size_t ReadBytes(T* data, size_t size, size_t offset = 0) const {
static_assert(std::is_trivially_copyable<T>::value,
@@ -56,6 +76,8 @@ struct VfsFile : NonCopyable {
return Read(reinterpret_cast<u8*>(data), size, offset);
}
// Reads one object of type T starting at offset in file.
// Returns the number of bytes read successfully (sizeof(T)).
template <typename T>
size_t ReadObject(T* data, size_t offset = 0) const {
static_assert(std::is_trivially_copyable<T>::value,
@@ -63,9 +85,15 @@ struct VfsFile : NonCopyable {
return Read(reinterpret_cast<u8*>(data), sizeof(T), offset);
}
// Writes exactly one byte to offset in file and retuns whether or not the byte was written
// successfully.
virtual bool WriteByte(u8 data, size_t offset = 0);
// Writes a vector of bytes to offset in file and returns the number of bytes successfully
// written.
virtual size_t WriteBytes(std::vector<u8> data, size_t offset = 0);
// Writes an array of type T, size number_elements to offset in file.
// Returns the number of bytes (sizeof(T)*number_elements) written successfully.
template <typename T>
size_t WriteArray(T* data, size_t number_elements, size_t offset = 0) {
static_assert(std::is_trivially_copyable<T>::value,
@@ -74,6 +102,8 @@ struct VfsFile : NonCopyable {
return Write(data, number_elements * sizeof(T), offset);
}
// Writes size bytes starting at memory location data to offset in file.
// Returns the number of bytes written successfully.
template <typename T>
size_t WriteBytes(T* data, size_t size, size_t offset = 0) {
static_assert(std::is_trivially_copyable<T>::value,
@@ -81,60 +111,105 @@ struct VfsFile : NonCopyable {
return Write(reinterpret_cast<u8*>(data), size, offset);
}
// Writes one object of type T to offset in file.
// Returns the number of bytes written successfully (sizeof(T)).
template <typename T>
size_t WriteObject(T& data, size_t offset = 0) {
size_t WriteObject(const T& data, size_t offset = 0) {
static_assert(std::is_trivially_copyable<T>::value,
"Data type must be trivially copyable.");
return Write(&data, sizeof(T), offset);
}
// Renames the file to name. Returns whether or not the operation was successsful.
virtual bool Rename(const std::string& name) = 0;
};
// A class representing a directory in an abstract filesystem.
struct VfsDirectory : NonCopyable {
virtual ~VfsDirectory();
virtual std::shared_ptr<VfsFile> GetFileRelative(const filesystem::path& path) const;
virtual std::shared_ptr<VfsFile> GetFileAbsolute(const filesystem::path& path) const;
// Retrives the file located at path as if the current directory was root. Returns nullptr if
// not found.
virtual std::shared_ptr<VfsFile> GetFileRelative(const std::string& path) const;
// Calls GetFileRelative(path) on the root of the current directory.
virtual std::shared_ptr<VfsFile> GetFileAbsolute(const std::string& path) const;
virtual std::shared_ptr<VfsDirectory> GetDirectoryRelative(const filesystem::path& path) const;
virtual std::shared_ptr<VfsDirectory> GetDirectoryAbsolute(const filesystem::path& path) const;
// Retrives the directory located at path as if the current directory was root. Returns nullptr
// if not found.
virtual std::shared_ptr<VfsDirectory> GetDirectoryRelative(const std::string& path) const;
// Calls GetDirectoryRelative(path) on the root of the current directory.
virtual std::shared_ptr<VfsDirectory> GetDirectoryAbsolute(const std::string& path) const;
// Returns a vector containing all of the files in this directory.
virtual std::vector<std::shared_ptr<VfsFile>> GetFiles() const = 0;
// Returns the file with filename matching name. Returns nullptr if directory dosen't have a
// file with name.
virtual std::shared_ptr<VfsFile> GetFile(const std::string& name) const;
// Returns a vector containing all of the subdirectories in this directory.
virtual std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const = 0;
// Returns the directory with name matching name. Returns nullptr if directory dosen't have a
// directory with name.
virtual std::shared_ptr<VfsDirectory> GetSubdirectory(const std::string& name) const;
// Returns whether or not the directory can be written to.
virtual bool IsWritable() const = 0;
// Returns whether of not the directory can be read from.
virtual bool IsReadable() const = 0;
// Returns whether or not the directory is the root of the current file tree.
virtual bool IsRoot() const;
// Returns the name of the directory.
virtual std::string GetName() const = 0;
// Returns the total size of all files and subdirectories in this directory.
virtual size_t GetSize() const;
// Returns the parent directory of this directory. Returns nullptr if this directory is root or
// has no parent.
virtual std::shared_ptr<VfsDirectory> GetParentDirectory() const = 0;
// Creates a new subdirectory with name name. Returns a pointer to the new directory or nullptr
// if the operation failed.
virtual std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) = 0;
// Creates a new file with name name. Returns a pointer to the new file or nullptr if the
// operation failed.
virtual std::shared_ptr<VfsFile> CreateFile(const std::string& name) = 0;
// Deletes the subdirectory with name and returns true on success.
virtual bool DeleteSubdirectory(const std::string& name) = 0;
// Deletes all subdirectories and files of subdirectory with name recirsively and then deletes
// the subdirectory. Returns true on success.
virtual bool DeleteSubdirectoryRecursive(const std::string& name);
// Returnes whether or not the file with name name was deleted successfully.
virtual bool DeleteFile(const std::string& name) = 0;
// Returns whether or not this directory was renamed to name.
virtual bool Rename(const std::string& name) = 0;
// Returns whether or not the file with name src was successfully copied to a new file with name
// dest.
virtual bool Copy(const std::string& src, const std::string& dest);
// Interprets the file with name file instead as a directory of type directory.
// The directory must have a constructor that takes a single argument of type
// std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a
// subdirectory in one call.
template <typename Directory>
bool InterpretAsDirectory(v_file file) {
return ReplaceFileWithSubdirectory(file, std::make_shared<Directory>(file));
bool InterpretAsDirectory(const std::string& file) {
auto file_p = GetFile(file);
if (file_p == nullptr)
return false;
return ReplaceFileWithSubdirectory(file, std::make_shared<Directory>(file_p));
}
protected:
// Backend for InterpretAsDirectory.
// Removes all references to file and adds a reference to dir in the directory's implementation.
virtual bool ReplaceFileWithSubdirectory(v_file file, v_dir dir) = 0;
};
// A convenience partial-implementation of VfsDirectory that stubs out methods that should only work
// if writable. This is to avoid redundant empty methods everywhere.
struct ReadOnlyVfsDirectory : public VfsDirectory {
bool IsWritable() const override;
bool IsReadable() const override;

View File

@@ -86,7 +86,7 @@ size_t OffsetVfsFile::GetOffset() const {
}
size_t OffsetVfsFile::TrimToFit(size_t r_size, size_t r_offset) const {
return std::max(std::min(size - r_offset, r_size), 0ull);
return std::max<size_t>(std::min<size_t>(size - r_offset, r_size), 0);
}
} // namespace FileSys

View File

@@ -8,6 +8,10 @@
namespace FileSys {
// An implementation of VfsFile that wraps around another VfsFile at a certain offset.
// Similar to seeking to an offset.
// If the file is writable, operations that would write past the end of the offset file will expand
// the size of this wrapper.
struct OffsetVfsFile : public VfsFile {
OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0,
const std::string& new_name = "");

View File

@@ -2,29 +2,37 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/common_paths.h"
#include "common/logging/log.h"
#include "core/file_sys/vfs_real.h"
namespace FileSys {
static std::string PermissionsToCharArray(filesystem::perms perms) {
static std::string PermissionsToCharArray(Mode perms) {
std::string out;
if ((perms & filesystem::perms::owner_read) != filesystem::perms::none)
switch (perms) {
case Mode::Read:
out += "r";
if ((perms & filesystem::perms::owner_write) != filesystem::perms::none)
out += "+";
if (out == "+")
out = "w";
break;
case Mode::Write:
out += "w+";
break;
case Mode::Append:
out += "a";
break;
}
return out + "b";
}
RealVfsFile::RealVfsFile(const filesystem::path& path_, filesystem::perms perms_)
: backing(path_.string(), PermissionsToCharArray(perms_).c_str()), path(path_), perms(perms_) {
path.make_preferred();
}
RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_)
: backing(path_, PermissionsToCharArray(perms_).c_str()), path(path_),
parent_path(FileUtil::GetParentPath(path_)),
path_components(FileUtil::SplitPathComponents(path_)),
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
perms(perms_) {}
std::string RealVfsFile::GetName() const {
return path.filename().string();
return path_components.back();
}
size_t RealVfsFile::GetSize() const {
@@ -36,15 +44,15 @@ bool RealVfsFile::Resize(size_t new_size) {
}
std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const {
return std::make_shared<RealVfsDirectory>(path.parent_path(), perms);
return std::make_shared<RealVfsDirectory>(parent_path, perms);
}
bool RealVfsFile::IsWritable() const {
return (perms & filesystem::perms::owner_write) != filesystem::perms::none;
return perms == Mode::Append || perms == Mode::Write;
}
bool RealVfsFile::IsReadable() const {
return (perms & filesystem::perms::owner_read) != filesystem::perms::none;
return perms == Mode::Read || perms == Mode::Write;
}
size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const {
@@ -61,23 +69,32 @@ size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) {
bool RealVfsFile::Rename(const std::string& name) {
const auto out = FileUtil::Rename(GetName(), name);
path = path.parent_path() / name;
backing = FileUtil::IOFile(path.string(), PermissionsToCharArray(perms).c_str());
path = parent_path + DIR_SEP + name;
path_components = parent_components;
path_components.push_back(name);
backing = FileUtil::IOFile(path, PermissionsToCharArray(perms).c_str());
return out;
}
RealVfsDirectory::RealVfsDirectory(const filesystem::path& path_, filesystem::perms perms_)
: path(path_), perms(perms_) {
path.make_preferred();
if (!filesystem::exists(path) &&
(perms_ & filesystem::perms::owner_write) != filesystem::perms::none)
filesystem::create_directory(path);
for (const auto& entry : filesystem::directory_iterator(path)) {
if (filesystem::is_directory(entry.path()))
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(entry.path(), perms));
else if (filesystem::is_regular_file(entry.path()))
files.emplace_back(std::make_shared<RealVfsFile>(entry.path(), perms));
}
RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_)
: path(FileUtil::RemoveTrailingSlash(path_)), parent_path(FileUtil::GetParentPath(path)),
path_components(FileUtil::SplitPathComponents(path)),
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
perms(perms_) {
if (!FileUtil::Exists(path) && (perms == Mode::Write || perms == Mode::Append))
FileUtil::CreateDir(path);
unsigned size;
if (perms != Mode::Append)
FileUtil::ForeachDirectoryEntry(
&size, path, [this](auto x1, auto directory, auto filename) {
std::string full_path = directory + DIR_SEP + filename;
if (FileUtil::IsDirectory(full_path))
subdirectories.emplace_back(
std::make_shared<RealVfsDirectory>(full_path, perms));
else
files.emplace_back(std::make_shared<RealVfsFile>(full_path, perms));
return true;
});
}
std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const {
@@ -89,48 +106,49 @@ std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories()
}
bool RealVfsDirectory::IsWritable() const {
return (perms & filesystem::perms::owner_write) != filesystem::perms::none;
return perms == Mode::Write || perms == Mode::Append;
}
bool RealVfsDirectory::IsReadable() const {
return (perms & filesystem::perms::owner_read) != filesystem::perms::none;
return perms == Mode::Read || perms == Mode::Write;
}
std::string RealVfsDirectory::GetName() const {
return path.filename().string();
return path_components.back();
}
std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const {
if (path.parent_path() == path.root_path())
if (path_components.size() <= 1)
return nullptr;
return std::make_shared<RealVfsDirectory>(path.parent_path(), perms);
return std::make_shared<RealVfsDirectory>(parent_path, perms);
}
std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(const std::string& name) {
if (!FileUtil::CreateDir((path / name).string()))
if (!FileUtil::CreateDir(path + DIR_SEP + name))
return nullptr;
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(path / name, perms));
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(path + DIR_SEP + name, perms));
return subdirectories.back();
}
std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(const std::string& name) {
if (!FileUtil::CreateEmptyFile((path / name).string()))
if (!FileUtil::CreateEmptyFile(path + DIR_SEP + name))
return nullptr;
files.emplace_back(std::make_shared<RealVfsFile>(path / name, perms));
files.emplace_back(std::make_shared<RealVfsFile>(path + DIR_SEP + name, perms));
return files.back();
}
bool RealVfsDirectory::DeleteSubdirectory(const std::string& name) {
return FileUtil::DeleteDirRecursively((path / name).string());
return FileUtil::DeleteDirRecursively(path + DIR_SEP + name);
}
bool RealVfsDirectory::DeleteFile(const std::string& name) {
return FileUtil::Delete((path / name).string());
return FileUtil::Delete(path + DIR_SEP + name);
}
bool RealVfsDirectory::Rename(const std::string& name) {
return FileUtil::Rename(path.string(), (path / name).string());
throw std::logic_error("lol");
return FileUtil::Rename(path, parent_path + DIR_SEP + name);
}
bool RealVfsDirectory::ReplaceFileWithSubdirectory(v_file file, v_dir dir) {

View File

@@ -5,14 +5,14 @@
#pragma once
#include "common/file_util.h"
#include "common/std_filesystem.h"
#include "core/file_sys/filesystem.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
// An implmentation of VfsFile that represents a file on the user's computer.
struct RealVfsFile : public VfsFile {
RealVfsFile(const filesystem::path& name,
filesystem::perms perms = filesystem::perms::owner_read);
RealVfsFile(const std::string& name, Mode perms = Mode::Read);
std::string GetName() const override;
size_t GetSize() const override;
@@ -26,12 +26,16 @@ struct RealVfsFile : public VfsFile {
private:
FileUtil::IOFile backing;
filesystem::path path;
filesystem::perms perms;
std::string path;
std::string parent_path;
std::vector<std::string> path_components;
std::vector<std::string> parent_components;
Mode perms;
};
// An implementation of VfsDirectory that represents a directory on the user's computer.
struct RealVfsDirectory : public VfsDirectory {
RealVfsDirectory(const filesystem::path& path, filesystem::perms perms);
RealVfsDirectory(const std::string& path, Mode perms);
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
@@ -49,8 +53,11 @@ protected:
bool ReplaceFileWithSubdirectory(v_file file, v_dir dir) override;
private:
filesystem::path path;
filesystem::perms perms;
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;
};

View File

@@ -24,9 +24,8 @@ std::string VfsDirectoryServiceWrapper::GetName() const {
}
ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path, u64 size) const {
filesystem::path s_path(path);
auto dir = backing->GetDirectoryRelative(s_path.parent_path());
auto file = dir->CreateFile(s_path.filename().string());
auto dir = backing->GetDirectoryRelative(FileUtil::GetParentPath(path));
auto file = dir->CreateFile(FileUtil::GetFilename(path));
if (file == nullptr)
return ResultCode(-1);
if (!file->Resize(size))
@@ -35,71 +34,53 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path, u64 s
}
ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const {
filesystem::path s_path(path);
auto dir = backing->GetDirectoryRelative(s_path.parent_path());
if (!backing->DeleteFile(s_path.filename().string()))
auto dir = backing->GetDirectoryRelative(FileUtil::GetParentPath(path));
if (!backing->DeleteFile(FileUtil::GetFilename(path)))
return ResultCode(-1);
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path) const {
filesystem::path s_path(path);
auto dir = backing->GetDirectoryRelative(s_path.parent_path());
auto new_dir = dir->CreateSubdirectory(s_path.filename().string());
auto dir = backing->GetDirectoryRelative(FileUtil::GetParentPath(path));
if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty())
dir = backing;
auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path));
if (new_dir == nullptr)
return ResultCode(-1);
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path) const {
filesystem::path s_path(path);
auto dir = backing->GetDirectoryRelative(s_path.parent_path());
if (!dir->DeleteSubdirectory(s_path.filename().string()))
auto dir = backing->GetDirectoryRelative(FileUtil::GetParentPath(path));
if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path)))
return ResultCode(-1);
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path) const {
filesystem::path s_path(path);
auto dir = backing->GetDirectoryRelative(s_path.parent_path());
if (!dir->DeleteSubdirectoryRecursive(s_path.filename().string()))
auto dir = backing->GetDirectoryRelative(FileUtil::GetParentPath(path));
if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path)))
return ResultCode(-1);
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path,
const std::string& dest_path) const {
filesystem::path s_path(src_path);
auto file = backing->GetFileRelative(s_path);
file->GetContainingDirectory()->DeleteFile(file->GetName());
auto res_code = CreateFile(dest_path, file->GetSize());
if (res_code != RESULT_SUCCESS)
return res_code;
auto file2 = backing->GetFileRelative(filesystem::path(dest_path));
if (file2->WriteBytes(file->ReadAllBytes()) != file->GetSize())
return ResultCode(-1);
return RESULT_SUCCESS;
NGLOG_CRITICAL(Service_FS, "unimplemented");
return ResultCode(-1);
}
ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path,
const std::string& dest_path) const {
filesystem::path s_path(src_path);
auto file = backing->GetFileRelative(s_path);
file->GetContainingDirectory()->DeleteFile(file->GetName());
auto res_code = CreateFile(dest_path, file->GetSize());
if (res_code != RESULT_SUCCESS)
return res_code;
auto file2 = backing->GetFileRelative(filesystem::path(dest_path));
if (file2->WriteBytes(file->ReadAllBytes()) != file->GetSize())
return ResultCode(-1);
return RESULT_SUCCESS;
NGLOG_CRITICAL(Service_FS, "unimplemented");
return ResultCode(-1);
}
ResultVal<v_file> VfsDirectoryServiceWrapper::OpenFile(const std::string& path,
FileSys::Mode mode) const {
auto file = backing->GetFileRelative(filesystem::path(path));
auto file = backing->GetFileRelative(path);
if (file == nullptr)
return FileSys::ERROR_FILE_NOT_FOUND;
return FileSys::ERROR_PATH_NOT_FOUND;
if (mode == FileSys::Mode::Append)
return MakeResult<v_file>(
std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize()));
@@ -110,8 +91,10 @@ ResultVal<v_file> VfsDirectoryServiceWrapper::OpenFile(const std::string& path,
return ResultCode(-1);
}
ResultVal<v_dir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path) const {
auto dir = backing->GetDirectoryRelative(filesystem::path(path));
ResultVal<v_dir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path) {
auto dir = backing->GetDirectoryRelative(path);
if (path == "/" || path == "\\")
return MakeResult(backing);
if (dir == nullptr)
return ResultCode(-1);
return MakeResult(dir);
@@ -127,17 +110,20 @@ u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const {
ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
const std::string& path) const {
filesystem::path r_path(path);
auto dir = backing->GetDirectoryRelative(r_path.parent_path());
auto dir = backing->GetDirectoryRelative(FileUtil::GetParentPath(path));
if (dir == nullptr)
return ResultCode(-1);
if (dir->GetFile(r_path.filename().string()) != nullptr)
auto filename = FileUtil::GetFilename(path);
if (dir->GetFile(filename) != nullptr)
return MakeResult(FileSys::EntryType::File);
if (dir->GetSubdirectory(r_path.filename().string()) != nullptr)
if (dir->GetSubdirectory(filename) != nullptr)
return MakeResult(FileSys::EntryType::Directory);
return ResultCode(-1);
}
// A deferred filesystem for nand save data.
// This must be deferred because the directory is dependent on title id, which is not set at
// registration time.
struct SaveDataDeferredFilesystem : DeferredFilesystem {
protected:
v_dir CreateFilesystem() override {
@@ -148,7 +134,7 @@ protected:
"{}save/{:016X}/{:08X}/", FileUtil::GetUserPath(D_NAND_IDX), title_id, user_id);
auto savedata =
std::make_shared<FileSys::RealVfsDirectory>(nand_directory, filesystem::perms::all);
std::make_shared<FileSys::RealVfsDirectory>(nand_directory, FileSys::Mode::Write);
return savedata;
}
};
@@ -158,7 +144,7 @@ protected:
* is never removed until UnregisterFileSystems is called.
*/
static boost::container::flat_map<Type, std::unique_ptr<DeferredFilesystem>> filesystem_map;
static v_file filesystem_romfs;
static v_file filesystem_romfs = nullptr;
ResultCode RegisterFileSystem(std::unique_ptr<DeferredFilesystem>&& factory, Type type) {
auto result = filesystem_map.emplace(type, std::move(factory));
@@ -172,8 +158,8 @@ ResultCode RegisterFileSystem(std::unique_ptr<DeferredFilesystem>&& factory, Typ
}
ResultCode RegisterRomFS(v_file filesystem) {
bool inserted = filesystem_romfs == nullptr;
ASSERT_MSG(inserted, "Tried to register more than one system with same id code");
ASSERT_MSG(filesystem_romfs == nullptr,
"Tried to register more than one system with same id code");
filesystem_romfs = filesystem;
NGLOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}",
@@ -216,9 +202,10 @@ ResultCode FormatFileSystem(Type type) {
void RegisterFileSystems() {
filesystem_map.clear();
filesystem_romfs = nullptr;
std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX);
auto sdcard = std::make_shared<FileSys::RealVfsDirectory>(sd_directory, filesystem::perms::all);
auto sdcard = std::make_shared<FileSys::RealVfsDirectory>(sd_directory, FileSys::Mode::Write);
RegisterFileSystem(std::make_unique<DeferredFilesystem>(sdcard), Type::SDMC);
RegisterFileSystem(std::make_unique<SaveDataDeferredFilesystem>(), Type::SaveData);

View File

@@ -31,6 +31,9 @@ enum class Type {
SDMC = 3,
};
// 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
// avoids repetitive code.
class VfsDirectoryServiceWrapper {
v_dir backing;
@@ -107,7 +110,7 @@ public:
* @param path Path relative to the archive
* @return Opened directory, or error code
*/
ResultVal<v_dir> OpenDirectory(const std::string& path) const;
ResultVal<v_dir> OpenDirectory(const std::string& path);
/**
* Get the free space
@@ -122,6 +125,10 @@ public:
ResultVal<FileSys::EntryType> GetEntryType(const std::string& path) const;
};
// A class that deferres the creation of a filesystem until a later time.
// This is useful if construction depends on a variable not known when the filesystem is registered.
// Construct this with a filesystem (v_dir) to avoid the deferrence feature or override the
// CreateFilesystem method which will be called on first use.
struct DeferredFilesystem {
DeferredFilesystem(){};
@@ -134,6 +141,8 @@ struct DeferredFilesystem {
return fs;
}
~DeferredFilesystem() = default;
protected:
virtual v_dir CreateFilesystem() {
return fs;

View File

@@ -406,7 +406,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_unique<FileSys::RealVfsFile>(physical_name));
Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(physical_name));
if (!loader)
return true;