From 56e8ed7833553466398d077637dc88d19f65d080 Mon Sep 17 00:00:00 2001 From: ElBread3 <92335081+ElBread3@users.noreply.github.com> Date: Thu, 10 Oct 2024 02:28:59 -0500 Subject: [PATCH] Multiple Install Folders (#1308) * multiple install folders implimentation * clang format * paths setting tab * clang format --- CMakeLists.txt | 2 + src/common/config.cpp | 33 ++++++++++--- src/common/config.h | 4 +- src/qt_gui/game_info.cpp | 16 ++++--- src/qt_gui/game_install_dialog.cpp | 8 +++- src/qt_gui/install_dir_select.cpp | 76 ++++++++++++++++++++++++++++++ src/qt_gui/install_dir_select.h | 31 ++++++++++++ src/qt_gui/main.cpp | 2 +- src/qt_gui/main_window.cpp | 8 +++- src/qt_gui/settings_dialog.cpp | 49 +++++++++++++++++++ src/qt_gui/settings_dialog.ui | 70 +++++++++++++++++++++++++++ 11 files changed, 278 insertions(+), 21 deletions(-) create mode 100644 src/qt_gui/install_dir_select.cpp create mode 100644 src/qt_gui/install_dir_select.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2db263b3..eeca274e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -693,6 +693,8 @@ set(QT_GUI src/qt_gui/about_dialog.cpp src/qt_gui/game_grid_frame.h src/qt_gui/game_install_dialog.cpp src/qt_gui/game_install_dialog.h + src/qt_gui/install_dir_select.cpp + src/qt_gui/install_dir_select.h src/qt_gui/pkg_viewer.cpp src/qt_gui/pkg_viewer.h src/qt_gui/trophy_viewer.cpp diff --git a/src/common/config.cpp b/src/common/config.cpp index 40be5ebe..55cf2942 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -62,7 +62,7 @@ static s16 cursorState = HideCursorState::Idle; static int cursorHideTimeout = 5; // 5 seconds (default) // Gui -std::filesystem::path settings_install_dir = {}; +std::vector settings_install_dirs = {}; std::filesystem::path settings_addon_install_dir = {}; u32 main_window_geometry_x = 400; u32 main_window_geometry_y = 400; @@ -325,8 +325,9 @@ void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) { main_window_geometry_w = w; main_window_geometry_h = h; } -void setGameInstallDir(const std::filesystem::path& dir) { - settings_install_dir = dir; +void setGameInstallDirs(const std::vector& dir) { + settings_install_dirs.resize(dir.size()); + settings_install_dirs = dir; } void setAddonInstallDir(const std::filesystem::path& dir) { settings_addon_install_dir = dir; @@ -384,8 +385,8 @@ u32 getMainWindowGeometryW() { u32 getMainWindowGeometryH() { return main_window_geometry_h; } -std::filesystem::path getGameInstallDir() { - return settings_install_dir; +std::vector getGameInstallDirs() { + return settings_install_dirs; } std::filesystem::path getAddonInstallDir() { if (settings_addon_install_dir.empty()) { @@ -523,7 +524,19 @@ void load(const std::filesystem::path& path) { mw_themes = toml::find_or(gui, "theme", 0); m_window_size_W = toml::find_or(gui, "mw_width", 0); m_window_size_H = toml::find_or(gui, "mw_height", 0); - settings_install_dir = toml::find_fs_path_or(gui, "installDir", {}); + + auto old_game_install_dir = toml::find_fs_path_or(gui, "installDir", {}); + if (!old_game_install_dir.empty()) { + settings_install_dirs.push_back(old_game_install_dir); + data.as_table().erase("installDir"); + } + + const auto install_dir_array = + toml::find_or>(gui, "installDirs", {}); + for (const auto& dir : install_dir_array) { + settings_install_dirs.emplace_back(std::filesystem::path{dir}); + } + settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {}); main_window_geometry_x = toml::find_or(gui, "geometry_x", 0); main_window_geometry_y = toml::find_or(gui, "geometry_y", 0); @@ -601,7 +614,13 @@ void save(const std::filesystem::path& path) { data["GUI"]["gameTableMode"] = m_table_mode; data["GUI"]["mw_width"] = m_window_size_W; data["GUI"]["mw_height"] = m_window_size_H; - data["GUI"]["installDir"] = std::string{fmt::UTF(settings_install_dir.u8string()).data}; + + std::vector install_dirs; + for (const auto& dirString : settings_install_dirs) { + install_dirs.emplace_back(std::string{fmt::UTF(dirString.u8string()).data}); + } + data["GUI"]["installDirs"] = install_dirs; + data["GUI"]["addonInstallDir"] = std::string{fmt::UTF(settings_addon_install_dir.u8string()).data}; data["GUI"]["geometry_x"] = main_window_geometry_x; diff --git a/src/common/config.h b/src/common/config.h index 90ebdb58..e76f389c 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -85,7 +85,7 @@ bool vkCrashDiagnosticEnabled(); // Gui void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h); -void setGameInstallDir(const std::filesystem::path& dir); +void setGameInstallDirs(const std::vector& dir); void setAddonInstallDir(const std::filesystem::path& dir); void setMainWindowTheme(u32 theme); void setIconSize(u32 size); @@ -104,7 +104,7 @@ u32 getMainWindowGeometryX(); u32 getMainWindowGeometryY(); u32 getMainWindowGeometryW(); u32 getMainWindowGeometryH(); -std::filesystem::path getGameInstallDir(); +std::vector getGameInstallDirs(); std::filesystem::path getAddonInstallDir(); u32 getMainWindowTheme(); u32 getIconSize(); diff --git a/src/qt_gui/game_info.cpp b/src/qt_gui/game_info.cpp index 6e8d8971..d82f43f2 100644 --- a/src/qt_gui/game_info.cpp +++ b/src/qt_gui/game_info.cpp @@ -10,14 +10,16 @@ GameInfoClass::GameInfoClass() = default; GameInfoClass::~GameInfoClass() = default; void GameInfoClass::GetGameInfo(QWidget* parent) { - QString installDir; - Common::FS::PathToQString(installDir, Config::getGameInstallDir()); QStringList filePaths; - QDir parentFolder(installDir); - QFileInfoList fileList = parentFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); - for (const auto& fileInfo : fileList) { - if (fileInfo.isDir()) { - filePaths.append(fileInfo.absoluteFilePath()); + for (const auto& installLoc : Config::getGameInstallDirs()) { + QString installDir; + Common::FS::PathToQString(installDir, installLoc); + QDir parentFolder(installDir); + QFileInfoList fileList = parentFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + for (const auto& fileInfo : fileList) { + if (fileInfo.isDir()) { + filePaths.append(fileInfo.absoluteFilePath()); + } } } m_games = QtConcurrent::mapped(filePaths, [&](const QString& path) { diff --git a/src/qt_gui/game_install_dialog.cpp b/src/qt_gui/game_install_dialog.cpp index 11daf2de..4418d9a4 100644 --- a/src/qt_gui/game_install_dialog.cpp +++ b/src/qt_gui/game_install_dialog.cpp @@ -51,7 +51,9 @@ QWidget* GameInstallDialog::SetupGamesDirectory() { // Input. m_gamesDirectory = new QLineEdit(); QString install_dir; - Common::FS::PathToQString(install_dir, Config::getGameInstallDir()); + std::filesystem::path install_path = + Config::getGameInstallDirs().empty() ? "" : Config::getGameInstallDirs().front(); + Common::FS::PathToQString(install_dir, install_path); m_gamesDirectory->setText(install_dir); m_gamesDirectory->setMinimumWidth(400); @@ -125,7 +127,9 @@ void GameInstallDialog::Save() { } } - Config::setGameInstallDir(Common::FS::PathFromQString(gamesDirectory)); + std::vector install_dirs; + install_dirs.emplace_back(Common::FS::PathFromQString(gamesDirectory)); + Config::setGameInstallDirs(install_dirs); Config::setAddonInstallDir(Common::FS::PathFromQString(addonsDirectory)); const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); Config::save(config_dir / "config.toml"); diff --git a/src/qt_gui/install_dir_select.cpp b/src/qt_gui/install_dir_select.cpp new file mode 100644 index 00000000..e0951b12 --- /dev/null +++ b/src/qt_gui/install_dir_select.cpp @@ -0,0 +1,76 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "install_dir_select.h" + +InstallDirSelect::InstallDirSelect() : selected_dir() { + selected_dir = Config::getGameInstallDirs().empty() ? "" : Config::getGameInstallDirs().front(); + + if (!Config::getGameInstallDirs().empty() && Config::getGameInstallDirs().size() == 1) { + reject(); + } + + auto layout = new QVBoxLayout(this); + + layout->addWidget(SetupInstallDirList()); + layout->addStretch(); + layout->addWidget(SetupDialogActions()); + + setWindowTitle(tr("shadPS4 - Choose directory")); + setWindowIcon(QIcon(":images/shadps4.ico")); +} + +InstallDirSelect::~InstallDirSelect() {} + +QWidget* InstallDirSelect::SetupInstallDirList() { + auto group = new QGroupBox(tr("Select which directory you want to install to.")); + auto vlayout = new QVBoxLayout(); + + auto m_path_list = new QListWidget(); + QList qt_list; + for (const auto& str : Config::getGameInstallDirs()) { + QString installDirPath; + Common::FS::PathToQString(installDirPath, str); + qt_list.append(installDirPath); + } + m_path_list->insertItems(0, qt_list); + m_path_list->setSpacing(1); + + connect(m_path_list, &QListWidget::itemClicked, this, &InstallDirSelect::setSelectedDirectory); + connect(m_path_list, &QListWidget::itemActivated, this, + &InstallDirSelect::setSelectedDirectory); + + vlayout->addWidget(m_path_list); + + group->setLayout(vlayout); + return group; +} + +void InstallDirSelect::setSelectedDirectory(QListWidgetItem* item) { + if (item) { + const auto highlighted_path = Common::FS::PathFromQString(item->text()); + if (!highlighted_path.empty()) { + selected_dir = highlighted_path; + } + } +} + +QWidget* InstallDirSelect::SetupDialogActions() { + auto actions = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + connect(actions, &QDialogButtonBox::accepted, this, &InstallDirSelect::accept); + connect(actions, &QDialogButtonBox::rejected, this, &InstallDirSelect::reject); + + return actions; +} diff --git a/src/qt_gui/install_dir_select.h b/src/qt_gui/install_dir_select.h new file mode 100644 index 00000000..fdadf2fe --- /dev/null +++ b/src/qt_gui/install_dir_select.h @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "common/config.h" +#include "common/path_util.h" + +class QLineEdit; + +class InstallDirSelect final : public QDialog { +public: + InstallDirSelect(); + ~InstallDirSelect(); + + std::filesystem::path getSelectedDirectory() { + return selected_dir; + } + +private slots: + void BrowseGamesDirectory(); + +private: + QWidget* SetupInstallDirList(); + QWidget* SetupDialogActions(); + void setSelectedDirectory(QListWidgetItem* item); + std::filesystem::path selected_dir; +}; diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index 8c565a19..da8804f6 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -30,7 +30,7 @@ int main(int argc, char* argv[]) { bool has_command_line_argument = argc > 1; // Check if the game install directory is set - if (Config::getGameInstallDir().empty() && !has_command_line_argument) { + if (Config::getGameInstallDirs().empty() && !has_command_line_argument) { GameInstallDialog dlg; dlg.exec(); } diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 2323214e..025749dd 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -16,6 +16,7 @@ #include "core/file_format/pkg.h" #include "core/loader.h" #include "game_install_dialog.h" +#include "install_dir_select.h" #include "main_window.h" #include "settings_dialog.h" #include "video_core/renderer_vulkan/vk_instance.h" @@ -672,7 +673,10 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int QMessageBox::critical(this, tr("PKG ERROR"), QString::fromStdString(failreason)); return; } - auto extract_path = Config::getGameInstallDir() / pkg.GetTitleID(); + InstallDirSelect ids; + ids.exec(); + auto game_install_dir = ids.getSelectedDirectory(); + auto extract_path = game_install_dir / pkg.GetTitleID(); QString pkgType = QString::fromStdString(pkg.GetPkgFlags()); QString gameDirPath; Common::FS::PathToQString(gameDirPath, extract_path); @@ -821,7 +825,7 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int connect(&futureWatcher, &QFutureWatcher::finished, this, [=, this]() { if (pkgNum == nPkg) { QString path; - Common::FS::PathToQString(path, Config::getGameInstallDir()); + Common::FS::PathToQString(path, game_install_dir); QMessageBox extractMsgBox(this); extractMsgBox.setWindowTitle(tr("Extraction Finished")); extractMsgBox.setText( diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index efc43845..a6fe6a26 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -220,6 +220,55 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge [](int val) { Config::setNullGpu(val); }); } + // PATH TAB + { + for (const auto& dir : Config::getGameInstallDirs()) { + QString path_string; + Common::FS::PathToQString(path_string, dir); + QListWidgetItem* item = new QListWidgetItem(path_string); + ui->gameFoldersListWidget->addItem(item); + } + + ui->removeFolderButton->setEnabled(false); + + connect(ui->addFolderButton, &QPushButton::clicked, this, [this]() { + QString file_path_string = + QFileDialog::getExistingDirectory(this, tr("Directory to install games")); + auto file_path = Common::FS::PathFromQString(file_path_string); + if (!file_path.empty()) { + std::vector install_dirs = Config::getGameInstallDirs(); + install_dirs.push_back(file_path); + Config::setGameInstallDirs(install_dirs); + QListWidgetItem* item = new QListWidgetItem(file_path_string); + ui->gameFoldersListWidget->addItem(item); + } + }); + + connect(ui->gameFoldersListWidget, &QListWidget::itemSelectionChanged, this, [this]() { + ui->removeFolderButton->setEnabled( + !ui->gameFoldersListWidget->selectedItems().isEmpty()); + }); + + connect(ui->removeFolderButton, &QPushButton::clicked, this, [this]() { + QListWidgetItem* selected_item = ui->gameFoldersListWidget->currentItem(); + QString item_path_string = selected_item ? selected_item->text() : QString(); + if (!item_path_string.isEmpty()) { + auto file_path = Common::FS::PathFromQString(item_path_string); + std::vector install_dirs = Config::getGameInstallDirs(); + + auto iterator = std::remove_if( + install_dirs.begin(), install_dirs.end(), + [&file_path](const std::filesystem::path& dir) { return file_path == dir; }); + + if (iterator != install_dirs.end()) { + install_dirs.erase(iterator, install_dirs.end()); + delete selected_item; + } + Config::setGameInstallDirs(install_dirs); + } + }); + } + // DEBUG TAB { connect(ui->debugDump, &QCheckBox::stateChanged, this, diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 9637c5fe..e1c064d5 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -918,6 +918,76 @@ + + + Paths + + + + + + + + Game Folders + + + + + 0 + 20 + 401 + 331 + + + + + + + 100 + 360 + 80 + 24 + + + + Add... + + + + + + 210 + 360 + 80 + 24 + + + + Remove + + + + + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::Preferred + + + + 40 + 20 + + + + + + + + Debug