Separate Updates from Game Folder (#1026)

* separate updates implementation

* clang format

clang format

clang format

clang format

clang hates me

work please

* hotfix: check for sfo file instead of the folder

* tiny change

* refactor

* forgot to change this over

* add review changes

* use operator
This commit is contained in:
ElBread3 2024-10-15 10:49:42 -05:00 committed by GitHub
parent 29ad2eca62
commit a588bc5da8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 135 additions and 12 deletions

View File

@ -60,6 +60,7 @@ static bool vkMarkers = false;
static bool vkCrashDiagnostic = false;
static s16 cursorState = HideCursorState::Idle;
static int cursorHideTimeout = 5; // 5 seconds (default)
static bool separateupdatefolder = false;
// Gui
std::vector<std::filesystem::path> settings_install_dirs = {};
@ -207,6 +208,10 @@ bool vkCrashDiagnosticEnabled() {
return vkCrashDiagnostic;
}
bool getSeparateUpdateEnabled() {
return separateupdatefolder;
}
void setGpuId(s32 selectedGpuId) {
gpuId = selectedGpuId;
}
@ -319,6 +324,10 @@ void setSpecialPadClass(int type) {
specialPadClass = type;
}
void setSeparateUpdateEnabled(bool use) {
separateupdatefolder = use;
}
void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) {
main_window_geometry_x = x;
main_window_geometry_y = y;
@ -483,6 +492,7 @@ void load(const std::filesystem::path& path) {
}
isShowSplash = toml::find_or<bool>(general, "showSplash", true);
isAutoUpdate = toml::find_or<bool>(general, "autoUpdate", false);
separateupdatefolder = toml::find_or<bool>(general, "separateUpdateEnabled", false);
}
if (data.contains("Input")) {
@ -597,6 +607,7 @@ void save(const std::filesystem::path& path) {
data["General"]["updateChannel"] = updateChannel;
data["General"]["showSplash"] = isShowSplash;
data["General"]["autoUpdate"] = isAutoUpdate;
data["General"]["separateUpdateEnabled"] = separateupdatefolder;
data["Input"]["cursorState"] = cursorState;
data["Input"]["cursorHideTimeout"] = cursorHideTimeout;
data["Input"]["backButtonBehavior"] = backButtonBehavior;

View File

@ -19,6 +19,7 @@ bool isFullscreenMode();
bool getPlayBGM();
int getBGMvolume();
bool getEnableDiscordRPC();
bool getSeparateUpdateEnabled();
std::string getLogFilter();
std::string getLogType();
@ -62,6 +63,7 @@ void setLanguage(u32 language);
void setNeoMode(bool enable);
void setUserName(const std::string& type);
void setUpdateChannel(const std::string& type);
void setSeparateUpdateEnabled(bool use);
void setCursorState(s16 cursorState);
void setCursorHideTimeout(int newcursorHideTimeout);

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include "common/config.h"
#include "common/string_util.h"
#include "core/file_sys/fs.h"
@ -53,7 +54,14 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view guest_directory, b
// Remove device (e.g /app0) from path to retrieve relative path.
pos = mount->mount.size() + 1;
const auto rel_path = std::string_view(corrected_path).substr(pos);
const auto host_path = mount->host_path / rel_path;
std::filesystem::path host_path = mount->host_path / rel_path;
std::filesystem::path patch_path = mount->host_path;
patch_path += "-UPDATE";
if (corrected_path.starts_with("/app0/") && std::filesystem::exists(patch_path / rel_path)) {
host_path = patch_path / rel_path;
}
if (!NeedsCaseInsensitiveSearch) {
return host_path;
}

View File

@ -114,7 +114,10 @@ void Emulator::Run(const std::filesystem::path& file) {
std::string app_version;
u32 fw_version;
std::filesystem::path sce_sys_folder = file.parent_path() / "sce_sys";
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 sce_sys_folder =
use_game_patch ? game_patch_folder / "sce_sys" : file.parent_path() / "sce_sys";
if (std::filesystem::is_directory(sce_sys_folder)) {
for (const auto& entry : std::filesystem::directory_iterator(sce_sys_folder)) {
if (entry.path().filename() == "param.sfo") {

View File

@ -17,7 +17,7 @@ void GameInfoClass::GetGameInfo(QWidget* parent) {
QDir parentFolder(installDir);
QFileInfoList fileList = parentFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
for (const auto& fileInfo : fileList) {
if (fileInfo.isDir()) {
if (fileInfo.isDir() && !fileInfo.filePath().endsWith("-UPDATE")) {
filePaths.append(fileInfo.absoluteFilePath());
}
}

View File

@ -25,9 +25,15 @@ public:
static GameInfo readGameInfo(const std::filesystem::path& filePath) {
GameInfo game;
game.path = filePath;
std::filesystem::path sce_folder_path = filePath / "sce_sys" / "param.sfo";
std::filesystem::path game_update_path =
std::filesystem::path(filePath.string() + "-UPDATE");
if (std::filesystem::exists(game_update_path / "sce_sys" / "param.sfo")) {
sce_folder_path = game_update_path / "sce_sys" / "param.sfo";
}
PSF psf;
if (psf.Open(game.path / "sce_sys" / "param.sfo")) {
if (psf.Open(sce_folder_path)) {
game.icon_path = game.path / "sce_sys" / "icon0.png";
QString iconpath;
Common::FS::PathToQString(iconpath, game.icon_path);

View File

@ -68,6 +68,18 @@ public:
menu.addMenu(copyMenu);
// "Delete..." submenu.
QMenu* deleteMenu = new QMenu(tr("Delete..."), widget);
QAction* deleteGame = new QAction(tr("Delete Game"), widget);
QAction* deleteUpdate = new QAction(tr("Delete Update"), widget);
QAction* deleteDLC = new QAction(tr("Delete DLC"), widget);
deleteMenu->addAction(deleteGame);
deleteMenu->addAction(deleteUpdate);
deleteMenu->addAction(deleteDLC);
menu.addMenu(deleteMenu);
// Show menu.
auto selected = menu.exec(global_pos);
if (!selected) {
@ -82,7 +94,13 @@ public:
if (selected == &openSfoViewer) {
PSF psf;
if (psf.Open(std::filesystem::path(m_games[itemID].path) / "sce_sys" / "param.sfo")) {
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);
}
if (psf.Open(game_folder_path / "sce_sys" / "param.sfo")) {
int rows = psf.GetEntries().size();
QTableWidget* tableWidget = new QTableWidget(rows, 2);
tableWidget->setAttribute(Qt::WA_DeleteOnClose);
@ -269,6 +287,55 @@ public:
.arg(QString::fromStdString(m_games[itemID].size));
clipboard->setText(combinedText);
}
if (selected == deleteGame || selected == deleteUpdate || selected == deleteDLC) {
bool error = false;
QString folder_path, game_update_path;
Common::FS::PathToQString(folder_path, m_games[itemID].path);
Common::FS::PathToQString(game_update_path, m_games[itemID].path.concat("-UPDATE"));
QString message_type = tr("Game");
if (selected == deleteUpdate) {
if (!Config::getSeparateUpdateEnabled()) {
QMessageBox::critical(
nullptr, tr("Error"),
QString(tr("This feature requires the 'Enable Separate Update Folder' "
"config option "
"to work. If you want to use this feature, please enable it.")));
error = true;
} else if (!std::filesystem::exists(m_games[itemID].path.concat("-UPDATE"))) {
QMessageBox::critical(nullptr, tr("Error"),
QString(tr("This game has no update to delete!")));
error = true;
} else {
folder_path = game_update_path;
message_type = tr("Update");
}
} else if (selected == deleteDLC) {
std::filesystem::path addon_path =
Config::getAddonInstallDir() /
Common::FS::PathFromQString(folder_path).parent_path().filename();
if (!std::filesystem::exists(addon_path)) {
QMessageBox::critical(nullptr, tr("Error"),
QString(tr("This game has no DLC to delete!")));
error = true;
} else {
folder_path = QString::fromStdString(addon_path.string());
message_type = tr("DLC");
}
}
if (!error) {
QString gameName = QString::fromStdString(m_games[itemID].name);
QDir dir(folder_path);
QMessageBox::StandardButton reply = QMessageBox::question(
nullptr, QString(tr("Delete %1")).arg(message_type),
QString(tr("Are you sure you want to delete %1's %2 directory?"))
.arg(gameName, message_type),
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
dir.removeRecursively();
}
}
}
}
int GetRowIndex(QTreeWidget* treeWidget, QTreeWidgetItem* item) {

View File

@ -676,10 +676,17 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int
InstallDirSelect ids;
ids.exec();
auto game_install_dir = ids.getSelectedDirectory();
auto extract_path = game_install_dir / pkg.GetTitleID();
auto game_folder_path = game_install_dir / pkg.GetTitleID();
QString pkgType = QString::fromStdString(pkg.GetPkgFlags());
bool use_game_update = pkgType.contains("Patch") && Config::getSeparateUpdateEnabled();
auto game_update_path = use_game_update
? game_install_dir / (std::string(pkg.GetTitleID()) + "-UPDATE")
: game_folder_path;
if (!std::filesystem::exists(game_update_path)) {
std::filesystem::create_directory(game_update_path);
}
QString gameDirPath;
Common::FS::PathToQString(gameDirPath, extract_path);
Common::FS::PathToQString(gameDirPath, game_folder_path);
QDir game_dir(gameDirPath);
if (game_dir.exists()) {
QMessageBox msgBox;
@ -715,7 +722,11 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int
QMessageBox::critical(this, tr("PKG ERROR"), "PSF file there is no APP_VER");
return;
}
psf.Open(extract_path / "sce_sys" / "param.sfo");
std::filesystem::path sce_folder_path =
std::filesystem::exists(game_update_path / "sce_sys" / "param.sfo")
? game_update_path / "sce_sys" / "param.sfo"
: game_folder_path / "sce_sys" / "param.sfo";
psf.Open(sce_folder_path);
QString game_app_version;
if (auto app_ver = psf.GetString("APP_VER"); app_ver.has_value()) {
game_app_version = QString::fromStdString(std::string{*app_ver});
@ -764,7 +775,7 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int
addonMsgBox.setDefaultButton(QMessageBox::No);
int result = addonMsgBox.exec();
if (result == QMessageBox::Yes) {
extract_path = addon_extract_path;
game_update_path = addon_extract_path;
} else {
return;
}
@ -775,12 +786,14 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int
msgBox.setDefaultButton(QMessageBox::No);
int result = msgBox.exec();
if (result == QMessageBox::Yes) {
extract_path = addon_extract_path;
game_update_path = addon_extract_path;
} else {
return;
}
}
} else {
QString gameDirPath;
Common::FS::PathToQString(gameDirPath, game_folder_path);
msgBox.setText(QString(tr("Game already installed") + "\n" + gameDirPath + "\n" +
tr("Would you like to overwrite?")));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
@ -801,8 +814,7 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int
}
// what else?
}
if (!pkg.Extract(file, extract_path, failreason)) {
if (!pkg.Extract(file, game_update_path, failreason)) {
QMessageBox::critical(this, tr("PKG ERROR"), QString::fromStdString(failreason));
} else {
int nfiles = pkg.GetNumberOfFiles();

View File

@ -133,6 +133,9 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices, QWidge
connect(ui->ps4proCheckBox, &QCheckBox::stateChanged, this,
[](int val) { Config::setNeoMode(val); });
connect(ui->separateUpdatesCheckBox, &QCheckBox::stateChanged, this,
[](int val) { Config::setSeparateUpdateEnabled(val); });
connect(ui->logTypeComboBox, &QComboBox::currentTextChanged, this,
[](const QString& text) { Config::setLogType(text.toStdString()); });
@ -270,6 +273,7 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices, QWidge
ui->showSplashCheckBox->installEventFilter(this);
ui->ps4proCheckBox->installEventFilter(this);
ui->discordRPCCheckbox->installEventFilter(this);
ui->separateUpdatesCheckBox->installEventFilter(this);
ui->userName->installEventFilter(this);
ui->logTypeGroupBox->installEventFilter(this);
ui->logFilter->installEventFilter(this);
@ -328,6 +332,7 @@ void SettingsDialog::LoadValuesFromConfig() {
ui->logTypeComboBox->setCurrentText(QString::fromStdString(Config::getLogType()));
ui->logFilterLineEdit->setText(QString::fromStdString(Config::getLogFilter()));
ui->userNameLineEdit->setText(QString::fromStdString(Config::getUserName()));
ui->separateUpdatesCheckBox->setChecked(Config::getSeparateUpdateEnabled());
ui->debugDump->setChecked(Config::debugDump());
ui->vkValidationCheckBox->setChecked(Config::vkValidationEnabled());
@ -437,6 +442,8 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) {
text = tr("ps4proCheckBox");
} else if (elementName == "discordRPCCheckbox") {
text = tr("discordRPCCheckbox");
} else if (elementName == "separateUpdatesCheckBox") {
text = tr("separateUpdatesCheckBox");
} else if (elementName == "userName") {
text = tr("userName");
} else if (elementName == "logTypeGroupBox") {

View File

@ -134,6 +134,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="separateUpdatesCheckBox">
<property name="text">
<string>Enable Separate Update Folder</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="showSplashCheckBox">
<property name="text">