mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-22 02:32:36 +01:00
Rewrite vfs::get and vfs::mount
Preprocess . and .. correctly Don't use recursive locking Also use std::string_view Fix format system for std::string and std::string_view Fix fmt::merge for std::string_view
This commit is contained in:
parent
16dcbe8c74
commit
e8b5555630
@ -513,6 +513,13 @@ bool fs::create_path(const std::string& path)
|
||||
|
||||
bool fs::remove_dir(const std::string& path)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
// Don't allow removing empty path (TODO)
|
||||
g_tls_error = fs::error::noent;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto device = get_virtual_device(path))
|
||||
{
|
||||
return device->remove_dir(path);
|
||||
@ -539,6 +546,13 @@ bool fs::remove_dir(const std::string& path)
|
||||
|
||||
bool fs::rename(const std::string& from, const std::string& to, bool overwrite)
|
||||
{
|
||||
if (from.empty() || to.empty())
|
||||
{
|
||||
// Don't allow opening empty path (TODO)
|
||||
g_tls_error = fs::error::noent;
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto device = get_virtual_device(from);
|
||||
|
||||
if (device != get_virtual_device(to))
|
||||
@ -786,6 +800,13 @@ void fs::file::xfail() const
|
||||
|
||||
fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
// Don't allow opening empty path (TODO)
|
||||
g_tls_error = fs::error::noent;
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto device = get_virtual_device(path))
|
||||
{
|
||||
if (auto&& _file = device->open(path, mode))
|
||||
@ -1176,6 +1197,13 @@ void fs::dir::xnull() const
|
||||
|
||||
bool fs::dir::open(const std::string& path)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
// Don't allow opening empty path (TODO)
|
||||
g_tls_error = fs::error::noent;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto device = get_virtual_device(path))
|
||||
{
|
||||
if (auto&& _dir = device->open_dir(path))
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "cfmt.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
@ -73,32 +74,31 @@ void fmt_class_string<fmt::base57>::format(std::string& out, u64 arg)
|
||||
|
||||
void fmt_class_string<const void*>::format(std::string& out, u64 arg)
|
||||
{
|
||||
if (arg)
|
||||
{
|
||||
fmt::append(out, "%p", reinterpret_cast<const void*>(static_cast<std::uintptr_t>(arg)));
|
||||
}
|
||||
else
|
||||
{
|
||||
out += "(NULL)";
|
||||
}
|
||||
fmt::append(out, "%p", arg);
|
||||
}
|
||||
|
||||
void fmt_class_string<const char*>::format(std::string& out, u64 arg)
|
||||
{
|
||||
if (arg)
|
||||
{
|
||||
out += reinterpret_cast<const char*>(static_cast<std::uintptr_t>(arg));
|
||||
out += reinterpret_cast<const char*>(arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
out += "(NULL)";
|
||||
out += "(NULLSTR)";
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<std::string>::format(std::string& out, u64 arg)
|
||||
{
|
||||
out += get_object(arg).c_str(); // TODO?
|
||||
out += get_object(arg);
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<std::string_view>::format(std::string& out, u64 arg)
|
||||
{
|
||||
out += get_object(arg);
|
||||
}
|
||||
|
||||
template <>
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
|
||||
// Copy null-terminated string from std::string to char array with truncation
|
||||
template <std::size_t N>
|
||||
@ -100,10 +101,10 @@ namespace fmt
|
||||
auto end = source.end();
|
||||
for (--end; it != end; ++it)
|
||||
{
|
||||
result += *it + separator;
|
||||
result += std::string{*it} + separator;
|
||||
}
|
||||
|
||||
return result + source.back();
|
||||
return result + std::string{source.back()};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
@ -198,7 +198,13 @@ error_code sys_fs_open(vm::cptr<char> path, s32 flags, vm::ptr<u32> fd, s32 mode
|
||||
if (!path[0])
|
||||
return CELL_ENOENT;
|
||||
|
||||
const std::string& local_path = vfs::get(path.get_ptr());
|
||||
const std::string_view vpath = path.get_ptr();
|
||||
const std::string local_path = vfs::get(vpath);
|
||||
|
||||
if (vpath.find_first_not_of('/') == -1)
|
||||
{
|
||||
return {CELL_EISDIR, path};
|
||||
}
|
||||
|
||||
if (local_path.empty())
|
||||
{
|
||||
@ -207,7 +213,7 @@ error_code sys_fs_open(vm::cptr<char> path, s32 flags, vm::ptr<u32> fd, s32 mode
|
||||
|
||||
// TODO: other checks for path
|
||||
|
||||
if (local_path == "/" || fs::is_dir(local_path))
|
||||
if (fs::is_dir(local_path))
|
||||
{
|
||||
return {CELL_EISDIR, path};
|
||||
}
|
||||
@ -462,7 +468,14 @@ error_code sys_fs_opendir(vm::cptr<char> path, vm::ptr<u32> fd)
|
||||
if (!path[0])
|
||||
return CELL_ENOENT;
|
||||
|
||||
const std::string& local_path = vfs::get(path.get_ptr());
|
||||
const std::string_view vpath = path.get_ptr();
|
||||
const std::string local_path = vfs::get(vpath);
|
||||
|
||||
if (vpath.find_first_not_of('/') == -1)
|
||||
{
|
||||
// TODO: open root
|
||||
return {CELL_EPERM, path};
|
||||
}
|
||||
|
||||
if (local_path.empty())
|
||||
{
|
||||
@ -471,11 +484,6 @@ error_code sys_fs_opendir(vm::cptr<char> path, vm::ptr<u32> fd)
|
||||
|
||||
// TODO: other checks for path
|
||||
|
||||
if (local_path == "/")
|
||||
{
|
||||
return {CELL_EPERM, path};
|
||||
}
|
||||
|
||||
if (fs::is_file(local_path))
|
||||
{
|
||||
return {CELL_ENOTDIR, path};
|
||||
@ -559,7 +567,14 @@ error_code sys_fs_stat(vm::cptr<char> path, vm::ptr<CellFsStat> sb)
|
||||
if (!path[0])
|
||||
return CELL_ENOENT;
|
||||
|
||||
const std::string local_path = vfs::get(path.get_ptr());
|
||||
const std::string_view vpath = path.get_ptr();
|
||||
const std::string local_path = vfs::get(vpath);
|
||||
|
||||
if (vpath.find_first_not_of('/') == -1)
|
||||
{
|
||||
*sb = {CELL_FS_S_IFDIR | 0444};
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
if (local_path.empty())
|
||||
{
|
||||
@ -568,12 +583,6 @@ error_code sys_fs_stat(vm::cptr<char> path, vm::ptr<CellFsStat> sb)
|
||||
|
||||
fs::stat_t info{};
|
||||
|
||||
if (local_path == "/")
|
||||
{
|
||||
sb->mode = CELL_FS_S_IFDIR | 0444;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
if (!fs::stat(local_path, info))
|
||||
{
|
||||
switch (auto error = fs::g_tls_error)
|
||||
@ -671,18 +680,19 @@ error_code sys_fs_mkdir(vm::cptr<char> path, s32 mode)
|
||||
if (!path[0])
|
||||
return CELL_ENOENT;
|
||||
|
||||
const std::string local_path = vfs::get(path.get_ptr());
|
||||
const std::string_view vpath = path.get_ptr();
|
||||
const std::string local_path = vfs::get(vpath);
|
||||
|
||||
if (vpath.find_first_not_of('/') == -1)
|
||||
{
|
||||
return {CELL_EEXIST, path};
|
||||
}
|
||||
|
||||
if (local_path.empty())
|
||||
{
|
||||
return {CELL_ENOTMOUNTED, path};
|
||||
}
|
||||
|
||||
if (local_path == "/")
|
||||
{
|
||||
return {CELL_EEXIST, path};
|
||||
}
|
||||
|
||||
if (!fs::create_path(local_path))
|
||||
{
|
||||
switch (auto error = fs::g_tls_error)
|
||||
@ -703,19 +713,22 @@ error_code sys_fs_rename(vm::cptr<char> from, vm::cptr<char> to)
|
||||
{
|
||||
sys_fs.warning("sys_fs_rename(from=%s, to=%s)", from, to);
|
||||
|
||||
const std::string local_from = vfs::get(from.get_ptr());
|
||||
const std::string local_to = vfs::get(to.get_ptr());
|
||||
const std::string_view vfrom = from.get_ptr();
|
||||
const std::string local_from = vfs::get(vfrom);
|
||||
|
||||
const std::string_view vto = to.get_ptr();
|
||||
const std::string local_to = vfs::get(vto);
|
||||
|
||||
if (vfrom.find_first_not_of('/') == -1 || vto.find_first_not_of('/') == -1)
|
||||
{
|
||||
return CELL_EPERM;
|
||||
}
|
||||
|
||||
if (local_from.empty() || local_to.empty())
|
||||
{
|
||||
return CELL_ENOTMOUNTED;
|
||||
}
|
||||
|
||||
if (local_to == "/" || local_from == "/")
|
||||
{
|
||||
return CELL_EPERM;
|
||||
}
|
||||
|
||||
if (!fs::rename(local_from, local_to, false))
|
||||
{
|
||||
switch (auto error = fs::g_tls_error)
|
||||
@ -742,18 +755,19 @@ error_code sys_fs_rmdir(vm::cptr<char> path)
|
||||
if (!path[0])
|
||||
return CELL_ENOENT;
|
||||
|
||||
const std::string local_path = vfs::get(path.get_ptr());
|
||||
const std::string_view vpath = path.get_ptr();
|
||||
const std::string local_path = vfs::get(vpath);
|
||||
|
||||
if (vpath.find_first_not_of('/') == -1)
|
||||
{
|
||||
return {CELL_EPERM, path};
|
||||
}
|
||||
|
||||
if (local_path.empty())
|
||||
{
|
||||
return {CELL_ENOTMOUNTED, path};
|
||||
}
|
||||
|
||||
if (local_path == "/")
|
||||
{
|
||||
return {CELL_EPERM, path};
|
||||
}
|
||||
|
||||
if (!fs::remove_dir(local_path))
|
||||
{
|
||||
switch (auto error = fs::g_tls_error)
|
||||
@ -780,18 +794,19 @@ error_code sys_fs_unlink(vm::cptr<char> path)
|
||||
if (!path[0])
|
||||
return CELL_ENOENT;
|
||||
|
||||
const std::string local_path = vfs::get(path.get_ptr());
|
||||
const std::string_view vpath = path.get_ptr();
|
||||
const std::string local_path = vfs::get(vpath);
|
||||
|
||||
if (vpath.find_first_not_of('/') == -1)
|
||||
{
|
||||
return {CELL_EISDIR, path};
|
||||
}
|
||||
|
||||
if (local_path.empty())
|
||||
{
|
||||
return {CELL_ENOTMOUNTED, path};
|
||||
}
|
||||
|
||||
if (local_path == "/")
|
||||
{
|
||||
return {CELL_EISDIR, path};
|
||||
}
|
||||
|
||||
if (!fs::remove_file(local_path))
|
||||
{
|
||||
switch (auto error = fs::g_tls_error)
|
||||
@ -922,16 +937,17 @@ error_code sys_fs_fcntl(u32 fd, u32 op, vm::ptr<void> _arg, u32 _size)
|
||||
{
|
||||
const auto arg = vm::static_ptr_cast<lv2_file_c0000002>(_arg);
|
||||
|
||||
const std::string local_path = vfs::get(arg->path.get_ptr());
|
||||
const std::string_view vpath = arg->path.get_ptr();
|
||||
const std::string local_path = vfs::get(vpath);
|
||||
|
||||
if (vpath.find_first_not_of('/') == -1)
|
||||
{
|
||||
return {CELL_EPERM, vpath};
|
||||
}
|
||||
|
||||
if (local_path.empty())
|
||||
{
|
||||
return {CELL_ENOTMOUNTED, arg->path};
|
||||
}
|
||||
|
||||
if (local_path == "/")
|
||||
{
|
||||
return {CELL_EPERM, arg->path};
|
||||
return {CELL_ENOTMOUNTED, vpath};
|
||||
}
|
||||
|
||||
fs::device_stat info;
|
||||
@ -939,7 +955,7 @@ error_code sys_fs_fcntl(u32 fd, u32 op, vm::ptr<void> _arg, u32 _size)
|
||||
{
|
||||
switch (auto error = fs::g_tls_error)
|
||||
{
|
||||
case fs::error::noent: return {CELL_ENOENT, arg->path};
|
||||
case fs::error::noent: return {CELL_ENOENT, vpath};
|
||||
default: sys_fs.error("sys_fs_fcntl(0xc0000002): unknown error %s", error);
|
||||
}
|
||||
|
||||
@ -1300,18 +1316,19 @@ error_code sys_fs_truncate(vm::cptr<char> path, u64 size)
|
||||
if (!path[0])
|
||||
return CELL_ENOENT;
|
||||
|
||||
const std::string local_path = vfs::get(path.get_ptr());
|
||||
const std::string_view vpath = path.get_ptr();
|
||||
const std::string local_path = vfs::get(vpath);
|
||||
|
||||
if (vpath.find_first_not_of('/') == -1)
|
||||
{
|
||||
return {CELL_EISDIR, path};
|
||||
}
|
||||
|
||||
if (local_path.empty())
|
||||
{
|
||||
return {CELL_ENOTMOUNTED, path};
|
||||
}
|
||||
|
||||
if (local_path == "/")
|
||||
{
|
||||
return {CELL_EISDIR, path};
|
||||
}
|
||||
|
||||
if (!fs::truncate_file(local_path, size))
|
||||
{
|
||||
switch (auto error = fs::g_tls_error)
|
||||
@ -1398,18 +1415,19 @@ error_code sys_fs_disk_free(vm::cptr<char> path, vm::ptr<u64> total_free, vm::pt
|
||||
if (!path[0])
|
||||
return CELL_EINVAL;
|
||||
|
||||
const std::string local_path = vfs::get(path.get_ptr());
|
||||
const std::string_view vpath = path.get_ptr();
|
||||
const std::string local_path = vfs::get(vpath);
|
||||
|
||||
if (vpath.find_first_not_of('/') == -1)
|
||||
{
|
||||
return {CELL_EPERM, path};
|
||||
}
|
||||
|
||||
if (local_path.empty())
|
||||
{
|
||||
return {CELL_ENOTMOUNTED, path};
|
||||
}
|
||||
|
||||
if (local_path == "/")
|
||||
{
|
||||
return {CELL_EPERM, path};
|
||||
}
|
||||
|
||||
fs::device_stat info;
|
||||
if (!fs::statfs(local_path, info))
|
||||
{
|
||||
@ -1439,18 +1457,19 @@ error_code sys_fs_utime(vm::cptr<char> path, vm::cptr<CellFsUtimbuf> timep)
|
||||
if (!path[0])
|
||||
return CELL_ENOENT;
|
||||
|
||||
const std::string local_path = vfs::get(path.get_ptr());
|
||||
const std::string_view vpath = path.get_ptr();
|
||||
const std::string local_path = vfs::get(vpath);
|
||||
|
||||
if (vpath.find_first_not_of('/') == -1)
|
||||
{
|
||||
return {CELL_EISDIR, path};
|
||||
}
|
||||
|
||||
if (local_path.empty())
|
||||
{
|
||||
return {CELL_ENOTMOUNTED, path};
|
||||
}
|
||||
|
||||
if (local_path == "/")
|
||||
{
|
||||
return {CELL_EISDIR, path};
|
||||
}
|
||||
|
||||
if (!fs::utime(local_path, timep->actime, timep->modtime))
|
||||
{
|
||||
switch (auto error = fs::g_tls_error)
|
||||
|
@ -708,13 +708,12 @@ void Emulator::Load(bool add_only)
|
||||
std::string bdvd_dir = g_cfg.vfs.dev_bdvd;
|
||||
|
||||
// Mount default relative path to non-existent directory
|
||||
vfs::mount("", fs::get_config_dir() + "delete_this_dir/");
|
||||
vfs::mount("dev_hdd0", fmt::replace_all(g_cfg.vfs.dev_hdd0, "$(EmulatorDir)", emu_dir));
|
||||
vfs::mount("dev_hdd1", fmt::replace_all(g_cfg.vfs.dev_hdd1, "$(EmulatorDir)", emu_dir));
|
||||
vfs::mount("dev_flash", g_cfg.vfs.get_dev_flash());
|
||||
vfs::mount("dev_usb", fmt::replace_all(g_cfg.vfs.dev_usb000, "$(EmulatorDir)", emu_dir));
|
||||
vfs::mount("dev_usb000", fmt::replace_all(g_cfg.vfs.dev_usb000, "$(EmulatorDir)", emu_dir));
|
||||
vfs::mount("app_home", home_dir.empty() ? elf_dir + '/' : fmt::replace_all(home_dir, "$(EmulatorDir)", emu_dir));
|
||||
vfs::mount("/dev_hdd0", fmt::replace_all(g_cfg.vfs.dev_hdd0, "$(EmulatorDir)", emu_dir));
|
||||
vfs::mount("/dev_hdd1", fmt::replace_all(g_cfg.vfs.dev_hdd1, "$(EmulatorDir)", emu_dir));
|
||||
vfs::mount("/dev_flash", g_cfg.vfs.get_dev_flash());
|
||||
vfs::mount("/dev_usb", fmt::replace_all(g_cfg.vfs.dev_usb000, "$(EmulatorDir)", emu_dir));
|
||||
vfs::mount("/dev_usb000", fmt::replace_all(g_cfg.vfs.dev_usb000, "$(EmulatorDir)", emu_dir));
|
||||
vfs::mount("/app_home", home_dir.empty() ? elf_dir + '/' : fmt::replace_all(home_dir, "$(EmulatorDir)", emu_dir));
|
||||
|
||||
// Special boot mode (directory scan)
|
||||
if (fs::is_dir(m_path))
|
||||
@ -903,7 +902,7 @@ void Emulator::Load(bool add_only)
|
||||
{
|
||||
fs::file sfb_file;
|
||||
|
||||
vfs::mount("dev_bdvd", bdvd_dir);
|
||||
vfs::mount("/dev_bdvd", bdvd_dir);
|
||||
LOG_NOTICE(LOADER, "Disc: %s", vfs::get("/dev_bdvd"));
|
||||
|
||||
if (!sfb_file.open(vfs::get("/dev_bdvd/PS3_DISC.SFB")) || sfb_file.size() < 4 || sfb_file.read<u32>() != ".SFB"_u32)
|
||||
@ -964,7 +963,7 @@ void Emulator::Load(bool add_only)
|
||||
}
|
||||
else if (m_cat == "DG" && from_hdd0_game)
|
||||
{
|
||||
vfs::mount("dev_bdvd/PS3_GAME", hdd0_game + m_path.substr(hdd0_game.size(), 10));
|
||||
vfs::mount("/dev_bdvd/PS3_GAME", hdd0_game + m_path.substr(hdd0_game.size(), 10));
|
||||
LOG_NOTICE(LOADER, "Game: %s", vfs::get("/dev_bdvd/PS3_GAME"));
|
||||
}
|
||||
else if (disc.empty())
|
||||
@ -975,7 +974,7 @@ void Emulator::Load(bool add_only)
|
||||
else
|
||||
{
|
||||
bdvd_dir = disc;
|
||||
vfs::mount("dev_bdvd", bdvd_dir);
|
||||
vfs::mount("/dev_bdvd", bdvd_dir);
|
||||
LOG_NOTICE(LOADER, "Disk: %s", vfs::get("/dev_bdvd"));
|
||||
}
|
||||
|
||||
@ -1062,10 +1061,10 @@ void Emulator::Load(bool add_only)
|
||||
return m_path = hdd0_boot, Load();
|
||||
}
|
||||
|
||||
// Mount /host_root/ if necessary
|
||||
// Mount /host_root/ if necessary (special value)
|
||||
if (g_cfg.vfs.host_root)
|
||||
{
|
||||
vfs::mount("host_root", {});
|
||||
vfs::mount("/host_root", "/");
|
||||
}
|
||||
|
||||
// Open SELF or ELF
|
||||
|
@ -2,88 +2,234 @@
|
||||
#include "IdManager.h"
|
||||
#include "VFS.h"
|
||||
|
||||
#include <regex>
|
||||
#include "Utilities/mutex.h"
|
||||
#include "Utilities/StrUtil.h"
|
||||
|
||||
struct vfs_directory
|
||||
{
|
||||
// Real path (empty if root or not exists)
|
||||
std::string path;
|
||||
|
||||
// Virtual subdirectories (vector because only vector allows incomplete types)
|
||||
std::vector<std::pair<std::string, vfs_directory>> dirs;
|
||||
};
|
||||
|
||||
struct vfs_manager
|
||||
{
|
||||
shared_mutex mutex;
|
||||
|
||||
// Device name -> Real path
|
||||
std::unordered_map<std::string, std::string> mounted;
|
||||
// VFS root
|
||||
vfs_directory root;
|
||||
};
|
||||
|
||||
const std::regex s_regex_ps3("^/+(.*?)(?:$|/)(.*)", std::regex::optimize);
|
||||
|
||||
bool vfs::mount(const std::string& dev_name, const std::string& path)
|
||||
bool vfs::mount(std::string_view vpath, std::string_view path)
|
||||
{
|
||||
const auto table = fxm::get_always<vfs_manager>();
|
||||
|
||||
safe_writer_lock lock(table->mutex);
|
||||
std::lock_guard lock(table->mutex);
|
||||
|
||||
return table->mounted.emplace(dev_name, path).second;
|
||||
if (vpath.empty())
|
||||
{
|
||||
// Empty relative path, should set relative path base; unsupported
|
||||
return false;
|
||||
}
|
||||
|
||||
for (std::vector<vfs_directory*> list{&table->root};;)
|
||||
{
|
||||
// Skip one or more '/'
|
||||
const auto pos = vpath.find_first_not_of('/');
|
||||
|
||||
if (pos == 0)
|
||||
{
|
||||
// Mounting relative path is not supported
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pos == -1)
|
||||
{
|
||||
// Mounting completed
|
||||
list.back()->path = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get fragment name
|
||||
const auto name = vpath.substr(pos, vpath.find_first_of('/', pos) - pos);
|
||||
vpath.remove_prefix(name.size() + pos);
|
||||
|
||||
if (name == ".")
|
||||
{
|
||||
// Keep current
|
||||
continue;
|
||||
}
|
||||
|
||||
if (name == "..")
|
||||
{
|
||||
// Root parent is root
|
||||
if (list.size() == 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Go back one level
|
||||
list.resize(list.size() - 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find or add
|
||||
const auto last = list.back();
|
||||
|
||||
for (auto& dir : last->dirs)
|
||||
{
|
||||
if (dir.first == name)
|
||||
{
|
||||
list.push_back(&dir.second);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (last == list.back())
|
||||
{
|
||||
// Add new entry
|
||||
list.push_back(&last->dirs.emplace_back(name, vfs_directory{}).second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string vfs::get(const std::string& vpath, const std::string* prev, std::size_t pos)
|
||||
std::string vfs::get(std::string_view vpath, std::vector<std::string>* out_dir)
|
||||
{
|
||||
const auto table = fxm::get_always<vfs_manager>();
|
||||
|
||||
safe_reader_lock lock(table->mutex);
|
||||
reader_lock lock(table->mutex);
|
||||
|
||||
std::smatch match;
|
||||
// Resulting path fragments: decoded ones
|
||||
std::vector<std::string_view> result;
|
||||
result.reserve(vpath.size() / 2);
|
||||
|
||||
if (!std::regex_match(vpath.begin() + pos, vpath.end(), match, s_regex_ps3))
|
||||
// Mounted path
|
||||
std::string_view result_base;
|
||||
|
||||
if (vpath.empty())
|
||||
{
|
||||
const auto found = table->mounted.find("");
|
||||
// Empty relative path (reuse further return)
|
||||
vpath = ".";
|
||||
}
|
||||
|
||||
if (found == table->mounted.end())
|
||||
for (std::vector<const vfs_directory*> list{&table->root};;)
|
||||
{
|
||||
// Skip one or more '/'
|
||||
const auto pos = vpath.find_first_not_of('/');
|
||||
|
||||
if (pos == 0)
|
||||
{
|
||||
LOG_WARNING(GENERAL, "vfs::get(): no default directory: %s", vpath);
|
||||
return {};
|
||||
// Relative path: point to non-existent location
|
||||
return fs::get_config_dir() + "delete_this_dir.../delete_this...";
|
||||
}
|
||||
|
||||
return found->second + vfs::escape(vpath);
|
||||
}
|
||||
|
||||
if (match.length(1) + pos == 0)
|
||||
{
|
||||
return "/";
|
||||
}
|
||||
|
||||
std::string dev;
|
||||
|
||||
if (prev)
|
||||
{
|
||||
dev += *prev;
|
||||
dev += '/';
|
||||
}
|
||||
|
||||
dev += match.str(1);
|
||||
|
||||
const auto found = table->mounted.find(dev);
|
||||
|
||||
if (found == table->mounted.end())
|
||||
{
|
||||
if (match.length(2))
|
||||
if (pos == -1)
|
||||
{
|
||||
return vfs::get(vpath, &dev, pos + match.position(1) + match.length(1));
|
||||
// Absolute path: finalize
|
||||
for (auto it = list.rbegin(), rend = list.rend(); it != rend; it++)
|
||||
{
|
||||
if (auto* dir = *it; dir && !dir->path.empty())
|
||||
{
|
||||
// Save latest valid mount path
|
||||
result_base = dir->path;
|
||||
|
||||
// Erase unnecessary path fragments
|
||||
result.erase(result.begin(), result.begin() + (std::distance(it, rend) - 1));
|
||||
|
||||
// Extract mounted subdirectories (TODO)
|
||||
if (out_dir)
|
||||
{
|
||||
for (auto& pair : dir->dirs)
|
||||
{
|
||||
if (!pair.second.path.empty())
|
||||
{
|
||||
out_dir->emplace_back(pair.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vpath.empty())
|
||||
{
|
||||
// Finalize path with '/'
|
||||
result.emplace_back("");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_WARNING(GENERAL, "vfs::get(): device not found: %s", vpath);
|
||||
// Get fragment name
|
||||
const auto name = vpath.substr(pos, vpath.find_first_of('/', pos) - pos);
|
||||
vpath.remove_prefix(name.size() + pos);
|
||||
|
||||
// Process special directories
|
||||
if (name == ".")
|
||||
{
|
||||
// Keep current
|
||||
continue;
|
||||
}
|
||||
|
||||
if (name == "..")
|
||||
{
|
||||
// Root parent is root
|
||||
if (list.size() == 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Go back one level
|
||||
list.resize(list.size() - 1);
|
||||
result.resize(result.size() - 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto last = list.back();
|
||||
list.push_back(nullptr);
|
||||
|
||||
result.push_back(name);
|
||||
|
||||
if (!last)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto& dir : last->dirs)
|
||||
{
|
||||
if (dir.first == name)
|
||||
{
|
||||
list.back() = &dir.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (last->path == "/")
|
||||
{
|
||||
if (vpath.empty())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// Handle /host_root (not escaped, not processed)
|
||||
return std::string{vpath.substr(1)};
|
||||
}
|
||||
}
|
||||
|
||||
if (result_base.empty())
|
||||
{
|
||||
// Not mounted
|
||||
return {};
|
||||
}
|
||||
|
||||
if (found->second.empty())
|
||||
{
|
||||
// Don't escape /host_root (TODO)
|
||||
return match.str(2);
|
||||
}
|
||||
|
||||
// Escape and concatenate
|
||||
return found->second + vfs::escape(match.str(2));
|
||||
// Escape and merge path fragments
|
||||
return std::string{result_base} + vfs::escape(fmt::merge(result, "/"));
|
||||
}
|
||||
|
||||
|
||||
std::string vfs::escape(const std::string& path)
|
||||
std::string vfs::escape(std::string_view path)
|
||||
{
|
||||
std::string result;
|
||||
result.reserve(path.size());
|
||||
@ -175,7 +321,7 @@ std::string vfs::escape(const std::string& path)
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string vfs::unescape(const std::string& path)
|
||||
std::string vfs::unescape(std::string_view path)
|
||||
{
|
||||
std::string result;
|
||||
result.reserve(path.size());
|
||||
|
@ -1,18 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace vfs
|
||||
{
|
||||
// Mount VFS device
|
||||
bool mount(const std::string& dev_name, const std::string& path);
|
||||
bool mount(std::string_view vpath, std::string_view path);
|
||||
|
||||
// Convert VFS path to fs path
|
||||
std::string get(const std::string& vpath, const std::string* = nullptr, std::size_t = 0);
|
||||
// Convert VFS path to fs path, optionally listing directories mounted in it
|
||||
std::string get(std::string_view vpath, std::vector<std::string>* out_dir = nullptr);
|
||||
|
||||
// Escape VFS path by replacing non-portable characters with surrogates
|
||||
std::string escape(const std::string& path);
|
||||
std::string escape(std::string_view path);
|
||||
|
||||
// Invert escape operation
|
||||
std::string unescape(const std::string& path);
|
||||
std::string unescape(std::string_view path);
|
||||
}
|
||||
|
@ -55,8 +55,7 @@ trophy_manager_dialog::trophy_manager_dialog(std::shared_ptr<gui_settings> gui_s
|
||||
m_show_platinum_trophies = m_gui_settings->GetValue(gui::tr_show_platinum).toBool();
|
||||
|
||||
// HACK: dev_hdd0 must be mounted for vfs to work for loading trophies.
|
||||
vfs::mount("dev_hdd0", Emu.GetHddDir());
|
||||
|
||||
vfs::mount("/dev_hdd0", Emu.GetHddDir());
|
||||
|
||||
// Get the currently selected user's trophy path.
|
||||
m_trophy_dir = "/dev_hdd0/home/" + Emu.GetUsr() + "/trophy/";
|
||||
@ -577,7 +576,7 @@ void trophy_manager_dialog::ShowContextMenu(const QPoint& loc)
|
||||
QString path = qstr(m_trophies_db[db_ind]->path);
|
||||
QDesktopServices::openUrl(QUrl("file:///" + path));
|
||||
});
|
||||
|
||||
|
||||
menu->addAction(show_trophy_dir);
|
||||
menu->exec(globalPos);
|
||||
}
|
||||
|
@ -43,5 +43,6 @@ namespace std { inline namespace literals { inline namespace chrono_literals {}}
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
|
||||
using namespace std::literals;
|
||||
|
Loading…
Reference in New Issue
Block a user