summaryrefslogtreecommitdiffstats
path: root/src/core/file_sys/vfs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/file_sys/vfs.cpp')
-rw-r--r--src/core/file_sys/vfs.cpp191
1 files changed, 191 insertions, 0 deletions
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index b99a4fd5b..a5ec50b1a 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 sanitized_old_path = FileUtil::SanitizePath(old_path);
+ const auto sanitized_new_path = FileUtil::SanitizePath(new_path);
+
+ // Again, non-default impls are highly encouraged to provide a more optimized version of this.
+ auto out = CopyFile(sanitized_old_path, sanitized_new_path);
+ if (out == nullptr)
+ return nullptr;
+ if (DeleteFile(sanitized_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 sanitized_old_path = FileUtil::SanitizePath(old_path);
+ const auto sanitized_new_path = FileUtil::SanitizePath(new_path);
+
+ // Non-default impls are highly encouraged to provide a more optimized version of this.
+ auto out = CopyDirectory(sanitized_old_path, sanitized_new_path);
+ if (out == nullptr)
+ return nullptr;
+ if (DeleteDirectory(sanitized_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 {
@@ -46,6 +194,13 @@ size_t VfsFile::WriteBytes(const std::vector<u8>& data, size_t offset) {
return Write(data.data(), data.size(), offset);
}
+std::string VfsFile::GetFullPath() const {
+ if (GetContainingDirectory() == nullptr)
+ return "/" + GetName();
+
+ return GetContainingDirectory()->GetFullPath() + "/" + GetName();
+}
+
std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const {
auto vec = FileUtil::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
@@ -243,6 +398,13 @@ bool VfsDirectory::Copy(std::string_view src, std::string_view dest) {
return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize();
}
+std::string VfsDirectory::GetFullPath() const {
+ if (IsRoot())
+ return GetName();
+
+ return GetParentDirectory()->GetFullPath() + "/" + GetName();
+}
+
bool ReadOnlyVfsDirectory::IsWritable() const {
return false;
}
@@ -270,4 +432,33 @@ bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) {
bool ReadOnlyVfsDirectory::Rename(std::string_view name) {
return false;
}
+
+bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size) {
+ if (file1->GetSize() != file2->GetSize())
+ return false;
+
+ std::vector<u8> f1_v(block_size);
+ std::vector<u8> f2_v(block_size);
+ for (size_t i = 0; i < file1->GetSize(); i += block_size) {
+ auto f1_vs = file1->Read(f1_v.data(), block_size, i);
+ auto f2_vs = file2->Read(f2_v.data(), block_size, i);
+
+ if (f1_vs != f2_vs)
+ return false;
+ auto iters = std::mismatch(f1_v.begin(), f1_v.end(), f2_v.begin(), f2_v.end());
+ if (iters.first != f1_v.end() && iters.second != f2_v.end())
+ return false;
+ }
+
+ return true;
+}
+
+bool VfsRawCopy(VirtualFile src, VirtualFile dest) {
+ if (src == nullptr || dest == nullptr)
+ return false;
+ if (!dest->Resize(src->GetSize()))
+ return false;
+ std::vector<u8> data = src->ReadAllBytes();
+ return dest->WriteBytes(data, 0) == data.size();
+}
} // namespace FileSys