Update the install and progress dialogs

- Remove the overwrite files checkbox, it will always overwrite
- The progressbar now reflects the progress in terms of data transferred.
master
Morph 2020-07-05 09:29:39 +07:00
parent 7f4d96d873
commit 6d8d7ebc66
4 changed files with 65 additions and 80 deletions

@ -22,7 +22,7 @@ InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialo
item->setCheckState(Qt::Checked); item->setCheckState(Qt::Checked);
} }
file_list->setMinimumWidth((file_list->sizeHintForColumn(0) * 10) / 9); file_list->setMinimumWidth((file_list->sizeHintForColumn(0) * 11) / 10);
vbox_layout = new QVBoxLayout; vbox_layout = new QVBoxLayout;
@ -30,8 +30,8 @@ InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialo
description = new QLabel(tr("Please confirm these are the files you wish to install.")); description = new QLabel(tr("Please confirm these are the files you wish to install."));
overwrite_files = new QCheckBox(tr("Overwrite Existing Files")); update_description =
overwrite_files->setCheckState(Qt::Unchecked); new QLabel(tr("Installing an Update or DLC will overwrite the previously installed one."));
buttons = new QDialogButtonBox; buttons = new QDialogButtonBox;
buttons->addButton(QDialogButtonBox::Cancel); buttons->addButton(QDialogButtonBox::Cancel);
@ -40,10 +40,10 @@ InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialo
connect(buttons, &QDialogButtonBox::accepted, this, &InstallDialog::accept); connect(buttons, &QDialogButtonBox::accepted, this, &InstallDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &InstallDialog::reject); connect(buttons, &QDialogButtonBox::rejected, this, &InstallDialog::reject);
hbox_layout->addWidget(overwrite_files);
hbox_layout->addWidget(buttons); hbox_layout->addWidget(buttons);
vbox_layout->addWidget(description); vbox_layout->addWidget(description);
vbox_layout->addWidget(update_description);
vbox_layout->addWidget(file_list); vbox_layout->addWidget(file_list);
vbox_layout->addLayout(hbox_layout); vbox_layout->addLayout(hbox_layout);
@ -67,10 +67,6 @@ QStringList InstallDialog::GetFiles() const {
return files; return files;
} }
bool InstallDialog::ShouldOverwriteFiles() const {
return overwrite_files->isChecked();
}
int InstallDialog::GetMinimumWidth() const { int InstallDialog::GetMinimumWidth() const {
return file_list->width(); return file_list->width();
} }

@ -31,6 +31,6 @@ private:
QHBoxLayout* hbox_layout; QHBoxLayout* hbox_layout;
QLabel* description; QLabel* description;
QCheckBox* overwrite_files; QLabel* update_description;
QDialogButtonBox* buttons; QDialogButtonBox* buttons;
}; };

@ -848,6 +848,9 @@ void GMainWindow::ConnectWidgetEvents() {
connect(game_list, &GameList::OpenPerGameGeneralRequested, this, connect(game_list, &GameList::OpenPerGameGeneralRequested, this,
&GMainWindow::OnGameListOpenPerGameProperties); &GMainWindow::OnGameListOpenPerGameProperties);
connect(this, &GMainWindow::UpdateInstallProgress, this,
&GMainWindow::IncrementInstallProgress);
connect(this, &GMainWindow::EmulationStarting, render_window, connect(this, &GMainWindow::EmulationStarting, render_window,
&GRenderWindow::OnEmulationStarting); &GRenderWindow::OnEmulationStarting);
connect(this, &GMainWindow::EmulationStopping, render_window, connect(this, &GMainWindow::EmulationStopping, render_window,
@ -1594,6 +1597,10 @@ void GMainWindow::OnMenuLoadFolder() {
} }
} }
void GMainWindow::IncrementInstallProgress() {
install_progress->setValue(install_progress->value() + 1);
}
void GMainWindow::OnMenuInstallToNAND() { void GMainWindow::OnMenuInstallToNAND() {
const QString file_filter = const QString file_filter =
tr("Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive " tr("Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive "
@ -1613,28 +1620,35 @@ void GMainWindow::OnMenuInstallToNAND() {
} }
const QStringList files = installDialog.GetFiles(); const QStringList files = installDialog.GetFiles();
const bool overwrite_files = installDialog.ShouldOverwriteFiles();
int count = 0; int remaining = filenames.size();
const int total_count = filenames.size();
// This would only overflow above 2^43 bytes (8.796 TB)
int total_size = 0;
for (const QString& file : files) {
total_size += static_cast<int>(QFile(file).size() / 0x1000);
}
if (total_size < 0) {
LOG_CRITICAL(Frontend, "Attempting to install too many files, aborting.");
return;
}
QStringList new_files{}; // Newly installed files that do not yet exist in the NAND QStringList new_files{}; // Newly installed files that do not yet exist in the NAND
QStringList overwritten_files{}; // Files that overwrote those existing in the NAND QStringList overwritten_files{}; // Files that overwrote those existing in the NAND
QStringList existing_files{}; // Files that were not installed as they already exist in the NAND
QStringList failed_files{}; // Files that failed to install due to errors QStringList failed_files{}; // Files that failed to install due to errors
ui.action_Install_File_NAND->setEnabled(false); ui.action_Install_File_NAND->setEnabled(false);
QProgressDialog install_progress(QStringLiteral(""), tr("Cancel"), 0, total_count, this); install_progress = new QProgressDialog(QStringLiteral(""), tr("Cancel"), 0, total_size, this);
install_progress.setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint & install_progress->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint &
~Qt::WindowMaximizeButtonHint); ~Qt::WindowMaximizeButtonHint);
install_progress.setAutoClose(false); install_progress->setAttribute(Qt::WA_DeleteOnClose, true);
install_progress.setFixedWidth(installDialog.GetMinimumWidth()); install_progress->setFixedWidth(installDialog.GetMinimumWidth() + 40);
install_progress.show(); install_progress->show();
for (const QString& file : files) { for (const QString& file : files) {
install_progress.setWindowTitle(tr("%n file(s) remaining", "", total_count - count)); install_progress->setWindowTitle(tr("%n file(s) remaining", "", remaining));
install_progress.setLabelText( install_progress->setLabelText(
tr("Installing file \"%1\"...").arg(QFileInfo(file).fileName())); tr("Installing file \"%1\"...").arg(QFileInfo(file).fileName()));
QFuture<InstallResult> future; QFuture<InstallResult> future;
@ -1642,19 +1656,21 @@ void GMainWindow::OnMenuInstallToNAND() {
if (file.endsWith(QStringLiteral("xci"), Qt::CaseInsensitive) || if (file.endsWith(QStringLiteral("xci"), Qt::CaseInsensitive) ||
file.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { file.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) {
future = QtConcurrent::run([this, &file, &overwrite_files, &install_progress] {
return InstallNSPXCI(file, overwrite_files, install_progress); future = QtConcurrent::run([this, &file] { return InstallNSPXCI(file); });
});
while (!future.isFinished()) { while (!future.isFinished()) {
QCoreApplication::processEvents(); QCoreApplication::processEvents();
} }
result = future.result(); result = future.result();
} else { } else {
result = InstallNCA(file, overwrite_files, install_progress); result = InstallNCA(file);
} }
std::this_thread::sleep_for(std::chrono::milliseconds(10));
switch (result) { switch (result) {
case InstallResult::Success: case InstallResult::Success:
new_files.append(QFileInfo(file).fileName()); new_files.append(QFileInfo(file).fileName());
@ -1662,19 +1678,15 @@ void GMainWindow::OnMenuInstallToNAND() {
case InstallResult::Overwrite: case InstallResult::Overwrite:
overwritten_files.append(QFileInfo(file).fileName()); overwritten_files.append(QFileInfo(file).fileName());
break; break;
case InstallResult::AlreadyExists:
existing_files.append(QFileInfo(file).fileName());
break;
case InstallResult::Failure: case InstallResult::Failure:
failed_files.append(QFileInfo(file).fileName()); failed_files.append(QFileInfo(file).fileName());
break; break;
} }
install_progress.setValue(++count); --remaining;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
install_progress.close(); install_progress->close();
const QString install_results = const QString install_results =
(new_files.isEmpty() ? QStringLiteral("") (new_files.isEmpty() ? QStringLiteral("")
@ -1682,9 +1694,6 @@ void GMainWindow::OnMenuInstallToNAND() {
(overwritten_files.isEmpty() (overwritten_files.isEmpty()
? QStringLiteral("") ? QStringLiteral("")
: tr("%n file(s) were overwritten\n", "", overwritten_files.size())) + : tr("%n file(s) were overwritten\n", "", overwritten_files.size())) +
(existing_files.isEmpty()
? QStringLiteral("")
: tr("%n file(s) already exist in NAND\n", "", existing_files.size())) +
(failed_files.isEmpty() ? QStringLiteral("") (failed_files.isEmpty() ? QStringLiteral("")
: tr("%n file(s) failed to install\n", "", failed_files.size())); : tr("%n file(s) failed to install\n", "", failed_files.size()));
@ -1695,11 +1704,9 @@ void GMainWindow::OnMenuInstallToNAND() {
ui.action_Install_File_NAND->setEnabled(true); ui.action_Install_File_NAND->setEnabled(true);
} }
InstallResult GMainWindow::InstallNSPXCI(const QString& filename, bool overwrite_files, InstallResult GMainWindow::InstallNSPXCI(const QString& filename) {
QProgressDialog& install_progress) { const auto qt_raw_copy = [this](const FileSys::VirtualFile& src,
const auto qt_raw_copy = [this, &install_progress](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, std::size_t block_size) {
const FileSys::VirtualFile& dest,
std::size_t block_size) {
if (src == nullptr || dest == nullptr) { if (src == nullptr || dest == nullptr) {
return false; return false;
} }
@ -1710,11 +1717,13 @@ InstallResult GMainWindow::InstallNSPXCI(const QString& filename, bool overwrite
std::array<u8, 0x1000> buffer{}; std::array<u8, 0x1000> buffer{};
for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
if (install_progress.wasCanceled()) { if (install_progress->wasCanceled()) {
dest->Resize(0); dest->Resize(0);
return false; return false;
} }
emit UpdateInstallProgress();
const auto read = src->Read(buffer.data(), buffer.size(), i); const auto read = src->Read(buffer.data(), buffer.size(), i);
dest->Write(buffer.data(), read, i); dest->Write(buffer.data(), read, i);
} }
@ -1739,32 +1748,19 @@ InstallResult GMainWindow::InstallNSPXCI(const QString& filename, bool overwrite
} }
const auto res = const auto res =
Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->InstallEntry( Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->InstallEntry(
*nsp, false, qt_raw_copy); *nsp, true, qt_raw_copy);
if (res == FileSys::InstallResult::Success) { if (res == FileSys::InstallResult::Success) {
return InstallResult::Success; return InstallResult::Success;
} else if (res == FileSys::InstallResult::ErrorAlreadyExists) { } else if (res == FileSys::InstallResult::ErrorAlreadyExists) {
if (overwrite_files) {
const auto res2 = Core::System::GetInstance()
.GetFileSystemController()
.GetUserNANDContents()
->InstallEntry(*nsp, true, qt_raw_copy);
if (res2 != FileSys::InstallResult::Success) {
return InstallResult::Failure;
}
return InstallResult::Overwrite; return InstallResult::Overwrite;
} else {
return InstallResult::AlreadyExists;
}
} else { } else {
return InstallResult::Failure; return InstallResult::Failure;
} }
} }
InstallResult GMainWindow::InstallNCA(const QString& filename, bool overwrite_files, InstallResult GMainWindow::InstallNCA(const QString& filename) {
QProgressDialog& install_progress) { const auto qt_raw_copy = [this](const FileSys::VirtualFile& src,
const auto qt_raw_copy = [this, &install_progress](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, std::size_t block_size) {
const FileSys::VirtualFile& dest,
std::size_t block_size) {
if (src == nullptr || dest == nullptr) { if (src == nullptr || dest == nullptr) {
return false; return false;
} }
@ -1775,11 +1771,13 @@ InstallResult GMainWindow::InstallNCA(const QString& filename, bool overwrite_fi
std::array<u8, 0x1000> buffer{}; std::array<u8, 0x1000> buffer{};
for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
if (install_progress.wasCanceled()) { if (install_progress->wasCanceled()) {
dest->Resize(0); dest->Resize(0);
return false; return false;
} }
emit UpdateInstallProgress();
const auto read = src->Read(buffer.data(), buffer.size(), i); const auto read = src->Read(buffer.data(), buffer.size(), i);
dest->Write(buffer.data(), read, i); dest->Write(buffer.data(), read, i);
} }
@ -1830,30 +1828,18 @@ InstallResult GMainWindow::InstallNCA(const QString& filename, bool overwrite_fi
res = Core::System::GetInstance() res = Core::System::GetInstance()
.GetFileSystemController() .GetFileSystemController()
.GetUserNANDContents() .GetUserNANDContents()
->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy); ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy);
} else { } else {
res = Core::System::GetInstance() res = Core::System::GetInstance()
.GetFileSystemController() .GetFileSystemController()
.GetSystemNANDContents() .GetSystemNANDContents()
->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy); ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy);
} }
if (res == FileSys::InstallResult::Success) { if (res == FileSys::InstallResult::Success) {
return InstallResult::Success; return InstallResult::Success;
} else if (res == FileSys::InstallResult::ErrorAlreadyExists) { } else if (res == FileSys::InstallResult::ErrorAlreadyExists) {
if (overwrite_files) {
const auto res2 =
Core::System::GetInstance()
.GetFileSystemController()
.GetUserNANDContents()
->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy);
if (res2 != FileSys::InstallResult::Success) {
return InstallResult::Failure;
}
return InstallResult::Overwrite; return InstallResult::Overwrite;
} else {
return InstallResult::AlreadyExists;
}
} else { } else {
return InstallResult::Failure; return InstallResult::Failure;
} }

@ -51,7 +51,6 @@ enum class EmulatedDirectoryTarget {
enum class InstallResult { enum class InstallResult {
Success, Success,
Overwrite, Overwrite,
AlreadyExists,
Failure, Failure,
}; };
@ -110,6 +109,8 @@ signals:
// Signal that tells widgets to update icons to use the current theme // Signal that tells widgets to update icons to use the current theme
void UpdateThemedIcons(); void UpdateThemedIcons();
void UpdateInstallProgress();
void ErrorDisplayFinished(); void ErrorDisplayFinished();
void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid); void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid);
@ -206,6 +207,7 @@ private slots:
void OnGameListOpenPerGameProperties(const std::string& file); void OnGameListOpenPerGameProperties(const std::string& file);
void OnMenuLoadFile(); void OnMenuLoadFile();
void OnMenuLoadFolder(); void OnMenuLoadFolder();
void IncrementInstallProgress();
void OnMenuInstallToNAND(); void OnMenuInstallToNAND();
void OnMenuRecentFile(); void OnMenuRecentFile();
void OnConfigure(); void OnConfigure();
@ -226,10 +228,8 @@ private slots:
private: private:
std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
InstallResult InstallNSPXCI(const QString& filename, bool overwrite_files, InstallResult InstallNSPXCI(const QString& filename);
QProgressDialog& install_progress); InstallResult InstallNCA(const QString& filename);
InstallResult InstallNCA(const QString& filename, bool overwrite_files,
QProgressDialog& install_progress);
void UpdateWindowTitle(const std::string& title_name = {}, void UpdateWindowTitle(const std::string& title_name = {},
const std::string& title_version = {}); const std::string& title_version = {});
void UpdateStatusBar(); void UpdateStatusBar();
@ -284,6 +284,9 @@ private:
HotkeyRegistry hotkey_registry; HotkeyRegistry hotkey_registry;
// Install progress dialog
QProgressDialog* install_progress;
protected: protected:
void dropEvent(QDropEvent* event) override; void dropEvent(QDropEvent* event) override;
void dragEnterEvent(QDragEnterEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override;