Remove std::filesystem and document/test
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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{};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = "");
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user