diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/core.cpp | 8 | ||||
-rw-r--r-- | src/core/core.h | 12 | ||||
-rw-r--r-- | src/core/file_sys/savedata_factory.h | 1 | ||||
-rw-r--r-- | src/core/file_sys/vfs.cpp | 148 | ||||
-rw-r--r-- | src/core/file_sys/vfs.h | 66 | ||||
-rw-r--r-- | src/core/file_sys/vfs_real.cpp | 341 | ||||
-rw-r--r-- | src/core/file_sys/vfs_real.h | 56 | ||||
-rw-r--r-- | src/core/hle/service/filesystem/filesystem.cpp | 16 | ||||
-rw-r--r-- | src/core/hle/service/filesystem/filesystem.h | 2 | ||||
-rw-r--r-- | src/core/hle/service/service.cpp | 4 | ||||
-rw-r--r-- | src/core/hle/service/service.h | 7 | ||||
-rw-r--r-- | src/core/loader/loader.cpp | 4 | ||||
-rw-r--r-- | src/core/loader/loader.h | 8 |
13 files changed, 560 insertions, 113 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp index 085ba68d0..69c45c026 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -89,7 +89,7 @@ System::ResultStatus System::SingleStep() { } System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& filepath) { - app_loader = Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(filepath)); + app_loader = Loader::GetLoader(virtual_filesystem->OpenFile(filepath, FileSys::Mode::Read)); if (!app_loader) { LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); @@ -174,6 +174,10 @@ System::ResultStatus System::Init(EmuWindow& emu_window) { CoreTiming::Init(); + // Create a default fs if one doesn't already exist. + if (virtual_filesystem == nullptr) + virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); + current_process = Kernel::Process::Create("main"); cpu_barrier = std::make_shared<CpuBarrier>(); @@ -186,7 +190,7 @@ System::ResultStatus System::Init(EmuWindow& emu_window) { service_manager = std::make_shared<Service::SM::ServiceManager>(); Kernel::Init(); - Service::Init(service_manager); + Service::Init(service_manager, virtual_filesystem); GDBStub::Init(); renderer = VideoCore::CreateRenderer(emu_window); diff --git a/src/core/core.h b/src/core/core.h index c8ca4b247..7cf7ea4e1 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -17,6 +17,8 @@ #include "core/memory.h" #include "core/perf_stats.h" #include "core/telemetry_session.h" +#include "file_sys/vfs_real.h" +#include "hle/service/filesystem/filesystem.h" #include "video_core/debug_utils/debug_utils.h" #include "video_core/gpu.h" @@ -211,6 +213,14 @@ public: return debug_context; } + void SetFilesystem(FileSys::VirtualFilesystem vfs) { + virtual_filesystem = std::move(vfs); + } + + FileSys::VirtualFilesystem GetFilesystem() const { + return virtual_filesystem; + } + private: System(); @@ -225,6 +235,8 @@ private: */ ResultStatus Init(EmuWindow& emu_window); + /// RealVfsFilesystem instance + FileSys::VirtualFilesystem virtual_filesystem; /// AppLoader used to load the current executing application std::unique_ptr<Loader::AppLoader> app_loader; std::unique_ptr<VideoCore::RendererBase> renderer; diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index e3a578c0f..f3cf50d5a 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h @@ -7,6 +7,7 @@ #include <memory> #include <string> #include "common/common_types.h" +#include "common/swap.h" #include "core/hle/result.h" namespace FileSys { diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index dae1c16ef..24e158962 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp @@ -4,12 +4,160 @@ #include <algorithm> #include <numeric> +#include <string> +#include "common/common_paths.h" #include "common/file_util.h" #include "common/logging/backend.h" #include "core/file_sys/vfs.h" namespace FileSys { +VfsFilesystem::VfsFilesystem(VirtualDir root_) : root(std::move(root_)) {} + +VfsFilesystem::~VfsFilesystem() = default; + +std::string VfsFilesystem::GetName() const { + return root->GetName(); +} + +bool VfsFilesystem::IsReadable() const { + return root->IsReadable(); +} + +bool VfsFilesystem::IsWritable() const { + return root->IsWritable(); +} + +VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const { + const auto path = FileUtil::SanitizePath(path_); + if (root->GetFileRelative(path) != nullptr) + return VfsEntryType::File; + if (root->GetDirectoryRelative(path) != nullptr) + return VfsEntryType::Directory; + + return VfsEntryType::None; +} + +VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) { + const auto path = FileUtil::SanitizePath(path_); + return root->GetFileRelative(path); +} + +VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) { + const auto path = FileUtil::SanitizePath(path_); + return root->CreateFileRelative(path); +} + +VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { + const auto old_path = FileUtil::SanitizePath(old_path_); + const auto new_path = FileUtil::SanitizePath(new_path_); + + // VfsDirectory impls are only required to implement copy across the current directory. + if (FileUtil::GetParentPath(old_path) == FileUtil::GetParentPath(new_path)) { + if (!root->Copy(FileUtil::GetFilename(old_path), FileUtil::GetFilename(new_path))) + return nullptr; + return OpenFile(new_path, Mode::ReadWrite); + } + + // Do it using RawCopy. Non-default impls are encouraged to optimize this. + const auto old_file = OpenFile(old_path, Mode::Read); + if (old_file == nullptr) + return nullptr; + auto new_file = OpenFile(new_path, Mode::Read); + if (new_file != nullptr) + return nullptr; + new_file = CreateFile(new_path, Mode::Write); + if (new_file == nullptr) + return nullptr; + if (!VfsRawCopy(old_file, new_file)) + return nullptr; + return new_file; +} + +VirtualFile VfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { + const auto old_path = FileUtil::SanitizePath(old_path_); + const auto new_path = FileUtil::SanitizePath(new_path_); + + // Again, non-default impls are highly encouraged to provide a more optimized version of this. + auto out = CopyFile(old_path_, new_path_); + if (out == nullptr) + return nullptr; + if (DeleteFile(old_path)) + return out; + return nullptr; +} + +bool VfsFilesystem::DeleteFile(std::string_view path_) { + const auto path = FileUtil::SanitizePath(path_); + auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write); + if (parent == nullptr) + return false; + return parent->DeleteFile(FileUtil::GetFilename(path)); +} + +VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { + const auto path = FileUtil::SanitizePath(path_); + return root->GetDirectoryRelative(path); +} + +VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { + const auto path = FileUtil::SanitizePath(path_); + return root->CreateDirectoryRelative(path); +} + +VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) { + const auto old_path = FileUtil::SanitizePath(old_path_); + const auto new_path = FileUtil::SanitizePath(new_path_); + + // Non-default impls are highly encouraged to provide a more optimized version of this. + auto old_dir = OpenDirectory(old_path, Mode::Read); + if (old_dir == nullptr) + return nullptr; + auto new_dir = OpenDirectory(new_path, Mode::Read); + if (new_dir != nullptr) + return nullptr; + new_dir = CreateDirectory(new_path, Mode::Write); + if (new_dir == nullptr) + return nullptr; + + for (const auto& file : old_dir->GetFiles()) { + const auto x = + CopyFile(old_path + DIR_SEP + file->GetName(), new_path + DIR_SEP + file->GetName()); + if (x == nullptr) + return nullptr; + } + + for (const auto& dir : old_dir->GetSubdirectories()) { + const auto x = + CopyDirectory(old_path + DIR_SEP + dir->GetName(), new_path + DIR_SEP + dir->GetName()); + if (x == nullptr) + return nullptr; + } + + return new_dir; +} + +VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path_, std::string_view new_path_) { + const auto old_path = FileUtil::SanitizePath(old_path_); + const auto new_path = FileUtil::SanitizePath(new_path_); + + // Non-default impls are highly encouraged to provide a more optimized version of this. + auto out = CopyDirectory(old_path_, new_path_); + if (out == nullptr) + return nullptr; + if (DeleteDirectory(old_path)) + return out; + return nullptr; +} + +bool VfsFilesystem::DeleteDirectory(std::string_view path_) { + const auto path = FileUtil::SanitizePath(path_); + auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write); + if (parent == nullptr) + return false; + return parent->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path)); +} + VfsFile::~VfsFile() = default; std::string VfsFile::GetExtension() const { diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h index fab9e2b45..141a053ce 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs.h @@ -11,14 +11,74 @@ #include <vector> #include "boost/optional.hpp" #include "common/common_types.h" +#include "core/file_sys/mode.h" namespace FileSys { + +struct VfsFilesystem; struct VfsFile; struct VfsDirectory; -// Convenience typedefs to use VfsDirectory and VfsFile -using VirtualDir = std::shared_ptr<FileSys::VfsDirectory>; -using VirtualFile = std::shared_ptr<FileSys::VfsFile>; +// Convenience typedefs to use Vfs* interfaces +using VirtualFilesystem = std::shared_ptr<VfsFilesystem>; +using VirtualDir = std::shared_ptr<VfsDirectory>; +using VirtualFile = std::shared_ptr<VfsFile>; + +// An enumeration representing what can be at the end of a path in a VfsFilesystem +enum class VfsEntryType { + None, + File, + Directory, +}; + +// A class representing an abstract filesystem. A default implementation given the root VirtualDir +// is provided for convenience, but if the Vfs implementation has any additional state or +// functionality, they will need to override. +struct VfsFilesystem : NonCopyable { + VfsFilesystem(VirtualDir root); + virtual ~VfsFilesystem(); + + // Gets the friendly name for the filesystem. + virtual std::string GetName() const; + + // Return whether or not the user has read permissions on this filesystem. + virtual bool IsReadable() const; + // Return whether or not the user has write permission on this filesystem. + virtual bool IsWritable() const; + + // Determine if the entry at path is non-existant, a file, or a directory. + virtual VfsEntryType GetEntryType(std::string_view path) const; + + // Opens the file with path relative to root. If it doesn't exist, returns nullptr. + virtual VirtualFile OpenFile(std::string_view path, Mode perms); + // Creates a new, empty file at path + virtual VirtualFile CreateFile(std::string_view path, Mode perms); + // Copies the file from old_path to new_path, returning the new file on success and nullptr on + // failure. + virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path); + // Moves the file from old_path to new_path, returning the moved file on success and nullptr on + // failure. + virtual VirtualFile MoveFile(std::string_view old_path, std::string_view new_path); + // Deletes the file with path relative to root, returing true on success. + virtual bool DeleteFile(std::string_view path); + + // Opens the directory with path relative to root. If it doesn't exist, returns nullptr. + virtual VirtualDir OpenDirectory(std::string_view path, Mode perms); + // Creates a new, empty directory at path + virtual VirtualDir CreateDirectory(std::string_view path, Mode perms); + // Copies the directory from old_path to new_path, returning the new directory on success and + // nullptr on failure. + virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path); + // Moves the directory from old_path to new_path, returning the moved directory on success and + // nullptr on failure. + virtual VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path); + // Deletes the directory with path relative to root, returing true on success. + virtual bool DeleteDirectory(std::string_view path); + +protected: + // Root directory in default implementation. + VirtualDir root; +}; // A class representing a file in an abstract filesystem. struct VfsFile : NonCopyable { diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index 82d54da4a..1b5919737 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp @@ -6,7 +6,7 @@ #include <cstddef> #include <iterator> #include <utility> - +#include "common/assert.h" #include "common/common_paths.h" #include "common/logging/log.h" #include "core/file_sys/vfs_real.h" @@ -29,6 +29,8 @@ static std::string ModeFlagsToString(Mode mode) { mode_str = "a"; else if (mode & Mode::Write) mode_str = "w"; + else + UNREACHABLE_MSG("Invalid file open mode: {:02X}", static_cast<u8>(mode)); } mode_str += "b"; @@ -36,8 +38,174 @@ static std::string ModeFlagsToString(Mode mode) { return mode_str; } -RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_) - : backing(path_, ModeFlagsToString(perms_).c_str()), path(path_), +RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {} + +std::string RealVfsFilesystem::GetName() const { + return "Real"; +} + +bool RealVfsFilesystem::IsReadable() const { + return true; +} + +bool RealVfsFilesystem::IsWritable() const { + return true; +} + +VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const { + const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); + if (!FileUtil::Exists(path)) + return VfsEntryType::None; + if (FileUtil::IsDirectory(path)) + return VfsEntryType::Directory; + + return VfsEntryType::File; +} + +VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { + const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); + if (cache.find(path) != cache.end()) { + auto weak = cache[path]; + if (!weak.expired()) { + return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms)); + } + } + + if (!FileUtil::Exists(path) && (perms & Mode::WriteAppend) != 0) + FileUtil::CreateEmptyFile(path); + + auto backing = std::make_shared<FileUtil::IOFile>(path, ModeFlagsToString(perms).c_str()); + cache[path] = backing; + + // Cannot use make_shared as RealVfsFile constructor is private + return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms)); +} + +VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { + const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); + if (!FileUtil::Exists(path) && !FileUtil::CreateEmptyFile(path)) + return nullptr; + return OpenFile(path, perms); +} + +VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { + const auto old_path = + FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); + const auto new_path = + FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); + + if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || + FileUtil::IsDirectory(old_path) || !FileUtil::Copy(old_path, new_path)) + return nullptr; + return OpenFile(new_path, Mode::ReadWrite); +} + +VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { + const auto old_path = + FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); + const auto new_path = + FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); + + if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || + FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) + return nullptr; + + if (cache.find(old_path) != cache.end()) { + auto cached = cache[old_path]; + if (!cached.expired()) { + auto file = cached.lock(); + file->Open(new_path, "r+b"); + cache.erase(old_path); + cache[new_path] = file; + } + } + return OpenFile(new_path, Mode::ReadWrite); +} + +bool RealVfsFilesystem::DeleteFile(std::string_view path_) { + const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); + if (cache.find(path) != cache.end()) { + if (!cache[path].expired()) + cache[path].lock()->Close(); + cache.erase(path); + } + return FileUtil::Delete(path); +} + +VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { + const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); + // Cannot use make_shared as RealVfsDirectory constructor is private + return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); +} + +VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { + const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); + if (!FileUtil::Exists(path) && !FileUtil::CreateDir(path)) + return nullptr; + // Cannot use make_shared as RealVfsDirectory constructor is private + return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); +} + +VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_, + std::string_view new_path_) { + const auto old_path = + FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); + const auto new_path = + FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); + if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || + !FileUtil::IsDirectory(old_path)) + return nullptr; + FileUtil::CopyDir(old_path, new_path); + return OpenDirectory(new_path, Mode::ReadWrite); +} + +VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, + std::string_view new_path_) { + const auto old_path = + FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); + const auto new_path = + FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); + if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || + FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) + return nullptr; + + for (auto& kv : cache) { + // Path in cache starts with old_path + if (kv.first.rfind(old_path, 0) == 0) { + const auto file_old_path = + FileUtil::SanitizePath(kv.first, FileUtil::DirectorySeparator::PlatformDefault); + const auto file_new_path = + FileUtil::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()), + FileUtil::DirectorySeparator::PlatformDefault); + auto cached = cache[file_old_path]; + if (!cached.expired()) { + auto file = cached.lock(); + file->Open(file_new_path, "r+b"); + cache.erase(file_old_path); + cache[file_new_path] = file; + } + } + } + + return OpenDirectory(new_path, Mode::ReadWrite); +} + +bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { + const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); + for (auto& kv : cache) { + // Path in cache starts with old_path + if (kv.first.rfind(path, 0) == 0) { + if (!cache[kv.first].expired()) + cache[kv.first].lock()->Close(); + cache.erase(kv.first); + } + } + return FileUtil::DeleteDirRecursively(path); +} + +RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOFile> backing_, + const std::string& path_, Mode perms_) + : base(base_), backing(std::move(backing_)), path(path_), parent_path(FileUtil::GetParentPath(path_)), path_components(FileUtil::SplitPathComponents(path_)), parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), @@ -48,15 +216,15 @@ std::string RealVfsFile::GetName() const { } size_t RealVfsFile::GetSize() const { - return backing.GetSize(); + return backing->GetSize(); } bool RealVfsFile::Resize(size_t new_size) { - return backing.Resize(new_size); + return backing->Resize(new_size); } std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const { - return std::make_shared<RealVfsDirectory>(parent_path, perms); + return base.OpenDirectory(parent_path, perms); } bool RealVfsFile::IsWritable() const { @@ -68,62 +236,118 @@ bool RealVfsFile::IsReadable() const { } size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const { - if (!backing.Seek(offset, SEEK_SET)) + if (!backing->Seek(offset, SEEK_SET)) return 0; - return backing.ReadBytes(data, length); + return backing->ReadBytes(data, length); } size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) { - if (!backing.Seek(offset, SEEK_SET)) + if (!backing->Seek(offset, SEEK_SET)) return 0; - return backing.WriteBytes(data, length); + return backing->WriteBytes(data, length); } bool RealVfsFile::Rename(std::string_view name) { - std::string name_str(name.begin(), name.end()); - const auto out = FileUtil::Rename(GetName(), name_str); + return base.MoveFile(path, parent_path + DIR_SEP + std::string(name)) != nullptr; +} + +bool RealVfsFile::Close() { + return backing->Close(); +} - path = (parent_path + DIR_SEP).append(name); - path_components = parent_components; - path_components.push_back(std::move(name_str)); - backing = FileUtil::IOFile(path, ModeFlagsToString(perms).c_str()); +// TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if +// constexpr' because there is a compile error in the branch not used. + +template <> +std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const { + if (perms == Mode::Append) + return {}; + + std::vector<VirtualFile> out; + FileUtil::ForeachDirectoryEntry( + nullptr, path, + [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) { + const std::string full_path = directory + DIR_SEP + filename; + if (!FileUtil::IsDirectory(full_path)) + out.emplace_back(base.OpenFile(full_path, perms)); + return true; + }); return out; } -bool RealVfsFile::Close() { - return backing.Close(); +template <> +std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const { + if (perms == Mode::Append) + return {}; + + std::vector<VirtualDir> out; + FileUtil::ForeachDirectoryEntry( + nullptr, path, + [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) { + const std::string full_path = directory + DIR_SEP + filename; + if (FileUtil::IsDirectory(full_path)) + out.emplace_back(base.OpenDirectory(full_path, perms)); + return true; + }); + + return out; } -RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_) - : path(FileUtil::RemoveTrailingSlash(path_)), parent_path(FileUtil::GetParentPath(path)), +RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_) + : base(base_), path(FileUtil::RemoveTrailingSlash(path_)), + parent_path(FileUtil::GetParentPath(path)), path_components(FileUtil::SplitPathComponents(path)), parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), perms(perms_) { if (!FileUtil::Exists(path) && perms & Mode::WriteAppend) FileUtil::CreateDir(path); +} - if (perms == Mode::Append) - return; +std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const { + const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); + if (!FileUtil::Exists(full_path)) + return nullptr; + return base.OpenFile(full_path, perms); +} - FileUtil::ForeachDirectoryEntry( - nullptr, path, - [this](u64* entries_out, const std::string& directory, const std::string& filename) { - std::string full_path = directory + DIR_SEP + filename; - if (FileUtil::IsDirectory(full_path)) - subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(full_path, perms)); - else - files.emplace_back(std::make_shared<RealVfsFile>(full_path, perms)); - return true; - }); +std::shared_ptr<VfsDirectory> RealVfsDirectory::GetDirectoryRelative(std::string_view path) const { + const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); + if (!FileUtil::Exists(full_path)) + return nullptr; + return base.OpenDirectory(full_path, perms); +} + +std::shared_ptr<VfsFile> RealVfsDirectory::GetFile(std::string_view name) const { + return GetFileRelative(name); +} + +std::shared_ptr<VfsDirectory> RealVfsDirectory::GetSubdirectory(std::string_view name) const { + return GetDirectoryRelative(name); +} + +std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view path) { + const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); + return base.CreateFile(full_path, perms); +} + +std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) { + const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); + auto parent = std::string(FileUtil::GetParentPath(full_path)); + return base.CreateDirectory(full_path, perms); +} + +bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { + auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(name)); + return base.DeleteDirectory(full_path); } std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const { - return files; + return IterateEntries<RealVfsFile, VfsFile>(); } std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const { - return subdirectories; + return IterateEntries<RealVfsDirectory, VfsDirectory>(); } bool RealVfsDirectory::IsWritable() const { @@ -142,57 +366,32 @@ std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const { if (path_components.size() <= 1) return nullptr; - return std::make_shared<RealVfsDirectory>(parent_path, perms); + return base.OpenDirectory(parent_path, perms); } std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(std::string_view name) { const std::string subdir_path = (path + DIR_SEP).append(name); - - if (!FileUtil::CreateDir(subdir_path)) { - return nullptr; - } - - subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(subdir_path, perms)); - return subdirectories.back(); + return base.CreateDirectory(subdir_path, perms); } std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(std::string_view name) { const std::string file_path = (path + DIR_SEP).append(name); - - if (!FileUtil::CreateEmptyFile(file_path)) { - return nullptr; - } - - files.emplace_back(std::make_shared<RealVfsFile>(file_path, perms)); - return files.back(); + return base.CreateFile(file_path, perms); } bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) { const std::string subdir_path = (path + DIR_SEP).append(name); - - return FileUtil::DeleteDirRecursively(subdir_path); + return base.DeleteDirectory(subdir_path); } bool RealVfsDirectory::DeleteFile(std::string_view name) { - const auto file = GetFile(name); - - if (file == nullptr) { - return false; - } - - files.erase(std::find(files.begin(), files.end(), file)); - - auto real_file = std::static_pointer_cast<RealVfsFile>(file); - real_file->Close(); - const std::string file_path = (path + DIR_SEP).append(name); - return FileUtil::Delete(file_path); + return base.DeleteFile(file_path); } bool RealVfsDirectory::Rename(std::string_view name) { const std::string new_name = (parent_path + DIR_SEP).append(name); - - return FileUtil::Rename(path, new_name); + return base.MoveFile(path, new_name) != nullptr; } std::string RealVfsDirectory::GetFullPath() const { @@ -202,16 +401,6 @@ std::string RealVfsDirectory::GetFullPath() const { } bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { - const auto iter = std::find(files.begin(), files.end(), file); - if (iter == files.end()) - return false; - - const std::ptrdiff_t offset = std::distance(files.begin(), iter); - files[offset] = files.back(); - files.pop_back(); - - subdirectories.emplace_back(std::move(dir)); - - return true; + return false; } } // namespace FileSys diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h index 243d58576..8a1e79ef6 100644 --- a/src/core/file_sys/vfs_real.h +++ b/src/core/file_sys/vfs_real.h @@ -6,18 +6,45 @@ #include <string_view> +#include <boost/container/flat_map.hpp> #include "common/file_util.h" #include "core/file_sys/mode.h" #include "core/file_sys/vfs.h" namespace FileSys { +class RealVfsFilesystem : public VfsFilesystem { +public: + RealVfsFilesystem(); + + std::string GetName() const override; + bool IsReadable() const override; + bool IsWritable() const override; + VfsEntryType GetEntryType(std::string_view path) const override; + VirtualFile OpenFile(std::string_view path, Mode perms = Mode::Read) override; + VirtualFile CreateFile(std::string_view path, Mode perms = Mode::ReadWrite) override; + VirtualFile CopyFile(std::string_view old_path, std::string_view new_path) override; + VirtualFile MoveFile(std::string_view old_path, std::string_view new_path) override; + bool DeleteFile(std::string_view path) override; + VirtualDir OpenDirectory(std::string_view path, Mode perms = Mode::Read) override; + VirtualDir CreateDirectory(std::string_view path, Mode perms = Mode::ReadWrite) override; + VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path) override; + VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path) override; + bool DeleteDirectory(std::string_view path) override; + +private: + boost::container::flat_map<std::string, std::weak_ptr<FileUtil::IOFile>> cache; +}; + // An implmentation of VfsFile that represents a file on the user's computer. -struct RealVfsFile : public VfsFile { - friend struct RealVfsDirectory; +class RealVfsFile : public VfsFile { + friend class RealVfsDirectory; + friend class RealVfsFilesystem; - RealVfsFile(const std::string& name, Mode perms = Mode::Read); + RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing, + const std::string& path, Mode perms = Mode::Read); +public: std::string GetName() const override; size_t GetSize() const override; bool Resize(size_t new_size) override; @@ -31,7 +58,8 @@ struct RealVfsFile : public VfsFile { private: bool Close(); - FileUtil::IOFile backing; + RealVfsFilesystem& base; + std::shared_ptr<FileUtil::IOFile> backing; std::string path; std::string parent_path; std::vector<std::string> path_components; @@ -40,9 +68,19 @@ private: }; // An implementation of VfsDirectory that represents a directory on the user's computer. -struct RealVfsDirectory : public VfsDirectory { - RealVfsDirectory(const std::string& path, Mode perms = Mode::Read); +class RealVfsDirectory : public VfsDirectory { + friend class RealVfsFilesystem; + RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read); + +public: + std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override; + std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override; + std::shared_ptr<VfsFile> GetFile(std::string_view name) const override; + std::shared_ptr<VfsDirectory> GetSubdirectory(std::string_view name) const override; + std::shared_ptr<VfsFile> CreateFileRelative(std::string_view path) override; + std::shared_ptr<VfsDirectory> CreateDirectoryRelative(std::string_view path) override; + bool DeleteSubdirectoryRecursive(std::string_view name) override; std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; bool IsWritable() const override; @@ -60,13 +98,15 @@ protected: bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; private: + template <typename T, typename R> + std::vector<std::shared_ptr<R>> IterateEntries() const; + + RealVfsFilesystem& base; std::string path; std::string parent_path; std::vector<std::string> path_components; std::vector<std::string> parent_components; Mode perms; - std::vector<std::shared_ptr<VfsFile>> files; - std::vector<std::shared_ptr<VfsDirectory>> subdirectories; }; } // namespace FileSys diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index e17d637e4..5e416cde2 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -59,7 +59,7 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const { std::string path(FileUtil::SanitizePath(path_)); auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); - if (path == "/" || path == "\\") { + if (path.empty()) { // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but... return RESULT_SUCCESS; } @@ -281,15 +281,15 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() { return sdmc_factory->Open(); } -void RegisterFileSystems() { +void RegisterFileSystems(const FileSys::VirtualFilesystem& vfs) { romfs_factory = nullptr; save_data_factory = nullptr; sdmc_factory = nullptr; - auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>( - FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), FileSys::Mode::ReadWrite); - auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>( - FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::Mode::ReadWrite); + auto nand_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), + FileSys::Mode::ReadWrite); + auto sd_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), + FileSys::Mode::ReadWrite); auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory)); save_data_factory = std::move(savedata); @@ -298,8 +298,8 @@ void RegisterFileSystems() { sdmc_factory = std::move(sdcard); } -void InstallInterfaces(SM::ServiceManager& service_manager) { - RegisterFileSystems(); +void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs) { + RegisterFileSystems(vfs); std::make_shared<FSP_LDR>()->InstallAsService(service_manager); std::make_shared<FSP_PR>()->InstallAsService(service_manager); std::make_shared<FSP_SRV>()->InstallAsService(service_manager); diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index d4483daa5..462c13f20 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -36,7 +36,7 @@ ResultVal<FileSys::VirtualDir> OpenSDMC(); // ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenBIS(); /// Registers all Filesystem services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs); // A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of // pointers and booleans. This makes using a VfsDirectory with switch services much easier and diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 6f286ea74..11951adaf 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -198,7 +198,7 @@ void AddNamedPort(std::string name, SharedPtr<ClientPort> port) { } /// Initialize ServiceManager -void Init(std::shared_ptr<SM::ServiceManager>& sm) { +void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesystem& rfs) { // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it // here and pass it into the respective InstallInterfaces functions. auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(); @@ -221,7 +221,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) { EUPLD::InstallInterfaces(*sm); Fatal::InstallInterfaces(*sm); FGM::InstallInterfaces(*sm); - FileSystem::InstallInterfaces(*sm); + FileSystem::InstallInterfaces(*sm, rfs); Friend::InstallInterfaces(*sm); GRC::InstallInterfaces(*sm); HID::InstallInterfaces(*sm); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 046c5e18d..8a294c0f2 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -22,6 +22,10 @@ class ServerSession; class HLERequestContext; } // namespace Kernel +namespace FileSys { +struct VfsFilesystem; +} + namespace Service { namespace SM { @@ -177,7 +181,8 @@ private: }; /// Initialize ServiceManager -void Init(std::shared_ptr<SM::ServiceManager>& sm); +void Init(std::shared_ptr<SM::ServiceManager>& sm, + const std::shared_ptr<FileSys::VfsFilesystem>& vfs); /// Shutdown ServiceManager void Shutdown(); diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 0781fb8c1..a288654df 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -43,10 +43,6 @@ FileType IdentifyFile(FileSys::VirtualFile file) { return FileType::Unknown; } -FileType IdentifyFile(const std::string& file_name) { - return IdentifyFile(std::make_shared<FileSys::RealVfsFile>(file_name)); -} - FileType GuessFromFilename(const std::string& name) { if (name == "main") return FileType::DeconstructedRomDirectory; diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 7bd0adedb..6a9e5a68b 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -43,14 +43,6 @@ enum class FileType { FileType IdentifyFile(FileSys::VirtualFile file); /** - * Identifies the type of a bootable file based on the magic value in its header. - * @param file_name path to file - * @return FileType of file. Note: this will return FileType::Unknown if it is unable to determine - * a filetype, and will never return FileType::Error. - */ -FileType IdentifyFile(const std::string& file_name); - -/** * Guess the type of a bootable file from its name * @param name String name of bootable file * @return FileType of file. Note: this will return FileType::Unknown if it is unable to determine |