diff options
| -rw-r--r-- | src/yuzu/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | src/yuzu/install_dialog.cpp | 72 | ||||
| -rw-r--r-- | src/yuzu/install_dialog.h | 35 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 295 | ||||
| -rw-r--r-- | src/yuzu/main.h | 4 | ||||
| -rw-r--r-- | src/yuzu/main.ui | 2 |
6 files changed, 290 insertions, 124 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 742b72856..f430525f8 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -98,11 +98,13 @@ add_executable(yuzu | |||
| 98 | game_list_p.h | 98 | game_list_p.h |
| 99 | game_list_worker.cpp | 99 | game_list_worker.cpp |
| 100 | game_list_worker.h | 100 | game_list_worker.h |
| 101 | hotkeys.cpp | ||
| 102 | hotkeys.h | ||
| 103 | install_dialog.cpp | ||
| 104 | install_dialog.h | ||
| 101 | loading_screen.cpp | 105 | loading_screen.cpp |
| 102 | loading_screen.h | 106 | loading_screen.h |
| 103 | loading_screen.ui | 107 | loading_screen.ui |
| 104 | hotkeys.cpp | ||
| 105 | hotkeys.h | ||
| 106 | main.cpp | 108 | main.cpp |
| 107 | main.h | 109 | main.h |
| 108 | main.ui | 110 | main.ui |
diff --git a/src/yuzu/install_dialog.cpp b/src/yuzu/install_dialog.cpp new file mode 100644 index 000000000..fac158c25 --- /dev/null +++ b/src/yuzu/install_dialog.cpp | |||
| @@ -0,0 +1,72 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <QCheckBox> | ||
| 6 | #include <QDialogButtonBox> | ||
| 7 | #include <QFileInfo> | ||
| 8 | #include <QHBoxLayout> | ||
| 9 | #include <QLabel> | ||
| 10 | #include <QListWidget> | ||
| 11 | #include <QVBoxLayout> | ||
| 12 | #include "yuzu/install_dialog.h" | ||
| 13 | #include "yuzu/uisettings.h" | ||
| 14 | |||
| 15 | InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialog(parent) { | ||
| 16 | file_list = new QListWidget(this); | ||
| 17 | |||
| 18 | for (const QString& file : files) { | ||
| 19 | QListWidgetItem* item = new QListWidgetItem(QFileInfo(file).fileName(), file_list); | ||
| 20 | item->setData(Qt::UserRole, file); | ||
| 21 | item->setFlags(item->flags() | Qt::ItemIsUserCheckable); | ||
| 22 | item->setCheckState(Qt::Checked); | ||
| 23 | } | ||
| 24 | |||
| 25 | file_list->setMinimumWidth((file_list->sizeHintForColumn(0) * 6) / 5); | ||
| 26 | |||
| 27 | vbox_layout = new QVBoxLayout; | ||
| 28 | |||
| 29 | hbox_layout = new QHBoxLayout; | ||
| 30 | |||
| 31 | description = new QLabel(tr("Please confirm these are the files you wish to install.")); | ||
| 32 | |||
| 33 | overwrite_files = new QCheckBox(tr("Overwrite Existing Files")); | ||
| 34 | overwrite_files->setCheckState(Qt::Unchecked); | ||
| 35 | |||
| 36 | buttons = new QDialogButtonBox; | ||
| 37 | buttons->addButton(QDialogButtonBox::Cancel); | ||
| 38 | buttons->addButton(tr("Install"), QDialogButtonBox::AcceptRole); | ||
| 39 | |||
| 40 | connect(buttons, &QDialogButtonBox::accepted, this, &InstallDialog::accept); | ||
| 41 | connect(buttons, &QDialogButtonBox::rejected, this, &InstallDialog::reject); | ||
| 42 | |||
| 43 | hbox_layout->addWidget(overwrite_files); | ||
| 44 | hbox_layout->addWidget(buttons); | ||
| 45 | |||
| 46 | vbox_layout->addWidget(description); | ||
| 47 | vbox_layout->addWidget(file_list); | ||
| 48 | vbox_layout->addLayout(hbox_layout); | ||
| 49 | |||
| 50 | setLayout(vbox_layout); | ||
| 51 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); | ||
| 52 | setWindowTitle(tr("Install Files to NAND")); | ||
| 53 | } | ||
| 54 | |||
| 55 | InstallDialog::~InstallDialog() = default; | ||
| 56 | |||
| 57 | QStringList InstallDialog::GetFilenames() const { | ||
| 58 | QStringList filenames; | ||
| 59 | |||
| 60 | for (int i = 0; i < file_list->count(); ++i) { | ||
| 61 | const QListWidgetItem* item = file_list->item(i); | ||
| 62 | if (item->checkState() == Qt::Checked) { | ||
| 63 | filenames.append(item->data(Qt::UserRole).toString()); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | return filenames; | ||
| 68 | } | ||
| 69 | |||
| 70 | bool InstallDialog::ShouldOverwriteFiles() const { | ||
| 71 | return overwrite_files->isChecked(); | ||
| 72 | } \ No newline at end of file | ||
diff --git a/src/yuzu/install_dialog.h b/src/yuzu/install_dialog.h new file mode 100644 index 000000000..3eaa9e60a --- /dev/null +++ b/src/yuzu/install_dialog.h | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <QDialog> | ||
| 8 | |||
| 9 | class QCheckBox; | ||
| 10 | class QDialogButtonBox; | ||
| 11 | class QHBoxLayout; | ||
| 12 | class QLabel; | ||
| 13 | class QListWidget; | ||
| 14 | class QVBoxLayout; | ||
| 15 | |||
| 16 | class InstallDialog : public QDialog { | ||
| 17 | Q_OBJECT | ||
| 18 | |||
| 19 | public: | ||
| 20 | explicit InstallDialog(QWidget* parent, const QStringList& files); | ||
| 21 | ~InstallDialog() override; | ||
| 22 | |||
| 23 | QStringList GetFilenames() const; | ||
| 24 | bool ShouldOverwriteFiles() const; | ||
| 25 | |||
| 26 | private: | ||
| 27 | QListWidget* file_list; | ||
| 28 | |||
| 29 | QVBoxLayout* vbox_layout; | ||
| 30 | QHBoxLayout* hbox_layout; | ||
| 31 | |||
| 32 | QLabel* description; | ||
| 33 | QCheckBox* overwrite_files; | ||
| 34 | QDialogButtonBox* buttons; | ||
| 35 | }; \ No newline at end of file | ||
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 4d501a8f9..45ddc3baf 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -107,6 +107,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 107 | #include "yuzu/game_list.h" | 107 | #include "yuzu/game_list.h" |
| 108 | #include "yuzu/game_list_p.h" | 108 | #include "yuzu/game_list_p.h" |
| 109 | #include "yuzu/hotkeys.h" | 109 | #include "yuzu/hotkeys.h" |
| 110 | #include "yuzu/install_dialog.h" | ||
| 110 | #include "yuzu/loading_screen.h" | 111 | #include "yuzu/loading_screen.h" |
| 111 | #include "yuzu/main.h" | 112 | #include "yuzu/main.h" |
| 112 | #include "yuzu/uisettings.h" | 113 | #include "yuzu/uisettings.h" |
| @@ -1596,38 +1597,67 @@ void GMainWindow::OnMenuLoadFolder() { | |||
| 1596 | void GMainWindow::OnMenuInstallToNAND() { | 1597 | void GMainWindow::OnMenuInstallToNAND() { |
| 1597 | const QString file_filter = | 1598 | const QString file_filter = |
| 1598 | tr("Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive " | 1599 | tr("Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive " |
| 1599 | "(*.nca);;Nintendo Submissions Package (*.nsp);;NX Cartridge " | 1600 | "(*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge " |
| 1600 | "Image (*.xci)"); | 1601 | "Image (*.xci)"); |
| 1601 | QString filename = QFileDialog::getOpenFileName(this, tr("Install File"), | 1602 | QStringList files = QFileDialog::getOpenFileNames(this, tr("Install Files"), |
| 1602 | UISettings::values.roms_path, file_filter); | 1603 | UISettings::values.roms_path, file_filter); |
| 1603 | 1604 | ||
| 1604 | if (filename.isEmpty()) { | 1605 | if (files.isEmpty()) { |
| 1606 | return; | ||
| 1607 | } | ||
| 1608 | |||
| 1609 | InstallDialog installDialog(this, files); | ||
| 1610 | if (installDialog.exec() == QDialog::Rejected) { | ||
| 1605 | return; | 1611 | return; |
| 1606 | } | 1612 | } |
| 1607 | 1613 | ||
| 1608 | const auto qt_raw_copy = [this](const FileSys::VirtualFile& src, | 1614 | const QStringList filenames = installDialog.GetFilenames(); |
| 1609 | const FileSys::VirtualFile& dest, std::size_t block_size) { | 1615 | const bool overwrite_files = installDialog.ShouldOverwriteFiles(); |
| 1610 | if (src == nullptr || dest == nullptr) | 1616 | |
| 1617 | int count = 0; | ||
| 1618 | int total_count = filenames.size(); | ||
| 1619 | bool is_progressdialog_created = false; | ||
| 1620 | |||
| 1621 | const auto qt_raw_copy = [this, &count, &total_count, &is_progressdialog_created]( | ||
| 1622 | const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, | ||
| 1623 | std::size_t block_size) { | ||
| 1624 | if (src == nullptr || dest == nullptr) { | ||
| 1611 | return false; | 1625 | return false; |
| 1612 | if (!dest->Resize(src->GetSize())) | 1626 | } |
| 1627 | if (!dest->Resize(src->GetSize())) { | ||
| 1613 | return false; | 1628 | return false; |
| 1629 | } | ||
| 1614 | 1630 | ||
| 1615 | std::array<u8, 0x1000> buffer{}; | 1631 | std::array<u8, 0x1000> buffer{}; |
| 1616 | const int progress_maximum = static_cast<int>(src->GetSize() / buffer.size()); | 1632 | const int progress_maximum = static_cast<int>(src->GetSize() / buffer.size()); |
| 1617 | 1633 | ||
| 1618 | QProgressDialog progress( | 1634 | if (!is_progressdialog_created) { |
| 1619 | tr("Installing file \"%1\"...").arg(QString::fromStdString(src->GetName())), | 1635 | ui.action_Install_File_NAND->setEnabled(false); |
| 1620 | tr("Cancel"), 0, progress_maximum, this); | 1636 | install_progress = new QProgressDialog( |
| 1621 | progress.setWindowModality(Qt::WindowModal); | 1637 | tr("Installing file \"%1\"...").arg(QString::fromStdString(src->GetName())), |
| 1638 | tr("Cancel"), 0, progress_maximum, this); | ||
| 1639 | install_progress->setWindowTitle( | ||
| 1640 | tr("%n file(s) remaining", "", total_count - count - 1)); | ||
| 1641 | install_progress->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint & | ||
| 1642 | ~Qt::WindowMaximizeButtonHint); | ||
| 1643 | install_progress->setAutoClose(false); | ||
| 1644 | is_progressdialog_created = true; | ||
| 1645 | } else { | ||
| 1646 | install_progress->setWindowTitle( | ||
| 1647 | tr("%n file(s) remaining", "", total_count - count - 1)); | ||
| 1648 | install_progress->setLabelText( | ||
| 1649 | tr("Installing file \"%1\"...").arg(QString::fromStdString(src->GetName()))); | ||
| 1650 | install_progress->setMaximum(progress_maximum); | ||
| 1651 | } | ||
| 1622 | 1652 | ||
| 1623 | for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { | 1653 | for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { |
| 1624 | if (progress.wasCanceled()) { | 1654 | if (install_progress->wasCanceled()) { |
| 1625 | dest->Resize(0); | 1655 | dest->Resize(0); |
| 1626 | return false; | 1656 | return false; |
| 1627 | } | 1657 | } |
| 1628 | 1658 | ||
| 1629 | const int progress_value = static_cast<int>(i / buffer.size()); | 1659 | const int progress_value = static_cast<int>(i / buffer.size()); |
| 1630 | progress.setValue(progress_value); | 1660 | install_progress->setValue(progress_value); |
| 1631 | 1661 | ||
| 1632 | const auto read = src->Read(buffer.data(), buffer.size(), i); | 1662 | const auto read = src->Read(buffer.data(), buffer.size(), i); |
| 1633 | dest->Write(buffer.data(), read, i); | 1663 | dest->Write(buffer.data(), read, i); |
| @@ -1636,143 +1666,166 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 1636 | return true; | 1666 | return true; |
| 1637 | }; | 1667 | }; |
| 1638 | 1668 | ||
| 1639 | const auto success = [this]() { | 1669 | const auto success = [this, &count, &is_progressdialog_created]() { |
| 1670 | if (is_progressdialog_created) { | ||
| 1671 | install_progress->close(); | ||
| 1672 | } | ||
| 1640 | QMessageBox::information(this, tr("Successfully Installed"), | 1673 | QMessageBox::information(this, tr("Successfully Installed"), |
| 1641 | tr("The file was successfully installed.")); | 1674 | tr("%n file(s) successfully installed", "", count)); |
| 1642 | game_list->PopulateAsync(UISettings::values.game_dirs); | 1675 | game_list->PopulateAsync(UISettings::values.game_dirs); |
| 1643 | FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + | 1676 | FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + |
| 1644 | DIR_SEP + "game_list"); | 1677 | DIR_SEP + "game_list"); |
| 1678 | ui.action_Install_File_NAND->setEnabled(true); | ||
| 1645 | }; | 1679 | }; |
| 1646 | 1680 | ||
| 1647 | const auto failed = [this]() { | 1681 | const auto failed = [this, &is_progressdialog_created](const QString& file) { |
| 1682 | if (is_progressdialog_created) { | ||
| 1683 | install_progress->close(); | ||
| 1684 | } | ||
| 1648 | QMessageBox::warning( | 1685 | QMessageBox::warning( |
| 1649 | this, tr("Failed to Install"), | 1686 | this, tr("Failed to Install %1").arg(QFileInfo(file).fileName()), |
| 1650 | tr("There was an error while attempting to install the provided file. It " | 1687 | tr("There was an error while attempting to install the provided file. It " |
| 1651 | "could have an incorrect format or be missing metadata. Please " | 1688 | "could have an incorrect format or be missing metadata. Please " |
| 1652 | "double-check your file and try again.")); | 1689 | "double-check your file and try again.")); |
| 1690 | game_list->PopulateAsync(UISettings::values.game_dirs); | ||
| 1691 | ui.action_Install_File_NAND->setEnabled(true); | ||
| 1653 | }; | 1692 | }; |
| 1654 | 1693 | ||
| 1655 | const auto overwrite = [this]() { | 1694 | const auto overwrite = [this](const QString& file) { |
| 1656 | return QMessageBox::question(this, tr("Failed to Install"), | 1695 | return QMessageBox::question( |
| 1657 | tr("The file you are attempting to install already exists " | 1696 | this, tr("Failed to Install %1").arg(QFileInfo(file).fileName()), |
| 1658 | "in the cache. Would you like to overwrite it?")) == | 1697 | tr("The file you are attempting to install already exists " |
| 1659 | QMessageBox::Yes; | 1698 | "in the cache. Would you like to overwrite it?")) == QMessageBox::Yes; |
| 1660 | }; | 1699 | }; |
| 1661 | 1700 | ||
| 1662 | if (filename.endsWith(QStringLiteral("xci"), Qt::CaseInsensitive) || | 1701 | for (const QString& filename : filenames) { |
| 1663 | filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { | 1702 | if (filename.endsWith(QStringLiteral("xci"), Qt::CaseInsensitive) || |
| 1664 | std::shared_ptr<FileSys::NSP> nsp; | 1703 | filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { |
| 1665 | if (filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { | 1704 | std::shared_ptr<FileSys::NSP> nsp; |
| 1666 | nsp = std::make_shared<FileSys::NSP>( | 1705 | if (filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { |
| 1667 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); | 1706 | nsp = std::make_shared<FileSys::NSP>( |
| 1668 | if (nsp->IsExtractedType()) | 1707 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); |
| 1669 | failed(); | 1708 | if (nsp->IsExtractedType()) { |
| 1670 | } else { | 1709 | failed(filename); |
| 1671 | const auto xci = std::make_shared<FileSys::XCI>( | 1710 | break; |
| 1672 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); | 1711 | } |
| 1673 | nsp = xci->GetSecurePartitionNSP(); | 1712 | } else { |
| 1674 | } | 1713 | const auto xci = std::make_shared<FileSys::XCI>( |
| 1714 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); | ||
| 1715 | nsp = xci->GetSecurePartitionNSP(); | ||
| 1716 | } | ||
| 1675 | 1717 | ||
| 1676 | if (nsp->GetStatus() != Loader::ResultStatus::Success) { | 1718 | if (nsp->GetStatus() != Loader::ResultStatus::Success) { |
| 1677 | failed(); | 1719 | failed(filename); |
| 1678 | return; | 1720 | break; |
| 1679 | } | 1721 | } |
| 1680 | const auto res = Core::System::GetInstance() | 1722 | const auto res = Core::System::GetInstance() |
| 1681 | .GetFileSystemController() | 1723 | .GetFileSystemController() |
| 1682 | .GetUserNANDContents() | 1724 | .GetUserNANDContents() |
| 1683 | ->InstallEntry(*nsp, false, qt_raw_copy); | 1725 | ->InstallEntry(*nsp, false, qt_raw_copy); |
| 1684 | if (res == FileSys::InstallResult::Success) { | 1726 | if (res == FileSys::InstallResult::Success) { |
| 1685 | success(); | 1727 | ++count; |
| 1686 | } else { | 1728 | } else if (res == FileSys::InstallResult::ErrorAlreadyExists) { |
| 1687 | if (res == FileSys::InstallResult::ErrorAlreadyExists) { | 1729 | if (overwrite_files && overwrite(filename)) { |
| 1688 | if (overwrite()) { | ||
| 1689 | const auto res2 = Core::System::GetInstance() | 1730 | const auto res2 = Core::System::GetInstance() |
| 1690 | .GetFileSystemController() | 1731 | .GetFileSystemController() |
| 1691 | .GetUserNANDContents() | 1732 | .GetUserNANDContents() |
| 1692 | ->InstallEntry(*nsp, true, qt_raw_copy); | 1733 | ->InstallEntry(*nsp, true, qt_raw_copy); |
| 1693 | if (res2 == FileSys::InstallResult::Success) { | 1734 | if (res2 != FileSys::InstallResult::Success) { |
| 1694 | success(); | 1735 | failed(filename); |
| 1695 | } else { | 1736 | break; |
| 1696 | failed(); | ||
| 1697 | } | 1737 | } |
| 1738 | ++count; | ||
| 1739 | } else { | ||
| 1740 | --total_count; | ||
| 1698 | } | 1741 | } |
| 1699 | } else { | 1742 | } else { |
| 1700 | failed(); | 1743 | failed(filename); |
| 1744 | break; | ||
| 1701 | } | 1745 | } |
| 1702 | } | 1746 | } else { |
| 1703 | } else { | 1747 | const auto nca = std::make_shared<FileSys::NCA>( |
| 1704 | const auto nca = std::make_shared<FileSys::NCA>( | 1748 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); |
| 1705 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); | 1749 | const auto id = nca->GetStatus(); |
| 1706 | const auto id = nca->GetStatus(); | ||
| 1707 | |||
| 1708 | // Game updates necessary are missing base RomFS | ||
| 1709 | if (id != Loader::ResultStatus::Success && | ||
| 1710 | id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { | ||
| 1711 | failed(); | ||
| 1712 | return; | ||
| 1713 | } | ||
| 1714 | 1750 | ||
| 1715 | const QStringList tt_options{tr("System Application"), | 1751 | // Game updates necessary are missing base RomFS |
| 1716 | tr("System Archive"), | 1752 | if (id != Loader::ResultStatus::Success && |
| 1717 | tr("System Application Update"), | 1753 | id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { |
| 1718 | tr("Firmware Package (Type A)"), | 1754 | failed(filename); |
| 1719 | tr("Firmware Package (Type B)"), | 1755 | break; |
| 1720 | tr("Game"), | 1756 | } |
| 1721 | tr("Game Update"), | ||
| 1722 | tr("Game DLC"), | ||
| 1723 | tr("Delta Title")}; | ||
| 1724 | bool ok; | ||
| 1725 | const auto item = QInputDialog::getItem( | ||
| 1726 | this, tr("Select NCA Install Type..."), | ||
| 1727 | tr("Please select the type of title you would like to install this NCA as:\n(In " | ||
| 1728 | "most instances, the default 'Game' is fine.)"), | ||
| 1729 | tt_options, 5, false, &ok); | ||
| 1730 | |||
| 1731 | auto index = tt_options.indexOf(item); | ||
| 1732 | if (!ok || index == -1) { | ||
| 1733 | QMessageBox::warning(this, tr("Failed to Install"), | ||
| 1734 | tr("The title type you selected for the NCA is invalid.")); | ||
| 1735 | return; | ||
| 1736 | } | ||
| 1737 | 1757 | ||
| 1738 | // If index is equal to or past Game, add the jump in TitleType. | 1758 | const QStringList tt_options{tr("System Application"), |
| 1739 | if (index >= 5) { | 1759 | tr("System Archive"), |
| 1740 | index += static_cast<size_t>(FileSys::TitleType::Application) - | 1760 | tr("System Application Update"), |
| 1741 | static_cast<size_t>(FileSys::TitleType::FirmwarePackageB); | 1761 | tr("Firmware Package (Type A)"), |
| 1742 | } | 1762 | tr("Firmware Package (Type B)"), |
| 1763 | tr("Game"), | ||
| 1764 | tr("Game Update"), | ||
| 1765 | tr("Game DLC"), | ||
| 1766 | tr("Delta Title")}; | ||
| 1767 | bool ok; | ||
| 1768 | const auto item = QInputDialog::getItem( | ||
| 1769 | this, tr("Select NCA Install Type..."), | ||
| 1770 | tr("Please select the type of title you would like to install this NCA as:\n(In " | ||
| 1771 | "most instances, the default 'Game' is fine.)"), | ||
| 1772 | tt_options, 5, false, &ok); | ||
| 1773 | |||
| 1774 | auto index = tt_options.indexOf(item); | ||
| 1775 | if (!ok || index == -1) { | ||
| 1776 | QMessageBox::warning(this, tr("Failed to Install"), | ||
| 1777 | tr("The title type you selected for the NCA is invalid.")); | ||
| 1778 | break; | ||
| 1779 | } | ||
| 1743 | 1780 | ||
| 1744 | FileSys::InstallResult res; | 1781 | // If index is equal to or past Game, add the jump in TitleType. |
| 1745 | if (index >= static_cast<s32>(FileSys::TitleType::Application)) { | 1782 | if (index >= 5) { |
| 1746 | res = Core::System::GetInstance() | 1783 | index += static_cast<size_t>(FileSys::TitleType::Application) - |
| 1747 | .GetFileSystemController() | 1784 | static_cast<size_t>(FileSys::TitleType::FirmwarePackageB); |
| 1748 | .GetUserNANDContents() | 1785 | } |
| 1749 | ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false, | ||
| 1750 | qt_raw_copy); | ||
| 1751 | } else { | ||
| 1752 | res = Core::System::GetInstance() | ||
| 1753 | .GetFileSystemController() | ||
| 1754 | .GetSystemNANDContents() | ||
| 1755 | ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false, | ||
| 1756 | qt_raw_copy); | ||
| 1757 | } | ||
| 1758 | 1786 | ||
| 1759 | if (res == FileSys::InstallResult::Success) { | 1787 | FileSys::InstallResult res; |
| 1760 | success(); | 1788 | if (index >= static_cast<s32>(FileSys::TitleType::Application)) { |
| 1761 | } else if (res == FileSys::InstallResult::ErrorAlreadyExists) { | 1789 | res = Core::System::GetInstance() |
| 1762 | if (overwrite()) { | 1790 | .GetFileSystemController() |
| 1763 | const auto res2 = Core::System::GetInstance() | 1791 | .GetUserNANDContents() |
| 1764 | .GetFileSystemController() | 1792 | ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false, |
| 1765 | .GetUserNANDContents() | 1793 | qt_raw_copy); |
| 1766 | ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), | 1794 | } else { |
| 1767 | true, qt_raw_copy); | 1795 | res = Core::System::GetInstance() |
| 1768 | if (res2 == FileSys::InstallResult::Success) { | 1796 | .GetFileSystemController() |
| 1769 | success(); | 1797 | .GetSystemNANDContents() |
| 1798 | ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false, | ||
| 1799 | qt_raw_copy); | ||
| 1800 | } | ||
| 1801 | |||
| 1802 | if (res == FileSys::InstallResult::Success) { | ||
| 1803 | ++count; | ||
| 1804 | } else if (res == FileSys::InstallResult::ErrorAlreadyExists) { | ||
| 1805 | if (overwrite_files && overwrite(filename)) { | ||
| 1806 | const auto res2 = | ||
| 1807 | Core::System::GetInstance() | ||
| 1808 | .GetFileSystemController() | ||
| 1809 | .GetUserNANDContents() | ||
| 1810 | ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), true, | ||
| 1811 | qt_raw_copy); | ||
| 1812 | if (res2 != FileSys::InstallResult::Success) { | ||
| 1813 | failed(filename); | ||
| 1814 | break; | ||
| 1815 | } | ||
| 1816 | ++count; | ||
| 1770 | } else { | 1817 | } else { |
| 1771 | failed(); | 1818 | --total_count; |
| 1772 | } | 1819 | } |
| 1820 | } else { | ||
| 1821 | failed(filename); | ||
| 1822 | break; | ||
| 1773 | } | 1823 | } |
| 1774 | } else { | 1824 | } |
| 1775 | failed(); | 1825 | |
| 1826 | // Return success only on the last file | ||
| 1827 | if (filename == filenames.last()) { | ||
| 1828 | success(); | ||
| 1776 | } | 1829 | } |
| 1777 | } | 1830 | } |
| 1778 | } | 1831 | } |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 8e3d39c38..55d072e96 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -28,6 +28,7 @@ class MicroProfileDialog; | |||
| 28 | class ProfilerWidget; | 28 | class ProfilerWidget; |
| 29 | class QLabel; | 29 | class QLabel; |
| 30 | class QPushButton; | 30 | class QPushButton; |
| 31 | class QProgressDialog; | ||
| 31 | class WaitTreeWidget; | 32 | class WaitTreeWidget; |
| 32 | enum class GameListOpenTarget; | 33 | enum class GameListOpenTarget; |
| 33 | class GameListPlaceholder; | 34 | class GameListPlaceholder; |
| @@ -272,6 +273,9 @@ private: | |||
| 272 | 273 | ||
| 273 | HotkeyRegistry hotkey_registry; | 274 | HotkeyRegistry hotkey_registry; |
| 274 | 275 | ||
| 276 | // Install to NAND progress dialog | ||
| 277 | QProgressDialog* install_progress; | ||
| 278 | |||
| 275 | protected: | 279 | protected: |
| 276 | void dropEvent(QDropEvent* event) override; | 280 | void dropEvent(QDropEvent* event) override; |
| 277 | void dragEnterEvent(QDragEnterEvent* event) override; | 281 | void dragEnterEvent(QDragEnterEvent* event) override; |
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index bee6e107e..c3a1d715e 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui | |||
| @@ -130,7 +130,7 @@ | |||
| 130 | <bool>true</bool> | 130 | <bool>true</bool> |
| 131 | </property> | 131 | </property> |
| 132 | <property name="text"> | 132 | <property name="text"> |
| 133 | <string>Install File to NAND...</string> | 133 | <string>Install Files to NAND...</string> |
| 134 | </property> | 134 | </property> |
| 135 | </action> | 135 | </action> |
| 136 | <action name="action_Load_File"> | 136 | <action name="action_Load_File"> |