2018-07-19 03:07:11 +02:00
|
|
|
// Copyright 2018 yuzu emulator team
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
2018-07-21 04:28:35 +02:00
|
|
|
#include <algorithm>
|
2018-07-21 04:30:20 +02:00
|
|
|
#include <cstddef>
|
2018-07-21 04:14:59 +02:00
|
|
|
#include <iterator>
|
2018-07-21 04:23:56 +02:00
|
|
|
#include <utility>
|
2018-07-21 04:14:59 +02:00
|
|
|
|
2018-07-19 03:07:11 +02:00
|
|
|
#include "common/common_paths.h"
|
|
|
|
#include "common/logging/log.h"
|
|
|
|
#include "core/file_sys/vfs_real.h"
|
|
|
|
|
|
|
|
namespace FileSys {
|
|
|
|
|
2018-07-24 04:40:35 +02:00
|
|
|
static std::string ModeFlagsToString(Mode mode) {
|
|
|
|
std::string mode_str;
|
|
|
|
|
|
|
|
// Calculate the correct open mode for the file.
|
|
|
|
if (mode & Mode::Read && mode & Mode::Write) {
|
|
|
|
if (mode & Mode::Append)
|
|
|
|
mode_str = "a+";
|
|
|
|
else
|
|
|
|
mode_str = "r+";
|
|
|
|
} else {
|
|
|
|
if (mode & Mode::Read)
|
|
|
|
mode_str = "r";
|
|
|
|
else if (mode & Mode::Append)
|
|
|
|
mode_str = "a";
|
|
|
|
else if (mode & Mode::Write)
|
|
|
|
mode_str = "w";
|
2018-07-19 03:07:11 +02:00
|
|
|
}
|
2018-07-24 04:40:35 +02:00
|
|
|
|
|
|
|
mode_str += "b";
|
|
|
|
|
|
|
|
return mode_str;
|
2018-07-19 03:07:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_)
|
2018-07-24 04:40:35 +02:00
|
|
|
: backing(path_, ModeFlagsToString(perms_).c_str()), path(path_),
|
2018-07-19 03:07:11 +02:00
|
|
|
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_components.back();
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t RealVfsFile::GetSize() const {
|
|
|
|
return backing.GetSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RealVfsFile::Resize(size_t new_size) {
|
|
|
|
return backing.Resize(new_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const {
|
|
|
|
return std::make_shared<RealVfsDirectory>(parent_path, perms);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RealVfsFile::IsWritable() const {
|
2018-07-24 04:40:35 +02:00
|
|
|
return (perms & Mode::WriteAppend) != 0;
|
2018-07-19 03:07:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool RealVfsFile::IsReadable() const {
|
2018-07-24 04:40:35 +02:00
|
|
|
return (perms & Mode::ReadWrite) != 0;
|
2018-07-19 03:07:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const {
|
|
|
|
if (!backing.Seek(offset, SEEK_SET))
|
|
|
|
return 0;
|
|
|
|
return backing.ReadBytes(data, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) {
|
|
|
|
if (!backing.Seek(offset, SEEK_SET))
|
|
|
|
return 0;
|
|
|
|
return backing.WriteBytes(data, length);
|
|
|
|
}
|
|
|
|
|
2018-07-22 07:23:29 +02:00
|
|
|
bool RealVfsFile::Rename(std::string_view name) {
|
|
|
|
std::string name_str(name.begin(), name.end());
|
|
|
|
const auto out = FileUtil::Rename(GetName(), name_str);
|
|
|
|
|
|
|
|
path = (parent_path + DIR_SEP).append(name);
|
2018-07-19 03:07:11 +02:00
|
|
|
path_components = parent_components;
|
2018-07-22 07:23:29 +02:00
|
|
|
path_components.push_back(std::move(name_str));
|
2018-07-24 04:40:35 +02:00
|
|
|
backing = FileUtil::IOFile(path, ModeFlagsToString(perms).c_str());
|
2018-07-22 07:23:29 +02:00
|
|
|
|
2018-07-19 03:07:11 +02:00
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RealVfsFile::Close() {
|
|
|
|
return backing.Close();
|
|
|
|
}
|
|
|
|
|
|
|
|
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_) {
|
2018-07-24 04:40:35 +02:00
|
|
|
if (!FileUtil::Exists(path) && perms & Mode::WriteAppend)
|
2018-07-19 03:07:11 +02:00
|
|
|
FileUtil::CreateDir(path);
|
2018-07-22 04:36:19 +02:00
|
|
|
|
2018-07-19 03:07:11 +02:00
|
|
|
if (perms == Mode::Append)
|
|
|
|
return;
|
|
|
|
|
|
|
|
FileUtil::ForeachDirectoryEntry(
|
2018-07-22 04:36:19 +02:00
|
|
|
nullptr, path,
|
|
|
|
[this](u64* entries_out, const std::string& directory, const std::string& filename) {
|
2018-07-19 03:07:11 +02:00
|
|
|
std::string full_path = directory + DIR_SEP + filename;
|
|
|
|
if (FileUtil::IsDirectory(full_path))
|
|
|
|
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(full_path, perms));
|
|
|
|
else
|
|
|
|
files.emplace_back(std::make_shared<RealVfsFile>(full_path, perms));
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const {
|
2018-07-21 04:30:20 +02:00
|
|
|
return files;
|
2018-07-19 03:07:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const {
|
2018-07-21 04:30:20 +02:00
|
|
|
return subdirectories;
|
2018-07-19 03:07:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool RealVfsDirectory::IsWritable() const {
|
2018-07-24 04:40:35 +02:00
|
|
|
return (perms & Mode::WriteAppend) != 0;
|
2018-07-19 03:07:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool RealVfsDirectory::IsReadable() const {
|
2018-07-24 04:40:35 +02:00
|
|
|
return (perms & Mode::ReadWrite) != 0;
|
2018-07-19 03:07:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string RealVfsDirectory::GetName() const {
|
|
|
|
return path_components.back();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const {
|
|
|
|
if (path_components.size() <= 1)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return std::make_shared<RealVfsDirectory>(parent_path, perms);
|
|
|
|
}
|
|
|
|
|
2018-07-22 07:23:29 +02:00
|
|
|
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)) {
|
2018-07-19 03:07:11 +02:00
|
|
|
return nullptr;
|
2018-07-22 07:23:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(subdir_path, perms));
|
2018-07-19 03:07:11 +02:00
|
|
|
return subdirectories.back();
|
|
|
|
}
|
|
|
|
|
2018-07-22 07:23:29 +02:00
|
|
|
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)) {
|
2018-07-19 03:07:11 +02:00
|
|
|
return nullptr;
|
2018-07-22 07:23:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
files.emplace_back(std::make_shared<RealVfsFile>(file_path, perms));
|
2018-07-19 03:07:11 +02:00
|
|
|
return files.back();
|
|
|
|
}
|
|
|
|
|
2018-07-22 07:23:29 +02:00
|
|
|
bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) {
|
|
|
|
const std::string subdir_path = (path + DIR_SEP).append(name);
|
|
|
|
|
|
|
|
return FileUtil::DeleteDirRecursively(subdir_path);
|
2018-07-19 03:07:11 +02:00
|
|
|
}
|
|
|
|
|
2018-07-22 07:23:29 +02:00
|
|
|
bool RealVfsDirectory::DeleteFile(std::string_view name) {
|
|
|
|
const auto file = GetFile(name);
|
|
|
|
|
|
|
|
if (file == nullptr) {
|
2018-07-19 03:07:11 +02:00
|
|
|
return false;
|
2018-07-22 07:23:29 +02:00
|
|
|
}
|
|
|
|
|
2018-07-19 03:07:11 +02:00
|
|
|
files.erase(std::find(files.begin(), files.end(), file));
|
2018-07-22 07:23:29 +02:00
|
|
|
|
2018-07-19 03:07:11 +02:00
|
|
|
auto real_file = std::static_pointer_cast<RealVfsFile>(file);
|
|
|
|
real_file->Close();
|
2018-07-22 07:23:29 +02:00
|
|
|
|
|
|
|
const std::string file_path = (path + DIR_SEP).append(name);
|
|
|
|
return FileUtil::Delete(file_path);
|
2018-07-19 03:07:11 +02:00
|
|
|
}
|
|
|
|
|
2018-07-22 07:23:29 +02:00
|
|
|
bool RealVfsDirectory::Rename(std::string_view name) {
|
|
|
|
const std::string new_name = (parent_path + DIR_SEP).append(name);
|
|
|
|
|
|
|
|
return FileUtil::Rename(path, new_name);
|
2018-07-19 03:07:11 +02:00
|
|
|
}
|
|
|
|
|
2018-07-28 00:14:03 +02:00
|
|
|
std::string RealVfsDirectory::GetFullPath() const {
|
|
|
|
auto out = path;
|
|
|
|
std::replace(out.begin(), out.end(), '\\', '/');
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2018-07-19 03:07:11 +02:00
|
|
|
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
2018-07-21 04:14:59 +02:00
|
|
|
const auto iter = std::find(files.begin(), files.end(), file);
|
2018-07-19 03:07:11 +02:00
|
|
|
if (iter == files.end())
|
|
|
|
return false;
|
|
|
|
|
2018-07-21 04:14:59 +02:00
|
|
|
const std::ptrdiff_t offset = std::distance(files.begin(), iter);
|
|
|
|
files[offset] = files.back();
|
2018-07-19 03:07:11 +02:00
|
|
|
files.pop_back();
|
|
|
|
|
2018-07-21 04:23:56 +02:00
|
|
|
subdirectories.emplace_back(std::move(dir));
|
2018-07-19 03:07:11 +02:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} // namespace FileSys
|