mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-12-16 04:09:07 +00:00
Check available and required size when installing a package (#17829)
Show and check available and required size when installing a package. Installation is not allowed, by disabling `Yes` and/or `Install` button, if not enough disk space is detected. On multi installation dialog box, installation size for each package is reported in the list. fixes #14440 </br> </br> ### Single PKG installation - Not enough space available  </br> </br> ### Single PKG installation - Required space available  </br> </br> ### Multi PKG installation - Not enough space available  </br> </br> ### Multi PKG installation - Required space available  --------- Co-authored-by: Megamouse <studienricky89@googlemail.com>
This commit is contained in:
parent
3f6529fecb
commit
0f1d516d9a
@ -347,6 +347,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool is_valid() const { return m_is_valid; }
|
bool is_valid() const { return m_is_valid; }
|
||||||
|
const PKGHeader& get_header() const { return m_header; }
|
||||||
package_install_result check_target_app_version() const;
|
package_install_result check_target_app_version() const;
|
||||||
static package_install_result extract_data(std::deque<package_reader>& readers, std::deque<std::string>& bootable_paths);
|
static package_install_result extract_data(std::deque<package_reader>& readers, std::deque<std::string>& bootable_paths);
|
||||||
const psf::registry& get_psf() const { return m_psf; }
|
const psf::registry& get_psf() const { return m_psf; }
|
||||||
|
|||||||
@ -184,6 +184,11 @@ namespace rpcs3::utils
|
|||||||
return g_cfg_vfs.get(g_cfg_vfs.dev_bdvd, get_emu_dir());
|
return g_cfg_vfs.get(g_cfg_vfs.dev_bdvd, get_emu_dir());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string get_hdd0_game_dir()
|
||||||
|
{
|
||||||
|
return get_hdd0_dir() + "game/";
|
||||||
|
}
|
||||||
|
|
||||||
u64 get_cache_disk_usage()
|
u64 get_cache_disk_usage()
|
||||||
{
|
{
|
||||||
if (const u64 data_size = fs::get_dir_size(rpcs3::utils::get_cache_dir(), 1); data_size != umax)
|
if (const u64 data_size = fs::get_dir_size(rpcs3::utils::get_cache_dir(), 1); data_size != umax)
|
||||||
|
|||||||
@ -34,6 +34,8 @@ namespace rpcs3::utils
|
|||||||
std::string get_flash3_dir();
|
std::string get_flash3_dir();
|
||||||
std::string get_bdvd_dir();
|
std::string get_bdvd_dir();
|
||||||
|
|
||||||
|
std::string get_hdd0_game_dir();
|
||||||
|
|
||||||
// Cache directories and disk usage
|
// Cache directories and disk usage
|
||||||
u64 get_cache_disk_usage();
|
u64 get_cache_disk_usage();
|
||||||
std::string get_cache_dir();
|
std::string get_cache_dir();
|
||||||
|
|||||||
@ -272,6 +272,10 @@ compat::package_info game_compatibility::GetPkgInfo(const QString& pkg_path, gam
|
|||||||
info.category = QString::fromStdString(std::string(psf::get_string(psf, "CATEGORY")));
|
info.category = QString::fromStdString(std::string(psf::get_string(psf, "CATEGORY")));
|
||||||
info.version = QString::fromStdString(std::string(psf::get_string(psf, "APP_VER")));
|
info.version = QString::fromStdString(std::string(psf::get_string(psf, "APP_VER")));
|
||||||
|
|
||||||
|
// Technically, there is no specific package's header info providing its installation size on disk.
|
||||||
|
// We use "data_size" header as an approximation (a bit larger) for this purpose
|
||||||
|
info.data_size = reader.get_header().data_size.value();
|
||||||
|
|
||||||
if (!info.category.isEmpty())
|
if (!info.category.isEmpty())
|
||||||
{
|
{
|
||||||
const Localized localized;
|
const Localized localized;
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "util/types.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
@ -108,6 +110,7 @@ namespace compat
|
|||||||
QString version; // May be empty
|
QString version; // May be empty
|
||||||
QString category; // HG, DG, GD etc.
|
QString category; // HG, DG, GD etc.
|
||||||
QString local_cat; // Localized category
|
QString local_cat; // Localized category
|
||||||
|
u64 data_size = 0; // Installation size
|
||||||
|
|
||||||
package_type type = package_type::other; // The type of package (Update, DLC or other)
|
package_type type = package_type::other; // The type of package (Update, DLC or other)
|
||||||
};
|
};
|
||||||
|
|||||||
@ -860,11 +860,28 @@ bool main_window::InstallPackages(QStringList file_paths, bool from_boot)
|
|||||||
info.changelog = tr("Changelog:\n%0", "Block for Changelog").arg(info.changelog);
|
info.changelog = tr("Changelog:\n%0", "Block for Changelog").arg(info.changelog);
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString info_string = QStringLiteral("%0\n\n%1%2%3%4").arg(file_info.fileName()).arg(info.title).arg(info.local_cat).arg(info.title_id).arg(info.version);
|
u64 free_space = 0;
|
||||||
QString message = tr("Do you want to install this package?\n\n%0").arg(info_string);
|
|
||||||
|
|
||||||
QMessageBox mb(QMessageBox::Icon::Question, tr("PKG Decrypter / Installer"), message, QMessageBox::Yes | QMessageBox::No, this);
|
// Retrieve disk space info on data path's drive
|
||||||
|
if (fs::device_stat stat{}; fs::statfs(rpcs3::utils::get_hdd0_dir(), stat))
|
||||||
|
{
|
||||||
|
free_space = stat.avail_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString installation_info =
|
||||||
|
tr("Installation path: %0\nAvailable disk space: %1%2\nRequired disk space: %3")
|
||||||
|
.arg(rpcs3::utils::get_hdd0_game_dir())
|
||||||
|
.arg(gui::utils::format_byte_size(free_space))
|
||||||
|
.arg(info.data_size <= free_space ? QString() : tr(" - <b>NOT ENOUGH SPACE</b>"))
|
||||||
|
.arg(gui::utils::format_byte_size(info.data_size));
|
||||||
|
|
||||||
|
const QString info_string = QStringLiteral("%0\n\n%1%2%3%4").arg(file_info.fileName()).arg(info.title).arg(info.local_cat).arg(info.title_id).arg(info.version);
|
||||||
|
QString message = tr("Do you want to install this package?\n\n%0\n\n%1").arg(info_string).arg(installation_info);
|
||||||
|
|
||||||
|
QMessageBox mb(QMessageBox::Icon::Question, tr("PKG Decrypter / Installer"), gui::utils::make_paragraph(message), QMessageBox::Yes | QMessageBox::No, this);
|
||||||
mb.setDefaultButton(QMessageBox::No);
|
mb.setDefaultButton(QMessageBox::No);
|
||||||
|
mb.setTextFormat(Qt::RichText); // Support HTML tags
|
||||||
|
mb.button(QMessageBox::Yes)->setEnabled(info.data_size <= free_space);
|
||||||
|
|
||||||
if (!info.changelog.isEmpty())
|
if (!info.changelog.isEmpty())
|
||||||
{
|
{
|
||||||
|
|||||||
@ -2,6 +2,10 @@
|
|||||||
#include "game_compatibility.h"
|
#include "game_compatibility.h"
|
||||||
#include "numbered_widget_item.h"
|
#include "numbered_widget_item.h"
|
||||||
#include "richtext_item_delegate.h"
|
#include "richtext_item_delegate.h"
|
||||||
|
#include "qt_utils.h"
|
||||||
|
|
||||||
|
#include "Emu/system_utils.hpp"
|
||||||
|
#include "Utilities/File.h"
|
||||||
|
|
||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
@ -17,6 +21,7 @@ enum Roles
|
|||||||
TitleRole = Qt::UserRole + 2,
|
TitleRole = Qt::UserRole + 2,
|
||||||
TitleIdRole = Qt::UserRole + 3,
|
TitleIdRole = Qt::UserRole + 3,
|
||||||
VersionRole = Qt::UserRole + 4,
|
VersionRole = Qt::UserRole + 4,
|
||||||
|
DataSizeRole = Qt::UserRole + 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
pkg_install_dialog::pkg_install_dialog(const QStringList& paths, game_compatibility* compat, QWidget* parent)
|
pkg_install_dialog::pkg_install_dialog(const QStringList& paths, game_compatibility* compat, QWidget* parent)
|
||||||
@ -89,7 +94,8 @@ pkg_install_dialog::pkg_install_dialog(const QStringList& paths, game_compatibil
|
|||||||
append_comma();
|
append_comma();
|
||||||
accumulated_info += file_info.fileName();
|
accumulated_info += file_info.fileName();
|
||||||
|
|
||||||
const QString text = tr("<b>%0</b> (%2)", "Package text").arg(info.title.simplified()).arg(accumulated_info);
|
const QString text = tr("<b>%0</b> (%1) - %2", "Package text").arg(info.title.simplified())
|
||||||
|
.arg(accumulated_info).arg(gui::utils::format_byte_size(info.data_size));
|
||||||
|
|
||||||
QListWidgetItem* item = new numbered_widget_item(text, m_dir_list);
|
QListWidgetItem* item = new numbered_widget_item(text, m_dir_list);
|
||||||
item->setData(Roles::FullPathRole, info.path);
|
item->setData(Roles::FullPathRole, info.path);
|
||||||
@ -97,6 +103,7 @@ pkg_install_dialog::pkg_install_dialog(const QStringList& paths, game_compatibil
|
|||||||
item->setData(Roles::TitleRole, info.title);
|
item->setData(Roles::TitleRole, info.title);
|
||||||
item->setData(Roles::TitleIdRole, info.title_id);
|
item->setData(Roles::TitleIdRole, info.title_id);
|
||||||
item->setData(Roles::VersionRole, info.version);
|
item->setData(Roles::VersionRole, info.version);
|
||||||
|
item->setData(Roles::DataSizeRole, static_cast<qulonglong>(info.data_size));
|
||||||
item->setToolTip(tooltip);
|
item->setToolTip(tooltip);
|
||||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||||
item->setCheckState(Qt::Checked);
|
item->setCheckState(Qt::Checked);
|
||||||
@ -106,6 +113,10 @@ pkg_install_dialog::pkg_install_dialog(const QStringList& paths, game_compatibil
|
|||||||
m_dir_list->setCurrentRow(0);
|
m_dir_list->setCurrentRow(0);
|
||||||
m_dir_list->setMinimumWidth((m_dir_list->sizeHintForColumn(0) * 125) / 100);
|
m_dir_list->setMinimumWidth((m_dir_list->sizeHintForColumn(0) * 125) / 100);
|
||||||
|
|
||||||
|
// Create contextual label (updated in connect(m_dir_list, &QListWidget::itemChanged ...))
|
||||||
|
QLabel* installation_info = new QLabel();
|
||||||
|
installation_info->setTextFormat(Qt::RichText); // Support HTML tags
|
||||||
|
|
||||||
// Create buttons
|
// Create buttons
|
||||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
|
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
|
||||||
buttons->button(QDialogButtonBox::Ok)->setText(tr("Install"));
|
buttons->button(QDialogButtonBox::Ok)->setText(tr("Install"));
|
||||||
@ -123,19 +134,9 @@ pkg_install_dialog::pkg_install_dialog(const QStringList& paths, game_compatibil
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(m_dir_list, &QListWidget::itemChanged, this, [this, buttons](QListWidgetItem*)
|
connect(m_dir_list, &QListWidget::itemChanged, this, [this, installation_info, buttons](QListWidgetItem*)
|
||||||
{
|
{
|
||||||
bool any_checked = false;
|
UpdateInfo(installation_info, buttons);
|
||||||
for (int i = 0; i < m_dir_list->count(); i++)
|
|
||||||
{
|
|
||||||
if (m_dir_list->item(i)->checkState() == Qt::Checked)
|
|
||||||
{
|
|
||||||
any_checked = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buttons->button(QDialogButtonBox::Ok)->setEnabled(any_checked);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
QToolButton* move_up = new QToolButton;
|
QToolButton* move_up = new QToolButton;
|
||||||
@ -159,11 +160,41 @@ pkg_install_dialog::pkg_install_dialog(const QStringList& paths, game_compatibil
|
|||||||
vbox->addWidget(description);
|
vbox->addWidget(description);
|
||||||
vbox->addLayout(hbox);
|
vbox->addLayout(hbox);
|
||||||
vbox->addWidget(m_dir_list);
|
vbox->addWidget(m_dir_list);
|
||||||
|
vbox->addWidget(installation_info);
|
||||||
vbox->addWidget(buttons);
|
vbox->addWidget(buttons);
|
||||||
|
|
||||||
setLayout(vbox);
|
setLayout(vbox);
|
||||||
setWindowTitle(tr("Batch PKG Installation"));
|
setWindowTitle(tr("Batch PKG Installation"));
|
||||||
setObjectName("pkg_install_dialog");
|
setObjectName("pkg_install_dialog");
|
||||||
|
UpdateInfo(installation_info, buttons); // Just to show and check available and required size
|
||||||
|
}
|
||||||
|
|
||||||
|
void pkg_install_dialog::UpdateInfo(QLabel* installation_info, QDialogButtonBox* buttons) const
|
||||||
|
{
|
||||||
|
u64 data_size = 0;
|
||||||
|
u64 free_space = 0;
|
||||||
|
|
||||||
|
// Retrieve disk space info on data path's drive
|
||||||
|
if (fs::device_stat stat{}; fs::statfs(rpcs3::utils::get_hdd0_game_dir(), stat))
|
||||||
|
{
|
||||||
|
free_space = stat.avail_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < m_dir_list->count(); i++)
|
||||||
|
{
|
||||||
|
if (m_dir_list->item(i)->checkState() == Qt::Checked)
|
||||||
|
{
|
||||||
|
data_size += m_dir_list->item(i)->data(Roles::DataSizeRole).toULongLong();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
installation_info->setText(gui::utils::make_paragraph(
|
||||||
|
tr("Installation path: %0\nAvailable disk space: %1%2\nRequired disk space: %3")
|
||||||
|
.arg(rpcs3::utils::get_hdd0_game_dir())
|
||||||
|
.arg(gui::utils::format_byte_size(free_space))
|
||||||
|
.arg(data_size <= free_space ? QString() : tr(" - <b>NOT ENOUGH SPACE</b>"))
|
||||||
|
.arg(gui::utils::format_byte_size(data_size))));
|
||||||
|
buttons->button(QDialogButtonBox::Ok)->setEnabled(data_size && (data_size <= free_space));
|
||||||
}
|
}
|
||||||
|
|
||||||
void pkg_install_dialog::MoveItem(int offset) const
|
void pkg_install_dialog::MoveItem(int offset) const
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QListWidget>
|
#include <QListWidget>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
|
||||||
namespace compat
|
namespace compat
|
||||||
{
|
{
|
||||||
@ -19,6 +21,7 @@ public:
|
|||||||
std::vector<compat::package_info> GetPathsToInstall() const;
|
std::vector<compat::package_info> GetPathsToInstall() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void UpdateInfo(QLabel* installation_info, QDialogButtonBox* buttons) const;
|
||||||
void MoveItem(int offset) const;
|
void MoveItem(int offset) const;
|
||||||
|
|
||||||
QListWidget* m_dir_list;
|
QListWidget* m_dir_list;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user