VFS Regression and Accuracy Fixes (#776)
* Regression and Mode Fixes * Review Fixes * string_view correction * Add operator& for FileSys::Mode * Return std::string from SanitizePath * Farming Simulator Fix * Use != With mode operator&
This commit is contained in:
parent
f6657bc8d7
commit
59cb258409
|
@ -826,7 +826,7 @@ std::string_view GetPathWithoutTop(std::string_view path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
while (path[0] == '\\' || path[0] == '/') {
|
while (path[0] == '\\' || path[0] == '/') {
|
||||||
path.remove_suffix(1);
|
path.remove_prefix(1);
|
||||||
if (path.empty()) {
|
if (path.empty()) {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
@ -870,6 +870,15 @@ std::string_view RemoveTrailingSlash(std::string_view path) {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string SanitizePath(std::string_view path_) {
|
||||||
|
std::string path(path_);
|
||||||
|
std::replace(path.begin(), path.end(), '\\', '/');
|
||||||
|
path.erase(std::unique(path.begin(), path.end(),
|
||||||
|
[](char c1, char c2) { return c1 == '/' && c2 == '/'; }),
|
||||||
|
path.end());
|
||||||
|
return std::string(RemoveTrailingSlash(path));
|
||||||
|
}
|
||||||
|
|
||||||
IOFile::IOFile() {}
|
IOFile::IOFile() {}
|
||||||
|
|
||||||
IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {
|
IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {
|
||||||
|
|
|
@ -178,6 +178,9 @@ std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t la
|
||||||
return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
|
return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'.
|
||||||
|
std::string SanitizePath(std::string_view path);
|
||||||
|
|
||||||
// simple wrapper for cstdlib file functions to
|
// simple wrapper for cstdlib file functions to
|
||||||
// hopefully will make error checking easier
|
// hopefully will make error checking easier
|
||||||
// and make forgetting an fclose() harder
|
// and make forgetting an fclose() harder
|
||||||
|
|
|
@ -11,7 +11,13 @@ namespace FileSys {
|
||||||
enum class Mode : u32 {
|
enum class Mode : u32 {
|
||||||
Read = 1,
|
Read = 1,
|
||||||
Write = 2,
|
Write = 2,
|
||||||
|
ReadWrite = 3,
|
||||||
Append = 4,
|
Append = 4,
|
||||||
|
WriteAppend = 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline u32 operator&(Mode lhs, Mode rhs) {
|
||||||
|
return static_cast<u32>(lhs) & static_cast<u32>(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
|
|
@ -13,24 +13,31 @@
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
static std::string PermissionsToCharArray(Mode perms) {
|
static std::string ModeFlagsToString(Mode mode) {
|
||||||
std::string out;
|
std::string mode_str;
|
||||||
switch (perms) {
|
|
||||||
case Mode::Read:
|
// Calculate the correct open mode for the file.
|
||||||
out += "r";
|
if (mode & Mode::Read && mode & Mode::Write) {
|
||||||
break;
|
if (mode & Mode::Append)
|
||||||
case Mode::Write:
|
mode_str = "a+";
|
||||||
out += "r+";
|
else
|
||||||
break;
|
mode_str = "r+";
|
||||||
case Mode::Append:
|
} else {
|
||||||
out += "a";
|
if (mode & Mode::Read)
|
||||||
break;
|
mode_str = "r";
|
||||||
|
else if (mode & Mode::Append)
|
||||||
|
mode_str = "a";
|
||||||
|
else if (mode & Mode::Write)
|
||||||
|
mode_str = "w";
|
||||||
}
|
}
|
||||||
return out + "b";
|
|
||||||
|
mode_str += "b";
|
||||||
|
|
||||||
|
return mode_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_)
|
RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_)
|
||||||
: backing(path_, PermissionsToCharArray(perms_).c_str()), path(path_),
|
: backing(path_, ModeFlagsToString(perms_).c_str()), path(path_),
|
||||||
parent_path(FileUtil::GetParentPath(path_)),
|
parent_path(FileUtil::GetParentPath(path_)),
|
||||||
path_components(FileUtil::SplitPathComponents(path_)),
|
path_components(FileUtil::SplitPathComponents(path_)),
|
||||||
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
|
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
|
||||||
|
@ -53,11 +60,11 @@ std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RealVfsFile::IsWritable() const {
|
bool RealVfsFile::IsWritable() const {
|
||||||
return perms == Mode::Append || perms == Mode::Write;
|
return (perms & Mode::WriteAppend) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RealVfsFile::IsReadable() const {
|
bool RealVfsFile::IsReadable() const {
|
||||||
return perms == Mode::Read || perms == Mode::Write;
|
return (perms & Mode::ReadWrite) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const {
|
size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const {
|
||||||
|
@ -79,7 +86,7 @@ bool RealVfsFile::Rename(std::string_view name) {
|
||||||
path = (parent_path + DIR_SEP).append(name);
|
path = (parent_path + DIR_SEP).append(name);
|
||||||
path_components = parent_components;
|
path_components = parent_components;
|
||||||
path_components.push_back(std::move(name_str));
|
path_components.push_back(std::move(name_str));
|
||||||
backing = FileUtil::IOFile(path, PermissionsToCharArray(perms).c_str());
|
backing = FileUtil::IOFile(path, ModeFlagsToString(perms).c_str());
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@ -93,7 +100,7 @@ RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_)
|
||||||
path_components(FileUtil::SplitPathComponents(path)),
|
path_components(FileUtil::SplitPathComponents(path)),
|
||||||
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
|
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
|
||||||
perms(perms_) {
|
perms(perms_) {
|
||||||
if (!FileUtil::Exists(path) && (perms == Mode::Write || perms == Mode::Append))
|
if (!FileUtil::Exists(path) && perms & Mode::WriteAppend)
|
||||||
FileUtil::CreateDir(path);
|
FileUtil::CreateDir(path);
|
||||||
|
|
||||||
if (perms == Mode::Append)
|
if (perms == Mode::Append)
|
||||||
|
@ -120,11 +127,11 @@ std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories()
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RealVfsDirectory::IsWritable() const {
|
bool RealVfsDirectory::IsWritable() const {
|
||||||
return perms == Mode::Write || perms == Mode::Append;
|
return (perms & Mode::WriteAppend) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RealVfsDirectory::IsReadable() const {
|
bool RealVfsDirectory::IsReadable() const {
|
||||||
return perms == Mode::Read || perms == Mode::Write;
|
return (perms & Mode::ReadWrite) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string RealVfsDirectory::GetName() const {
|
std::string RealVfsDirectory::GetName() const {
|
||||||
|
|
|
@ -24,7 +24,8 @@ namespace Service::FileSystem {
|
||||||
constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
|
constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
|
||||||
|
|
||||||
static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
|
static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
|
||||||
std::string_view dir_name) {
|
std::string_view dir_name_) {
|
||||||
|
std::string dir_name(FileUtil::SanitizePath(dir_name_));
|
||||||
if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
|
if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
|
||||||
return base;
|
return base;
|
||||||
|
|
||||||
|
@ -38,7 +39,8 @@ std::string VfsDirectoryServiceWrapper::GetName() const {
|
||||||
return backing->GetName();
|
return backing->GetName();
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path, u64 size) const {
|
ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const {
|
||||||
|
std::string path(FileUtil::SanitizePath(path_));
|
||||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||||
auto file = dir->CreateFile(FileUtil::GetFilename(path));
|
auto file = dir->CreateFile(FileUtil::GetFilename(path));
|
||||||
if (file == nullptr) {
|
if (file == nullptr) {
|
||||||
|
@ -52,7 +54,8 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path, u64 s
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const {
|
ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
|
||||||
|
std::string path(FileUtil::SanitizePath(path_));
|
||||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||||
if (path == "/" || path == "\\") {
|
if (path == "/" || path == "\\") {
|
||||||
// TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
|
// TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
|
||||||
|
@ -60,14 +63,15 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const
|
||||||
}
|
}
|
||||||
if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr)
|
if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr)
|
||||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||||
if (!backing->DeleteFile(FileUtil::GetFilename(path))) {
|
if (!dir->DeleteFile(FileUtil::GetFilename(path))) {
|
||||||
// TODO(DarkLordZach): Find a better error code for this
|
// TODO(DarkLordZach): Find a better error code for this
|
||||||
return ResultCode(-1);
|
return ResultCode(-1);
|
||||||
}
|
}
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path) const {
|
ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
|
||||||
|
std::string path(FileUtil::SanitizePath(path_));
|
||||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||||
if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty())
|
if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty())
|
||||||
dir = backing;
|
dir = backing;
|
||||||
|
@ -79,7 +83,8 @@ ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path)
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path) const {
|
ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
|
||||||
|
std::string path(FileUtil::SanitizePath(path_));
|
||||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||||
if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) {
|
if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) {
|
||||||
// TODO(DarkLordZach): Find a better error code for this
|
// TODO(DarkLordZach): Find a better error code for this
|
||||||
|
@ -88,7 +93,8 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path)
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path) const {
|
ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
|
||||||
|
std::string path(FileUtil::SanitizePath(path_));
|
||||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||||
if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) {
|
if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) {
|
||||||
// TODO(DarkLordZach): Find a better error code for this
|
// TODO(DarkLordZach): Find a better error code for this
|
||||||
|
@ -97,8 +103,10 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path,
|
ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
|
||||||
const std::string& dest_path) const {
|
const std::string& dest_path_) const {
|
||||||
|
std::string src_path(FileUtil::SanitizePath(src_path_));
|
||||||
|
std::string dest_path(FileUtil::SanitizePath(dest_path_));
|
||||||
auto src = backing->GetFileRelative(src_path);
|
auto src = backing->GetFileRelative(src_path);
|
||||||
if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
|
if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
|
||||||
// Use more-optimized vfs implementation rename.
|
// Use more-optimized vfs implementation rename.
|
||||||
|
@ -130,8 +138,10 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path,
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path,
|
ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
|
||||||
const std::string& dest_path) const {
|
const std::string& dest_path_) const {
|
||||||
|
std::string src_path(FileUtil::SanitizePath(src_path_));
|
||||||
|
std::string dest_path(FileUtil::SanitizePath(dest_path_));
|
||||||
auto src = GetDirectoryRelativeWrapped(backing, src_path);
|
auto src = GetDirectoryRelativeWrapped(backing, src_path);
|
||||||
if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
|
if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
|
||||||
// Use more-optimized vfs implementation rename.
|
// Use more-optimized vfs implementation rename.
|
||||||
|
@ -154,8 +164,9 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa
|
||||||
return ResultCode(-1);
|
return ResultCode(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path,
|
ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_,
|
||||||
FileSys::Mode mode) const {
|
FileSys::Mode mode) const {
|
||||||
|
std::string path(FileUtil::SanitizePath(path_));
|
||||||
auto npath = path;
|
auto npath = path;
|
||||||
while (npath.size() > 0 && (npath[0] == '/' || npath[0] == '\\'))
|
while (npath.size() > 0 && (npath[0] == '/' || npath[0] == '\\'))
|
||||||
npath = npath.substr(1);
|
npath = npath.substr(1);
|
||||||
|
@ -171,7 +182,8 @@ ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::
|
||||||
return MakeResult<FileSys::VirtualFile>(file);
|
return MakeResult<FileSys::VirtualFile>(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path) {
|
ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) {
|
||||||
|
std::string path(FileUtil::SanitizePath(path_));
|
||||||
auto dir = GetDirectoryRelativeWrapped(backing, path);
|
auto dir = GetDirectoryRelativeWrapped(backing, path);
|
||||||
if (dir == nullptr) {
|
if (dir == nullptr) {
|
||||||
// TODO(DarkLordZach): Find a better error code for this
|
// TODO(DarkLordZach): Find a better error code for this
|
||||||
|
@ -188,7 +200,8 @@ u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
|
ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
|
||||||
const std::string& path) const {
|
const std::string& path_) const {
|
||||||
|
std::string path(FileUtil::SanitizePath(path_));
|
||||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||||
if (dir == nullptr)
|
if (dir == nullptr)
|
||||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||||
|
@ -272,9 +285,9 @@ void RegisterFileSystems() {
|
||||||
sdmc_factory = nullptr;
|
sdmc_factory = nullptr;
|
||||||
|
|
||||||
auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>(
|
auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>(
|
||||||
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), FileSys::Mode::Write);
|
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), FileSys::Mode::ReadWrite);
|
||||||
auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>(
|
auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>(
|
||||||
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::Mode::Write);
|
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::Mode::ReadWrite);
|
||||||
|
|
||||||
auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
|
auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
|
||||||
save_data_factory = std::move(savedata);
|
save_data_factory = std::move(savedata);
|
||||||
|
|
Reference in New Issue