From aa5c1c10dfa560d55c59da2d7f841c3e9e0485fc Mon Sep 17 00:00:00 2001 From: ElBread3 <92335081+ElBread3@users.noreply.github.com> Date: Tue, 17 Dec 2024 04:42:21 -0600 Subject: [PATCH] More Fixes for Separate Update (#1487) * handle getdents + fix condition + add info to description * fix not handling dents errors * to not overwrite it, only gather separate update entries when normal folder is done * fix always setting entries to 0 and guest name including "UPDATE" * reset indexes on completion * don't use concat, fixes long standing bug * make sce_module module loading take both paths into account --- src/core/file_sys/fs.cpp | 10 ++++ src/core/file_sys/fs.h | 10 ++++ src/core/libraries/kernel/file_system.cpp | 58 ++++++++++++++++++++++- src/emulator.cpp | 46 +++++++++++++----- src/qt_gui/gui_context_menus.h | 22 ++++----- src/qt_gui/translations/en.ts | 2 +- 6 files changed, 119 insertions(+), 29 deletions(-) diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index 92f725cc..45ba67b9 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -199,4 +199,14 @@ void HandleTable::CreateStdHandles() { setup("/dev/stderr", new Devices::Logger("stderr", true)); // stderr } +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); + } + return 0; +} + } // namespace Core::FileSys diff --git a/src/core/file_sys/fs.h b/src/core/file_sys/fs.h index e219887c..56df32ad 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" #include "core/devices/base_device.h" namespace Core::FileSys { @@ -37,6 +38,14 @@ public: 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(m_mnt_pairs, [&](const auto& mount) { @@ -86,6 +95,7 @@ public: void DeleteHandle(int d); File* GetFile(int d); File* GetFile(const std::filesystem::path& host_name); + int GetFileDescriptor(File* file); void CreateStdHandles(); diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index 5ba9976c..57efbb63 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -695,12 +695,66 @@ 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}; + + size_t pos = update_dir_name.find("-UPDATE"); + if (pos != std::string::npos) { + update_dir_name.erase(pos, 7); + auto guest_name = mount->mount + "/" + update_dir_name.substr(suffix.size() + 1); + int descriptor; + + auto existent_folder = h->GetFile(update_dir_name); + if (!existent_folder) { + u32 handle = h->CreateHandle(); + auto* new_file = h->GetFile(handle); + new_file->type = Core::FileSys::FileType::Directory; + 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; + descriptor = h->GetFileDescriptor(new_file); + } else { + descriptor = h->GetFileDescriptor(existent_folder); + } + + dir_entries = GetDents(descriptor, buf, nbytes, basep); + if (dir_entries == ORBIS_OK && existent_folder) { + existent_folder->dirents_index = 0; + file->dirents_index = 0; + } + } + + return dir_entries; +} + int PS4_SYSV_ABI sceKernelGetdents(int fd, char* buf, int nbytes) { - return GetDents(fd, buf, nbytes, nullptr); + int a = GetDents(fd, buf, nbytes, nullptr); + if (a == ORBIS_OK) { + return HandleSeparateUpdateDents(fd, buf, nbytes, nullptr); + } + return a; } int PS4_SYSV_ABI sceKernelGetdirentries(int fd, char* buf, int nbytes, s64* basep) { - return GetDents(fd, buf, nbytes, basep); + int a = GetDents(fd, buf, nbytes, basep); + if (a == ORBIS_OK) { + return HandleSeparateUpdateDents(fd, buf, nbytes, basep); + } + return a; } 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 c517bc28..252a3441 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include "common/config.h" @@ -106,9 +107,11 @@ Emulator::~Emulator() { 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 game_patch_folder = file.parent_path(); + game_patch_folder += "-UPDATE"; + 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(); @@ -226,20 +229,37 @@ void Emulator::Run(const std::filesystem::path& file) { LoadSystemModules(eboot_path, game_info.game_serial); // Load all prx from game's sce_module folder - std::filesystem::path sce_module_folder = file.parent_path() / "sce_module"; - if (std::filesystem::is_directory(sce_module_folder)) { - for (const auto& entry : std::filesystem::directory_iterator(sce_module_folder)) { - std::filesystem::path module_path = entry.path(); - std::filesystem::path update_module_path = - eboot_path.parent_path() / "sce_module" / entry.path().filename(); - if (std::filesystem::exists(update_module_path) && use_game_patch) { - module_path = update_module_path; + std::vector modules_to_load; + std::filesystem::path game_module_folder = file.parent_path() / "sce_module"; + if (std::filesystem::is_directory(game_module_folder)) { + for (const auto& entry : std::filesystem::directory_iterator(game_module_folder)) { + if (entry.is_regular_file()) { + modules_to_load.push_back(entry.path()); } - LOG_INFO(Loader, "Loading {}", fmt::UTF(module_path.u8string())); - linker->LoadModule(module_path); } } + // Load all prx from separate update's sce_module folder + std::filesystem::path update_module_folder = game_patch_folder / "sce_module"; + if (std::filesystem::is_directory(update_module_folder)) { + for (const auto& entry : std::filesystem::directory_iterator(update_module_folder)) { + auto it = std::find_if(modules_to_load.begin(), modules_to_load.end(), + [&entry](const std::filesystem::path& p) { + return p.filename() == entry.path().filename(); + }); + if (it != modules_to_load.end()) { + *it = entry.path(); + } else { + modules_to_load.push_back(entry.path()); + } + } + } + + for (const auto& module_path : modules_to_load) { + LOG_INFO(Loader, "Loading {}", fmt::UTF(module_path.u8string())); + linker->LoadModule(module_path); + } + #ifdef ENABLE_DISCORD_RPC // Discord RPC if (Config::getEnableDiscordRPC()) { diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 6eef1230..3cc12c11 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -122,11 +122,11 @@ public: if (selected == &openSfoViewer) { PSF psf; - QString game_update_path; - Common::FS::PathToQString(game_update_path, m_games[itemID].path.concat("-UPDATE")); std::filesystem::path game_folder_path = m_games[itemID].path; - if (std::filesystem::exists(Common::FS::PathFromQString(game_update_path))) { - game_folder_path = Common::FS::PathFromQString(game_update_path); + std::filesystem::path game_update_path = game_folder_path; + game_update_path += "UPDATE"; + if (std::filesystem::exists(game_update_path)) { + game_folder_path = game_update_path; } if (psf.Open(game_folder_path / "sce_sys" / "param.sfo")) { int rows = psf.GetEntries().size(); @@ -320,21 +320,17 @@ public: bool error = false; QString folder_path, game_update_path, dlc_path; Common::FS::PathToQString(folder_path, m_games[itemID].path); - Common::FS::PathToQString(game_update_path, m_games[itemID].path.concat("-UPDATE")); + game_update_path = folder_path + "-UPDATE"; Common::FS::PathToQString( dlc_path, Config::getAddonInstallDir() / Common::FS::PathFromQString(folder_path).parent_path().filename()); QString message_type = tr("Game"); if (selected == deleteUpdate) { - if (!Config::getSeparateUpdateEnabled()) { - QMessageBox::critical(nullptr, tr("Error"), - QString(tr("requiresEnableSeparateUpdateFolder_MSG"))); - error = true; - } else if (!std::filesystem::exists( - Common::FS::PathFromQString(game_update_path))) { - QMessageBox::critical(nullptr, tr("Error"), - QString(tr("This game has no update to delete!"))); + if (!std::filesystem::exists(Common::FS::PathFromQString(game_update_path))) { + QMessageBox::critical( + nullptr, tr("Error"), + QString(tr("This game has no separate update to delete!"))); error = true; } else { folder_path = game_update_path; diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 7ae58304..9eccec8e 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.