2018-07-18 21:07:11 -04:00
|
|
|
// Copyright 2018 yuzu emulator team
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <numeric>
|
|
|
|
#include "common/file_util.h"
|
|
|
|
#include "common/logging/backend.h"
|
|
|
|
#include "core/file_sys/vfs.h"
|
|
|
|
|
|
|
|
namespace FileSys {
|
|
|
|
|
|
|
|
VfsFile::~VfsFile() = default;
|
|
|
|
|
|
|
|
std::string VfsFile::GetExtension() const {
|
2018-07-22 01:23:29 -04:00
|
|
|
return std::string(FileUtil::GetExtensionFromFilename(GetName()));
|
2018-07-18 21:07:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
VfsDirectory::~VfsDirectory() = default;
|
|
|
|
|
|
|
|
boost::optional<u8> VfsFile::ReadByte(size_t offset) const {
|
|
|
|
u8 out{};
|
|
|
|
size_t size = Read(&out, 1, offset);
|
|
|
|
if (size == 1)
|
|
|
|
return out;
|
|
|
|
|
|
|
|
return boost::none;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<u8> VfsFile::ReadBytes(size_t size, size_t offset) const {
|
|
|
|
std::vector<u8> out(size);
|
|
|
|
size_t read_size = Read(out.data(), size, offset);
|
|
|
|
out.resize(read_size);
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<u8> VfsFile::ReadAllBytes() const {
|
|
|
|
return ReadBytes(GetSize());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VfsFile::WriteByte(u8 data, size_t offset) {
|
|
|
|
return Write(&data, 1, offset) == 1;
|
|
|
|
}
|
|
|
|
|
2018-07-20 21:51:28 -04:00
|
|
|
size_t VfsFile::WriteBytes(const std::vector<u8>& data, size_t offset) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return Write(data.data(), data.size(), offset);
|
|
|
|
}
|
|
|
|
|
2018-07-27 18:14:03 -04:00
|
|
|
std::string VfsFile::GetFullPath() const {
|
|
|
|
if (GetContainingDirectory() == nullptr)
|
|
|
|
return "/" + GetName();
|
|
|
|
|
|
|
|
return GetContainingDirectory()->GetFullPath() + "/" + GetName();
|
|
|
|
}
|
|
|
|
|
2018-07-22 01:23:29 -04:00
|
|
|
std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const {
|
2018-07-18 21:07:11 -04:00
|
|
|
auto vec = FileUtil::SplitPathComponents(path);
|
|
|
|
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
|
|
|
|
vec.end());
|
2018-07-22 01:23:29 -04:00
|
|
|
if (vec.empty()) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return nullptr;
|
2018-07-22 01:23:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (vec.size() == 1) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return GetFile(vec[0]);
|
2018-07-22 01:23:29 -04:00
|
|
|
}
|
|
|
|
|
2018-07-18 21:07:11 -04:00
|
|
|
auto dir = GetSubdirectory(vec[0]);
|
|
|
|
for (size_t component = 1; component < vec.size() - 1; ++component) {
|
2018-07-22 01:23:29 -04:00
|
|
|
if (dir == nullptr) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return nullptr;
|
2018-07-22 01:23:29 -04:00
|
|
|
}
|
|
|
|
|
2018-07-18 21:07:11 -04:00
|
|
|
dir = dir->GetSubdirectory(vec[component]);
|
|
|
|
}
|
2018-07-22 01:23:29 -04:00
|
|
|
|
|
|
|
if (dir == nullptr) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return nullptr;
|
2018-07-22 01:23:29 -04:00
|
|
|
}
|
|
|
|
|
2018-07-18 21:07:11 -04:00
|
|
|
return dir->GetFile(vec.back());
|
|
|
|
}
|
|
|
|
|
2018-07-22 01:23:29 -04:00
|
|
|
std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(std::string_view path) const {
|
|
|
|
if (IsRoot()) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return GetFileRelative(path);
|
2018-07-22 01:23:29 -04:00
|
|
|
}
|
2018-07-18 21:07:11 -04:00
|
|
|
|
|
|
|
return GetParentDirectory()->GetFileAbsolute(path);
|
|
|
|
}
|
|
|
|
|
2018-07-22 01:23:29 -04:00
|
|
|
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_view path) const {
|
2018-07-18 21:07:11 -04:00
|
|
|
auto vec = FileUtil::SplitPathComponents(path);
|
|
|
|
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
|
|
|
|
vec.end());
|
2018-07-22 01:23:29 -04:00
|
|
|
if (vec.empty()) {
|
2018-07-18 21:07:11 -04:00
|
|
|
// TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently
|
|
|
|
// because of const-ness
|
|
|
|
return nullptr;
|
2018-07-22 01:23:29 -04:00
|
|
|
}
|
|
|
|
|
2018-07-18 21:07:11 -04:00
|
|
|
auto dir = GetSubdirectory(vec[0]);
|
|
|
|
for (size_t component = 1; component < vec.size(); ++component) {
|
2018-07-22 01:23:29 -04:00
|
|
|
if (dir == nullptr) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return nullptr;
|
2018-07-22 01:23:29 -04:00
|
|
|
}
|
|
|
|
|
2018-07-18 21:07:11 -04:00
|
|
|
dir = dir->GetSubdirectory(vec[component]);
|
|
|
|
}
|
2018-07-22 01:23:29 -04:00
|
|
|
|
2018-07-18 21:07:11 -04:00
|
|
|
return dir;
|
|
|
|
}
|
|
|
|
|
2018-07-22 01:23:29 -04:00
|
|
|
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryAbsolute(std::string_view path) const {
|
|
|
|
if (IsRoot()) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return GetDirectoryRelative(path);
|
2018-07-22 01:23:29 -04:00
|
|
|
}
|
2018-07-18 21:07:11 -04:00
|
|
|
|
|
|
|
return GetParentDirectory()->GetDirectoryAbsolute(path);
|
|
|
|
}
|
|
|
|
|
2018-07-22 01:23:29 -04:00
|
|
|
std::shared_ptr<VfsFile> VfsDirectory::GetFile(std::string_view name) const {
|
2018-07-18 21:07:11 -04:00
|
|
|
const auto& files = GetFiles();
|
|
|
|
const auto iter = std::find_if(files.begin(), files.end(),
|
|
|
|
[&name](const auto& file1) { return name == file1->GetName(); });
|
|
|
|
return iter == files.end() ? nullptr : *iter;
|
|
|
|
}
|
|
|
|
|
2018-07-22 01:23:29 -04:00
|
|
|
std::shared_ptr<VfsDirectory> VfsDirectory::GetSubdirectory(std::string_view name) const {
|
2018-07-18 21:07:11 -04:00
|
|
|
const auto& subs = GetSubdirectories();
|
|
|
|
const auto iter = std::find_if(subs.begin(), subs.end(),
|
|
|
|
[&name](const auto& file1) { return name == file1->GetName(); });
|
|
|
|
return iter == subs.end() ? nullptr : *iter;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VfsDirectory::IsRoot() const {
|
|
|
|
return GetParentDirectory() == nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t VfsDirectory::GetSize() const {
|
|
|
|
const auto& files = GetFiles();
|
2018-07-18 23:03:22 -04:00
|
|
|
const auto sum_sizes = [](const auto& range) {
|
|
|
|
return std::accumulate(range.begin(), range.end(), 0ULL,
|
|
|
|
[](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); });
|
|
|
|
};
|
2018-07-18 21:07:11 -04:00
|
|
|
|
2018-07-18 23:03:22 -04:00
|
|
|
const auto file_total = sum_sizes(files);
|
2018-07-18 21:07:11 -04:00
|
|
|
const auto& sub_dir = GetSubdirectories();
|
2018-07-18 23:03:22 -04:00
|
|
|
const auto subdir_total = sum_sizes(sub_dir);
|
2018-07-18 21:07:11 -04:00
|
|
|
|
|
|
|
return file_total + subdir_total;
|
|
|
|
}
|
|
|
|
|
2018-07-22 01:23:29 -04:00
|
|
|
std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path) {
|
2018-07-18 21:07:11 -04:00
|
|
|
auto vec = FileUtil::SplitPathComponents(path);
|
|
|
|
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
|
|
|
|
vec.end());
|
2018-07-22 01:23:29 -04:00
|
|
|
if (vec.empty()) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return nullptr;
|
2018-07-22 01:23:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (vec.size() == 1) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return CreateFile(vec[0]);
|
2018-07-22 01:23:29 -04:00
|
|
|
}
|
|
|
|
|
2018-07-18 21:07:11 -04:00
|
|
|
auto dir = GetSubdirectory(vec[0]);
|
|
|
|
if (dir == nullptr) {
|
|
|
|
dir = CreateSubdirectory(vec[0]);
|
2018-07-22 01:23:29 -04:00
|
|
|
if (dir == nullptr) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return nullptr;
|
2018-07-22 01:23:29 -04:00
|
|
|
}
|
2018-07-18 21:07:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return dir->CreateFileRelative(FileUtil::GetPathWithoutTop(path));
|
|
|
|
}
|
|
|
|
|
2018-07-22 01:23:29 -04:00
|
|
|
std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path) {
|
|
|
|
if (IsRoot()) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return CreateFileRelative(path);
|
2018-07-22 01:23:29 -04:00
|
|
|
}
|
|
|
|
|
2018-07-18 21:07:11 -04:00
|
|
|
return GetParentDirectory()->CreateFileAbsolute(path);
|
|
|
|
}
|
|
|
|
|
2018-07-22 01:23:29 -04:00
|
|
|
std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_view path) {
|
2018-07-18 21:07:11 -04:00
|
|
|
auto vec = FileUtil::SplitPathComponents(path);
|
|
|
|
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
|
|
|
|
vec.end());
|
2018-07-22 01:23:29 -04:00
|
|
|
if (vec.empty()) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return nullptr;
|
2018-07-22 01:23:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (vec.size() == 1) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return CreateSubdirectory(vec[0]);
|
2018-07-22 01:23:29 -04:00
|
|
|
}
|
|
|
|
|
2018-07-18 21:07:11 -04:00
|
|
|
auto dir = GetSubdirectory(vec[0]);
|
|
|
|
if (dir == nullptr) {
|
|
|
|
dir = CreateSubdirectory(vec[0]);
|
2018-07-22 01:23:29 -04:00
|
|
|
if (dir == nullptr) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return nullptr;
|
2018-07-22 01:23:29 -04:00
|
|
|
}
|
2018-07-18 21:07:11 -04:00
|
|
|
}
|
2018-07-22 01:23:29 -04:00
|
|
|
|
2018-07-18 21:07:11 -04:00
|
|
|
return dir->CreateDirectoryRelative(FileUtil::GetPathWithoutTop(path));
|
|
|
|
}
|
|
|
|
|
2018-07-22 01:23:29 -04:00
|
|
|
std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(std::string_view path) {
|
|
|
|
if (IsRoot()) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return CreateDirectoryRelative(path);
|
2018-07-22 01:23:29 -04:00
|
|
|
}
|
|
|
|
|
2018-07-18 21:07:11 -04:00
|
|
|
return GetParentDirectory()->CreateDirectoryAbsolute(path);
|
|
|
|
}
|
|
|
|
|
2018-07-22 01:23:29 -04:00
|
|
|
bool VfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
|
2018-07-18 21:07:11 -04:00
|
|
|
auto dir = GetSubdirectory(name);
|
2018-07-22 01:23:29 -04:00
|
|
|
if (dir == nullptr) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return false;
|
2018-07-22 01:23:29 -04:00
|
|
|
}
|
2018-07-18 21:07:11 -04:00
|
|
|
|
|
|
|
bool success = true;
|
|
|
|
for (const auto& file : dir->GetFiles()) {
|
2018-07-22 01:23:29 -04:00
|
|
|
if (!DeleteFile(file->GetName())) {
|
2018-07-18 21:07:11 -04:00
|
|
|
success = false;
|
2018-07-22 01:23:29 -04:00
|
|
|
}
|
2018-07-18 21:07:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto& sdir : dir->GetSubdirectories()) {
|
2018-07-22 01:23:29 -04:00
|
|
|
if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) {
|
2018-07-18 21:07:11 -04:00
|
|
|
success = false;
|
2018-07-22 01:23:29 -04:00
|
|
|
}
|
2018-07-18 21:07:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
2018-07-22 01:23:29 -04:00
|
|
|
bool VfsDirectory::Copy(std::string_view src, std::string_view dest) {
|
2018-07-18 21:07:11 -04:00
|
|
|
const auto f1 = GetFile(src);
|
|
|
|
auto f2 = CreateFile(dest);
|
2018-07-22 01:23:29 -04:00
|
|
|
if (f1 == nullptr || f2 == nullptr) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return false;
|
2018-07-22 01:23:29 -04:00
|
|
|
}
|
2018-07-18 21:07:11 -04:00
|
|
|
|
|
|
|
if (!f2->Resize(f1->GetSize())) {
|
|
|
|
DeleteFile(dest);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize();
|
|
|
|
}
|
|
|
|
|
2018-07-27 18:14:03 -04:00
|
|
|
std::string VfsDirectory::GetFullPath() const {
|
|
|
|
if (IsRoot())
|
|
|
|
return GetName();
|
|
|
|
|
|
|
|
return GetParentDirectory()->GetFullPath() + "/" + GetName();
|
|
|
|
}
|
|
|
|
|
2018-07-18 21:07:11 -04:00
|
|
|
bool ReadOnlyVfsDirectory::IsWritable() const {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ReadOnlyVfsDirectory::IsReadable() const {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-22 01:23:29 -04:00
|
|
|
std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateSubdirectory(std::string_view name) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-07-22 01:23:29 -04:00
|
|
|
std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFile(std::string_view name) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-07-22 01:23:29 -04:00
|
|
|
bool ReadOnlyVfsDirectory::DeleteSubdirectory(std::string_view name) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-22 01:23:29 -04:00
|
|
|
bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-22 01:23:29 -04:00
|
|
|
bool ReadOnlyVfsDirectory::Rename(std::string_view name) {
|
2018-07-18 21:07:11 -04:00
|
|
|
return false;
|
|
|
|
}
|
2018-07-27 18:14:03 -04:00
|
|
|
|
2018-07-27 23:55:23 -04:00
|
|
|
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;
|
2018-07-28 21:39:42 -04:00
|
|
|
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;
|
2018-07-27 23:55:23 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-27 18:14:03 -04:00
|
|
|
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();
|
|
|
|
}
|
2018-07-18 21:07:11 -04:00
|
|
|
} // namespace FileSys
|