diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index 769940cf0a..8e0d65a33a 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -171,4 +171,14 @@ File* HandleTable::GetFile(const std::filesystem::path& host_name) { return nullptr; } +int HandleTable::GetFileDescriptor(File* file) { + std::scoped_lock lock{m_mutex}; + auto it = std::find(m_files.begin(), m_files.end(), file); + + if (it != m_files.end()) { + return std::distance(m_files.begin(), it) + RESERVED_HANDLES; + } + return 0; +} + } // namespace Core::FileSys diff --git a/src/core/file_sys/fs.h b/src/core/file_sys/fs.h index eeaeaf7810..01003ab8f6 100644 --- a/src/core/file_sys/fs.h +++ b/src/core/file_sys/fs.h @@ -9,6 +9,7 @@ #include #include #include "common/io_file.h" +#include "common/logging/formatter.h" namespace Core::FileSys { @@ -36,6 +37,14 @@ class MntPoints { std::filesystem::path GetHostPath(std::string_view guest_directory, bool* is_read_only = nullptr); + const MntPair* GetMountFromHostPath(const std::string& host_path) { + std::scoped_lock lock{m_mutex}; + const auto it = std::ranges::find_if(m_mnt_pairs, [&](const MntPair& mount) { + return host_path.starts_with(std::string{fmt::UTF(mount.host_path.u8string()).data}); + }); + return it == m_mnt_pairs.end() ? nullptr : &*it; + } + const MntPair* GetMount(const std::string& guest_path) { std::scoped_lock lock{m_mutex}; const auto it = std::ranges::find_if( @@ -75,6 +84,7 @@ class HandleTable { void DeleteHandle(int d); File* GetFile(int d); File* GetFile(const std::filesystem::path& host_name); + int GetFileDescriptor(File* file); private: std::vector m_files; diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index c4f1e47990..3f99991d55 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -563,12 +563,47 @@ static int GetDents(int fd, char* buf, int nbytes, s64* basep) { return sizeof(OrbisKernelDirent); } +static int HandleSeparateUpdateDents(int fd, char* buf, int nbytes, s64* basep) { + int dir_entries = 0; + + auto* h = Common::Singleton::Instance(); + auto* mnt = Common::Singleton::Instance(); + auto* file = h->GetFile(fd); + auto update_dir_name = std::string{fmt::UTF(file->m_host_name.u8string()).data}; + auto mount = mnt->GetMountFromHostPath(update_dir_name); + auto suffix = std::string{fmt::UTF(mount->host_path.u8string()).data}; + auto guest_name = mount->mount + "/" + update_dir_name.substr(suffix.size() + 1); + + size_t pos = update_dir_name.find("-UPDATE"); + if (pos != std::string::npos) { + update_dir_name.erase(pos, 7); + + u32 handle = h->CreateHandle(); + auto* new_file = h->GetFile(handle); + new_file->is_directory = true; + new_file->m_guest_name = guest_name; + new_file->m_host_name = update_dir_name; + if (!std::filesystem::is_directory(new_file->m_host_name)) { + h->DeleteHandle(handle); + return dir_entries; + } else { + new_file->dirents = GetDirectoryEntries(new_file->m_host_name); + new_file->dirents_index = 0; + } + new_file->is_opened = true; + dir_entries = GetDents(h->GetFileDescriptor(new_file), buf, nbytes, basep); + h->DeleteHandle(handle); + } + + return dir_entries; +} + int PS4_SYSV_ABI sceKernelGetdents(int fd, char* buf, int nbytes) { - return GetDents(fd, buf, nbytes, nullptr); + return GetDents(fd, buf, nbytes, nullptr) + HandleSeparateUpdateDents(fd, buf, nbytes, nullptr); } int PS4_SYSV_ABI sceKernelGetdirentries(int fd, char* buf, int nbytes, s64* basep) { - return GetDents(fd, buf, nbytes, basep); + return GetDents(fd, buf, nbytes, basep) + HandleSeparateUpdateDents(fd, buf, nbytes, basep); } s64 PS4_SYSV_ABI sceKernelPwrite(int d, void* buf, size_t nbytes, s64 offset) { diff --git a/src/emulator.cpp b/src/emulator.cpp index c0b01229a0..0b3f86b01b 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -105,7 +105,9 @@ void Emulator::Run(const std::filesystem::path& file) { // Use the eboot from the separated updates folder if it's there std::filesystem::path game_patch_folder = file.parent_path().concat("-UPDATE"); bool use_game_patch = std::filesystem::exists(game_patch_folder / "sce_sys"); - std::filesystem::path eboot_path = use_game_patch ? game_patch_folder / file.filename() : file; + std::filesystem::path eboot_path = std::filesystem::exists(game_patch_folder / file.filename()) + ? game_patch_folder / file.filename() + : file; // Applications expect to be run from /app0 so mount the file's parent path as app0. auto* mnt = Common::Singleton::Instance(); diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 9bf7c7188a..3c0c7fb068 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -1159,7 +1159,7 @@ separateUpdatesCheckBox - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID.