mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-01-22 14:31:39 +00:00
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
This commit is contained in:
parent
c1d6a93246
commit
428b8989b8
|
@ -199,4 +199,14 @@ void HandleTable::CreateStdHandles() {
|
||||||
setup("/dev/stderr", new Devices::Logger("stderr", true)); // stderr
|
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
|
} // namespace Core::FileSys
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <tsl/robin_map.h>
|
#include <tsl/robin_map.h>
|
||||||
#include "common/io_file.h"
|
#include "common/io_file.h"
|
||||||
|
#include "common/logging/formatter.h"
|
||||||
#include "core/devices/base_device.h"
|
#include "core/devices/base_device.h"
|
||||||
|
|
||||||
namespace Core::FileSys {
|
namespace Core::FileSys {
|
||||||
|
@ -37,6 +38,14 @@ public:
|
||||||
std::filesystem::path GetHostPath(std::string_view guest_directory,
|
std::filesystem::path GetHostPath(std::string_view guest_directory,
|
||||||
bool* is_read_only = nullptr);
|
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) {
|
const MntPair* GetMount(const std::string& guest_path) {
|
||||||
std::scoped_lock lock{m_mutex};
|
std::scoped_lock lock{m_mutex};
|
||||||
const auto it = std::ranges::find_if(m_mnt_pairs, [&](const auto& mount) {
|
const auto it = std::ranges::find_if(m_mnt_pairs, [&](const auto& mount) {
|
||||||
|
@ -86,6 +95,7 @@ public:
|
||||||
void DeleteHandle(int d);
|
void DeleteHandle(int d);
|
||||||
File* GetFile(int d);
|
File* GetFile(int d);
|
||||||
File* GetFile(const std::filesystem::path& host_name);
|
File* GetFile(const std::filesystem::path& host_name);
|
||||||
|
int GetFileDescriptor(File* file);
|
||||||
|
|
||||||
void CreateStdHandles();
|
void CreateStdHandles();
|
||||||
|
|
||||||
|
|
|
@ -695,12 +695,66 @@ static int GetDents(int fd, char* buf, int nbytes, s64* basep) {
|
||||||
return sizeof(OrbisKernelDirent);
|
return sizeof(OrbisKernelDirent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int HandleSeparateUpdateDents(int fd, char* buf, int nbytes, s64* basep) {
|
||||||
|
int dir_entries = 0;
|
||||||
|
|
||||||
|
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||||
|
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::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) {
|
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) {
|
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) {
|
s64 PS4_SYSV_ABI sceKernelPwrite(int d, void* buf, size_t nbytes, s64 offset) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <set>
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
|
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
|
@ -106,9 +107,11 @@ Emulator::~Emulator() {
|
||||||
void Emulator::Run(const std::filesystem::path& file) {
|
void Emulator::Run(const std::filesystem::path& file) {
|
||||||
|
|
||||||
// Use the eboot from the separated updates folder if it's there
|
// Use the eboot from the separated updates folder if it's there
|
||||||
std::filesystem::path game_patch_folder = file.parent_path().concat("-UPDATE");
|
std::filesystem::path game_patch_folder = file.parent_path();
|
||||||
bool use_game_patch = std::filesystem::exists(game_patch_folder / "sce_sys");
|
game_patch_folder += "-UPDATE";
|
||||||
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.
|
// Applications expect to be run from /app0 so mount the file's parent path as app0.
|
||||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||||
|
@ -226,19 +229,36 @@ void Emulator::Run(const std::filesystem::path& file) {
|
||||||
LoadSystemModules(eboot_path, game_info.game_serial);
|
LoadSystemModules(eboot_path, game_info.game_serial);
|
||||||
|
|
||||||
// Load all prx from game's sce_module folder
|
// Load all prx from game's sce_module folder
|
||||||
std::filesystem::path sce_module_folder = file.parent_path() / "sce_module";
|
std::vector<std::filesystem::path> modules_to_load;
|
||||||
if (std::filesystem::is_directory(sce_module_folder)) {
|
std::filesystem::path game_module_folder = file.parent_path() / "sce_module";
|
||||||
for (const auto& entry : std::filesystem::directory_iterator(sce_module_folder)) {
|
if (std::filesystem::is_directory(game_module_folder)) {
|
||||||
std::filesystem::path module_path = entry.path();
|
for (const auto& entry : std::filesystem::directory_iterator(game_module_folder)) {
|
||||||
std::filesystem::path update_module_path =
|
if (entry.is_regular_file()) {
|
||||||
eboot_path.parent_path() / "sce_module" / entry.path().filename();
|
modules_to_load.push_back(entry.path());
|
||||||
if (std::filesystem::exists(update_module_path) && use_game_patch) {
|
|
||||||
module_path = update_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()));
|
LOG_INFO(Loader, "Loading {}", fmt::UTF(module_path.u8string()));
|
||||||
linker->LoadModule(module_path);
|
linker->LoadModule(module_path);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ENABLE_DISCORD_RPC
|
#ifdef ENABLE_DISCORD_RPC
|
||||||
// Discord RPC
|
// Discord RPC
|
||||||
|
|
|
@ -122,11 +122,11 @@ public:
|
||||||
|
|
||||||
if (selected == &openSfoViewer) {
|
if (selected == &openSfoViewer) {
|
||||||
PSF psf;
|
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;
|
std::filesystem::path game_folder_path = m_games[itemID].path;
|
||||||
if (std::filesystem::exists(Common::FS::PathFromQString(game_update_path))) {
|
std::filesystem::path game_update_path = game_folder_path;
|
||||||
game_folder_path = Common::FS::PathFromQString(game_update_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")) {
|
if (psf.Open(game_folder_path / "sce_sys" / "param.sfo")) {
|
||||||
int rows = psf.GetEntries().size();
|
int rows = psf.GetEntries().size();
|
||||||
|
@ -320,21 +320,17 @@ public:
|
||||||
bool error = false;
|
bool error = false;
|
||||||
QString folder_path, game_update_path, dlc_path;
|
QString folder_path, game_update_path, dlc_path;
|
||||||
Common::FS::PathToQString(folder_path, m_games[itemID].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(
|
Common::FS::PathToQString(
|
||||||
dlc_path, Config::getAddonInstallDir() /
|
dlc_path, Config::getAddonInstallDir() /
|
||||||
Common::FS::PathFromQString(folder_path).parent_path().filename());
|
Common::FS::PathFromQString(folder_path).parent_path().filename());
|
||||||
QString message_type = tr("Game");
|
QString message_type = tr("Game");
|
||||||
|
|
||||||
if (selected == deleteUpdate) {
|
if (selected == deleteUpdate) {
|
||||||
if (!Config::getSeparateUpdateEnabled()) {
|
if (!std::filesystem::exists(Common::FS::PathFromQString(game_update_path))) {
|
||||||
QMessageBox::critical(nullptr, tr("Error"),
|
QMessageBox::critical(
|
||||||
QString(tr("requiresEnableSeparateUpdateFolder_MSG")));
|
nullptr, tr("Error"),
|
||||||
error = true;
|
QString(tr("This game has no separate update to delete!")));
|
||||||
} 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!")));
|
|
||||||
error = true;
|
error = true;
|
||||||
} else {
|
} else {
|
||||||
folder_path = game_update_path;
|
folder_path = game_update_path;
|
||||||
|
|
|
@ -1159,7 +1159,7 @@
|
||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.cpp" line="293"/>
|
<location filename="../settings_dialog.cpp" line="293"/>
|
||||||
<source>separateUpdatesCheckBox</source>
|
<source>separateUpdatesCheckBox</source>
|
||||||
<translation>Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.</translation>
|
<translation>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.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.cpp" line="295"/>
|
<location filename="../settings_dialog.cpp" line="295"/>
|
||||||
|
|
Loading…
Reference in a new issue