diff --git a/rpcs3/Emu/Cell/lv2/sys_fs.cpp b/rpcs3/Emu/Cell/lv2/sys_fs.cpp index b99bce08b8..9ead6f8e48 100644 --- a/rpcs3/Emu/Cell/lv2/sys_fs.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_fs.cpp @@ -199,8 +199,9 @@ error_code sys_fs_open(ppu_thread& ppu, vm::cptr path, s32 flags, vm::ptr< if (!path[0]) return CELL_ENOENT; + std::string processed_path; const std::string_view vpath = path.get_ptr(); - const std::string local_path = vfs::get(vpath); + const std::string local_path = vfs::get(vpath, nullptr, &processed_path); if (vpath.find_first_not_of('/') == -1) { @@ -369,7 +370,7 @@ error_code sys_fs_open(ppu_thread& ppu, vm::cptr path, s32 flags, vm::ptr< } } - if (const u32 id = idm::make(path.get_ptr(), std::move(file), mode, flags)) + if (const u32 id = idm::make(processed_path.c_str(), std::move(file), mode, flags)) { *fd = id; return CELL_OK; @@ -480,9 +481,12 @@ error_code sys_fs_opendir(ppu_thread& ppu, vm::cptr path, vm::ptr fd) if (!path[0]) return CELL_ENOENT; + std::string processed_path; std::vector ext; const std::string_view vpath = path.get_ptr(); - const std::string local_path = vfs::get(vpath, &ext); + const std::string local_path = vfs::get(vpath, &ext, &processed_path); + + processed_path += "/"; if (local_path.empty() && ext.empty()) { @@ -568,7 +572,7 @@ error_code sys_fs_opendir(ppu_thread& ppu, vm::cptr path, vm::ptr fd) data.erase(last, data.end()); - if (const u32 id = idm::make(path.get_ptr(), std::move(data))) + if (const u32 id = idm::make(processed_path.c_str(), std::move(data))) { *fd = id; return CELL_OK; diff --git a/rpcs3/Emu/VFS.cpp b/rpcs3/Emu/VFS.cpp index 95da084ee3..5a3f705c4f 100644 --- a/rpcs3/Emu/VFS.cpp +++ b/rpcs3/Emu/VFS.cpp @@ -84,7 +84,7 @@ bool vfs::mount(std::string_view vpath, std::string_view path) } // Go back one level - list.resize(list.size() - 1); + list.pop_back(); continue; } @@ -108,7 +108,7 @@ bool vfs::mount(std::string_view vpath, std::string_view path) } } -std::string vfs::get(std::string_view vpath, std::vector* out_dir) +std::string vfs::get(std::string_view vpath, std::vector* out_dir, std::string* out_path) { const auto table = g_fxo->get(); @@ -127,6 +127,14 @@ std::string vfs::get(std::string_view vpath, std::vector* out_dir) vpath = "."; } + // Fragments for out_path + std::vector name_list; + + if (out_path) + { + name_list.reserve(vpath.size() / 2); + } + for (std::vector list{&table->root};;) { // Skip one or more '/' @@ -196,14 +204,24 @@ std::string vfs::get(std::string_view vpath, std::vector* out_dir) } // Go back one level - list.resize(list.size() - 1); - result.resize(result.size() - 1); + if (out_path) + { + name_list.pop_back(); + } + + list.pop_back(); + result.pop_back(); continue; } const auto last = list.back(); list.push_back(nullptr); + if (out_path) + { + name_list.push_back(name); + } + result.push_back(name); if (!last) @@ -225,6 +243,13 @@ std::string vfs::get(std::string_view vpath, std::vector* out_dir) } // Handle /host_root (not escaped, not processed) + if (out_path) + { + *out_path = "/"; + *out_path += fmt::merge(name_list, "/"); + *out_path += vpath; + } + return std::string{vpath.substr(1)}; } @@ -240,6 +265,12 @@ std::string vfs::get(std::string_view vpath, std::vector* out_dir) } // Escape and merge path fragments + if (out_path) + { + *out_path = "/"; + *out_path += fmt::merge(name_list, "/"); + } + return std::string{result_base} + vfs::escape(fmt::merge(result, "/")); } diff --git a/rpcs3/Emu/VFS.h b/rpcs3/Emu/VFS.h index 06c931ed0e..5c9075455a 100644 --- a/rpcs3/Emu/VFS.h +++ b/rpcs3/Emu/VFS.h @@ -10,7 +10,7 @@ namespace vfs bool mount(std::string_view vpath, std::string_view path); // Convert VFS path to fs path, optionally listing directories mounted in it - std::string get(std::string_view vpath, std::vector* out_dir = nullptr); + std::string get(std::string_view vpath, std::vector* out_dir = nullptr, std::string* out_path = nullptr); // Escape VFS path by replacing non-portable characters with surrogates std::string escape(std::string_view path, bool escape_slash = false);