From 2251e0d406a2c92b711832121ddc2d72566704e3 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 30 Jun 2018 18:16:14 -0400 Subject: [PATCH] Remove std::filesystem and document/test --- src/common/CMakeLists.txt | 1 - src/common/file_util.cpp | 42 ++++++++ src/common/file_util.h | 22 +++++ src/common/std_filesystem.h | 13 --- src/core/file_sys/content_archive.h | 2 + src/core/file_sys/vfs.cpp | 55 ++++++----- src/core/file_sys/vfs.h | 91 +++++++++++++++-- src/core/file_sys/vfs_offset.cpp | 2 +- src/core/file_sys/vfs_offset.h | 4 + src/core/file_sys/vfs_real.cpp | 98 +++++++++++-------- src/core/file_sys/vfs_real.h | 23 +++-- .../hle/service/filesystem/filesystem.cpp | 83 +++++++--------- src/core/hle/service/filesystem/filesystem.h | 11 ++- src/yuzu/game_list.cpp | 2 +- 14 files changed, 301 insertions(+), 148 deletions(-) delete mode 100644 src/common/std_filesystem.h diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 4660ed2a58..f49a316127 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -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 diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 29e92988b6..c131af748b 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -799,6 +799,48 @@ void SplitFilename83(const std::string& filename, std::array& short_nam } } +std::vector SplitPathComponents(const std::string& filename) { + auto copy(filename); + std::replace(copy.begin(), copy.end(), '\\', '/'); + std::vector 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(name_bck_index, name_fwd_index); + else + name_index = std::max(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) { diff --git a/src/common/file_util.h b/src/common/file_util.h index caed8163a6..a2731daf83 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -150,6 +150,28 @@ size_t ReadFileToString(bool text_file, const char* filename, std::string& str); void SplitFilename83(const std::string& filename, std::array& short_name, std::array& 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 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 +std::vector SliceVector(const std::vector& vector, size_t first, size_t last) { + if (first >= last) + return {}; + last = std::min(last, vector.size()); + return std::vector(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 diff --git a/src/common/std_filesystem.h b/src/common/std_filesystem.h deleted file mode 100644 index 40cdb67a74..0000000000 --- a/src/common/std_filesystem.h +++ /dev/null @@ -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 -namespace filesystem = std::filesystem; -#else -#include -namespace filesystem = std::experimental::filesystem; -#endif diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index 19d04feb24..7d659493c7 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -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 dirs{}; std::vector files{}; diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index 82e8c6041e..5c55bb691a 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp @@ -48,44 +48,43 @@ size_t VfsFile::WriteBytes(std::vector data, size_t offset) { return Write(data.data(), data.size(), offset); } -std::shared_ptr 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 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 VfsDirectory::GetFileAbsolute(const filesystem::path& path) const { +std::shared_ptr VfsDirectory::GetFileAbsolute(const std::string& path) const { if (IsRoot()) return GetFileRelative(path); return GetParentDirectory()->GetFileAbsolute(path); } -std::shared_ptr 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::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(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::GetDirectoryAbsolute( - const filesystem::path& path) const { +std::shared_ptr VfsDirectory::GetDirectoryAbsolute(const std::string& path) const { if (IsRoot()) return GetDirectoryRelative(path); @@ -101,6 +100,8 @@ std::shared_ptr VfsDirectory::GetFile(const std::string& name) const { } std::shared_ptr VfsDirectory::GetSubdirectory(const std::string& name) const { + // if (name == "" || name == "." || name == "/" || name == "\\") + // return std::shared_ptr(const_cast(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; diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h index b130a717fe..1e94aa0f32 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs.h @@ -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; using v_file = std::shared_ptr; 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 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 ReadByte(size_t offset = 0) const; + // Reads size bytes starting at offset in file into a vector. virtual std::vector 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 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 size_t ReadArray(T* data, size_t number_elements, size_t offset = 0) const { static_assert(std::is_trivially_copyable::value, @@ -49,6 +67,8 @@ struct VfsFile : NonCopyable { return Read(reinterpret_cast(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 size_t ReadBytes(T* data, size_t size, size_t offset = 0) const { static_assert(std::is_trivially_copyable::value, @@ -56,6 +76,8 @@ struct VfsFile : NonCopyable { return Read(reinterpret_cast(data), size, offset); } + // Reads one object of type T starting at offset in file. + // Returns the number of bytes read successfully (sizeof(T)). template size_t ReadObject(T* data, size_t offset = 0) const { static_assert(std::is_trivially_copyable::value, @@ -63,9 +85,15 @@ struct VfsFile : NonCopyable { return Read(reinterpret_cast(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 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 size_t WriteArray(T* data, size_t number_elements, size_t offset = 0) { static_assert(std::is_trivially_copyable::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 size_t WriteBytes(T* data, size_t size, size_t offset = 0) { static_assert(std::is_trivially_copyable::value, @@ -81,60 +111,105 @@ struct VfsFile : NonCopyable { return Write(reinterpret_cast(data), size, offset); } + // Writes one object of type T to offset in file. + // Returns the number of bytes written successfully (sizeof(T)). template - 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::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 GetFileRelative(const filesystem::path& path) const; - virtual std::shared_ptr 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 GetFileRelative(const std::string& path) const; + // Calls GetFileRelative(path) on the root of the current directory. + virtual std::shared_ptr GetFileAbsolute(const std::string& path) const; - virtual std::shared_ptr GetDirectoryRelative(const filesystem::path& path) const; - virtual std::shared_ptr 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 GetDirectoryRelative(const std::string& path) const; + // Calls GetDirectoryRelative(path) on the root of the current directory. + virtual std::shared_ptr GetDirectoryAbsolute(const std::string& path) const; + // Returns a vector containing all of the files in this directory. virtual std::vector> 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 GetFile(const std::string& name) const; + // Returns a vector containing all of the subdirectories in this directory. virtual std::vector> 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 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 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 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 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. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a + // subdirectory in one call. template - bool InterpretAsDirectory(v_file file) { - return ReplaceFileWithSubdirectory(file, std::make_shared(file)); + bool InterpretAsDirectory(const std::string& file) { + auto file_p = GetFile(file); + if (file_p == nullptr) + return false; + return ReplaceFileWithSubdirectory(file, std::make_shared(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; diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp index 0bb6973fe0..288499cb51 100644 --- a/src/core/file_sys/vfs_offset.cpp +++ b/src/core/file_sys/vfs_offset.cpp @@ -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(std::min(size - r_offset, r_size), 0); } } // namespace FileSys diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h index a3ef0bde33..adc615b389 100644 --- a/src/core/file_sys/vfs_offset.h +++ b/src/core/file_sys/vfs_offset.h @@ -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 file, size_t size, size_t offset = 0, const std::string& new_name = ""); diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index 7c89d6ffea..0398ca395a 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp @@ -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 RealVfsFile::GetContainingDirectory() const { - return std::make_shared(path.parent_path(), perms); + return std::make_shared(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(entry.path(), perms)); - else if (filesystem::is_regular_file(entry.path())) - files.emplace_back(std::make_shared(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(full_path, perms)); + else + files.emplace_back(std::make_shared(full_path, perms)); + return true; + }); } std::vector> RealVfsDirectory::GetFiles() const { @@ -89,48 +106,49 @@ std::vector> 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 RealVfsDirectory::GetParentDirectory() const { - if (path.parent_path() == path.root_path()) + if (path_components.size() <= 1) return nullptr; - return std::make_shared(path.parent_path(), perms); + return std::make_shared(parent_path, perms); } std::shared_ptr 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(path / name, perms)); + subdirectories.emplace_back(std::make_shared(path + DIR_SEP + name, perms)); return subdirectories.back(); } std::shared_ptr 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(path / name, perms)); + files.emplace_back(std::make_shared(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) { diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h index 8897293366..a06dbbee5a 100644 --- a/src/core/file_sys/vfs_real.h +++ b/src/core/file_sys/vfs_real.h @@ -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 path_components; + std::vector 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> GetFiles() const override; std::vector> 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 path_components; + std::vector parent_components; + Mode perms; std::vector> files; std::vector> subdirectories; }; diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index b42654e1fb..a8fe96b24d 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -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 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( std::make_shared(file, 0, file->GetSize())); @@ -110,8 +91,10 @@ ResultVal VfsDirectoryServiceWrapper::OpenFile(const std::string& path, return ResultCode(-1); } -ResultVal VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path) const { - auto dir = backing->GetDirectoryRelative(filesystem::path(path)); +ResultVal 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 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(nand_directory, filesystem::perms::all); + std::make_shared(nand_directory, FileSys::Mode::Write); return savedata; } }; @@ -158,7 +144,7 @@ protected: * is never removed until UnregisterFileSystems is called. */ static boost::container::flat_map> filesystem_map; -static v_file filesystem_romfs; +static v_file filesystem_romfs = nullptr; ResultCode RegisterFileSystem(std::unique_ptr&& factory, Type type) { auto result = filesystem_map.emplace(type, std::move(factory)); @@ -172,8 +158,8 @@ ResultCode RegisterFileSystem(std::unique_ptr&& 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(sd_directory, filesystem::perms::all); + auto sdcard = std::make_shared(sd_directory, FileSys::Mode::Write); RegisterFileSystem(std::make_unique(sdcard), Type::SDMC); RegisterFileSystem(std::make_unique(), Type::SaveData); diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 2df7222509..e77aff489e 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -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 OpenDirectory(const std::string& path) const; + ResultVal OpenDirectory(const std::string& path); /** * Get the free space @@ -122,6 +125,10 @@ public: ResultVal 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; diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 59f4896ac8..be6cd6da45 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -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 = - Loader::GetLoader(std::make_unique(physical_name)); + Loader::GetLoader(std::make_shared(physical_name)); if (!loader) return true;