diff options
| -rw-r--r-- | src/yuzu/install_dialog.cpp | 16 | ||||
| -rw-r--r-- | src/yuzu/install_dialog.h | 5 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 399 | ||||
| -rw-r--r-- | src/yuzu/main.h | 14 |
4 files changed, 238 insertions, 196 deletions
diff --git a/src/yuzu/install_dialog.cpp b/src/yuzu/install_dialog.cpp index fac158c25..5f3b4c963 100644 --- a/src/yuzu/install_dialog.cpp +++ b/src/yuzu/install_dialog.cpp | |||
| @@ -22,7 +22,7 @@ InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialo | |||
| 22 | item->setCheckState(Qt::Checked); | 22 | item->setCheckState(Qt::Checked); |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | file_list->setMinimumWidth((file_list->sizeHintForColumn(0) * 6) / 5); | 25 | file_list->setMinimumWidth((file_list->sizeHintForColumn(0) * 10) / 9); |
| 26 | 26 | ||
| 27 | vbox_layout = new QVBoxLayout; | 27 | vbox_layout = new QVBoxLayout; |
| 28 | 28 | ||
| @@ -54,19 +54,23 @@ InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialo | |||
| 54 | 54 | ||
| 55 | InstallDialog::~InstallDialog() = default; | 55 | InstallDialog::~InstallDialog() = default; |
| 56 | 56 | ||
| 57 | QStringList InstallDialog::GetFilenames() const { | 57 | QStringList InstallDialog::GetFiles() const { |
| 58 | QStringList filenames; | 58 | QStringList files; |
| 59 | 59 | ||
| 60 | for (int i = 0; i < file_list->count(); ++i) { | 60 | for (int i = 0; i < file_list->count(); ++i) { |
| 61 | const QListWidgetItem* item = file_list->item(i); | 61 | const QListWidgetItem* item = file_list->item(i); |
| 62 | if (item->checkState() == Qt::Checked) { | 62 | if (item->checkState() == Qt::Checked) { |
| 63 | filenames.append(item->data(Qt::UserRole).toString()); | 63 | files.append(item->data(Qt::UserRole).toString()); |
| 64 | } | 64 | } |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | return filenames; | 67 | return files; |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | bool InstallDialog::ShouldOverwriteFiles() const { | 70 | bool InstallDialog::ShouldOverwriteFiles() const { |
| 71 | return overwrite_files->isChecked(); | 71 | return overwrite_files->isChecked(); |
| 72 | } \ No newline at end of file | 72 | } |
| 73 | |||
| 74 | int InstallDialog::GetMinimumWidth() const { | ||
| 75 | return file_list->width(); | ||
| 76 | } | ||
diff --git a/src/yuzu/install_dialog.h b/src/yuzu/install_dialog.h index 3eaa9e60a..55a458ba8 100644 --- a/src/yuzu/install_dialog.h +++ b/src/yuzu/install_dialog.h | |||
| @@ -20,8 +20,9 @@ public: | |||
| 20 | explicit InstallDialog(QWidget* parent, const QStringList& files); | 20 | explicit InstallDialog(QWidget* parent, const QStringList& files); |
| 21 | ~InstallDialog() override; | 21 | ~InstallDialog() override; |
| 22 | 22 | ||
| 23 | QStringList GetFilenames() const; | 23 | QStringList GetFiles() const; |
| 24 | bool ShouldOverwriteFiles() const; | 24 | bool ShouldOverwriteFiles() const; |
| 25 | int GetMinimumWidth() const; | ||
| 25 | 26 | ||
| 26 | private: | 27 | private: |
| 27 | QListWidget* file_list; | 28 | QListWidget* file_list; |
| @@ -32,4 +33,4 @@ private: | |||
| 32 | QLabel* description; | 33 | QLabel* description; |
| 33 | QCheckBox* overwrite_files; | 34 | QCheckBox* overwrite_files; |
| 34 | QDialogButtonBox* buttons; | 35 | QDialogButtonBox* buttons; |
| 35 | }; \ No newline at end of file | 36 | }; |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 45ddc3baf..4539cbe0d 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -1599,28 +1599,107 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 1599 | tr("Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive " | 1599 | tr("Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive " |
| 1600 | "(*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge " | 1600 | "(*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge " |
| 1601 | "Image (*.xci)"); | 1601 | "Image (*.xci)"); |
| 1602 | QStringList files = QFileDialog::getOpenFileNames(this, tr("Install Files"), | ||
| 1603 | UISettings::values.roms_path, file_filter); | ||
| 1604 | 1602 | ||
| 1605 | if (files.isEmpty()) { | 1603 | QStringList filenames = QFileDialog::getOpenFileNames( |
| 1604 | this, tr("Install Files"), UISettings::values.roms_path, file_filter); | ||
| 1605 | |||
| 1606 | if (filenames.isEmpty()) { | ||
| 1606 | return; | 1607 | return; |
| 1607 | } | 1608 | } |
| 1608 | 1609 | ||
| 1609 | InstallDialog installDialog(this, files); | 1610 | InstallDialog installDialog(this, filenames); |
| 1610 | if (installDialog.exec() == QDialog::Rejected) { | 1611 | if (installDialog.exec() == QDialog::Rejected) { |
| 1611 | return; | 1612 | return; |
| 1612 | } | 1613 | } |
| 1613 | 1614 | ||
| 1614 | const QStringList filenames = installDialog.GetFilenames(); | 1615 | const QStringList files = installDialog.GetFiles(); |
| 1615 | const bool overwrite_files = installDialog.ShouldOverwriteFiles(); | 1616 | const bool overwrite_files = installDialog.ShouldOverwriteFiles(); |
| 1616 | 1617 | ||
| 1617 | int count = 0; | 1618 | int count = 0; |
| 1618 | int total_count = filenames.size(); | 1619 | const int total_count = filenames.size(); |
| 1619 | bool is_progressdialog_created = false; | 1620 | |
| 1621 | QStringList new_files{}; // Newly installed files that do not yet exist in the NAND | ||
| 1622 | QStringList overwritten_files{}; // Files that overwrote those existing in the NAND | ||
| 1623 | QStringList existing_files{}; // Files that were not installed as they already exist in the NAND | ||
| 1624 | QStringList failed_files{}; // Files that failed to install due to errors | ||
| 1625 | |||
| 1626 | ui.action_Install_File_NAND->setEnabled(false); | ||
| 1627 | |||
| 1628 | QProgressDialog install_progress(QStringLiteral(""), tr("Cancel"), 0, total_count, this); | ||
| 1629 | install_progress.setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint & | ||
| 1630 | ~Qt::WindowMaximizeButtonHint); | ||
| 1631 | install_progress.setAutoClose(false); | ||
| 1632 | install_progress.setFixedWidth(installDialog.GetMinimumWidth()); | ||
| 1633 | install_progress.show(); | ||
| 1634 | |||
| 1635 | for (const QString& file : files) { | ||
| 1636 | install_progress.setWindowTitle(tr("%n file(s) remaining", "", total_count - count)); | ||
| 1637 | install_progress.setLabelText( | ||
| 1638 | tr("Installing file \"%1\"...").arg(QFileInfo(file).fileName())); | ||
| 1639 | |||
| 1640 | QFuture<InstallResult> future; | ||
| 1641 | InstallResult result; | ||
| 1642 | |||
| 1643 | if (file.endsWith(QStringLiteral("xci"), Qt::CaseInsensitive) || | ||
| 1644 | file.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { | ||
| 1645 | future = QtConcurrent::run([this, &file, &overwrite_files, &install_progress] { | ||
| 1646 | return InstallNSPXCI(file, overwrite_files, install_progress); | ||
| 1647 | }); | ||
| 1648 | |||
| 1649 | while (!future.isFinished()) { | ||
| 1650 | QCoreApplication::processEvents(); | ||
| 1651 | } | ||
| 1652 | |||
| 1653 | result = future.result(); | ||
| 1654 | } else { | ||
| 1655 | result = InstallNCA(file, overwrite_files, install_progress); | ||
| 1656 | } | ||
| 1620 | 1657 | ||
| 1621 | const auto qt_raw_copy = [this, &count, &total_count, &is_progressdialog_created]( | 1658 | switch (result) { |
| 1622 | const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, | 1659 | case InstallResult::Success: |
| 1623 | std::size_t block_size) { | 1660 | new_files.append(QFileInfo(file).fileName()); |
| 1661 | break; | ||
| 1662 | case InstallResult::Overwrite: | ||
| 1663 | overwritten_files.append(QFileInfo(file).fileName()); | ||
| 1664 | break; | ||
| 1665 | case InstallResult::AlreadyExists: | ||
| 1666 | existing_files.append(QFileInfo(file).fileName()); | ||
| 1667 | break; | ||
| 1668 | case InstallResult::Failure: | ||
| 1669 | failed_files.append(QFileInfo(file).fileName()); | ||
| 1670 | break; | ||
| 1671 | } | ||
| 1672 | |||
| 1673 | install_progress.setValue(++count); | ||
| 1674 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); | ||
| 1675 | } | ||
| 1676 | |||
| 1677 | install_progress.close(); | ||
| 1678 | |||
| 1679 | const QString install_results = | ||
| 1680 | (new_files.isEmpty() ? QStringLiteral("") | ||
| 1681 | : tr("%n file(s) were newly installed\n", "", new_files.size())) + | ||
| 1682 | (overwritten_files.isEmpty() | ||
| 1683 | ? QStringLiteral("") | ||
| 1684 | : tr("%n file(s) were overwritten\n", "", overwritten_files.size())) + | ||
| 1685 | (existing_files.isEmpty() | ||
| 1686 | ? QStringLiteral("") | ||
| 1687 | : tr("%n file(s) already exist in NAND\n", "", existing_files.size())) + | ||
| 1688 | (failed_files.isEmpty() ? QStringLiteral("") | ||
| 1689 | : tr("%n file(s) failed to install\n", "", failed_files.size())); | ||
| 1690 | |||
| 1691 | QMessageBox::information(this, tr("Install Results"), install_results); | ||
| 1692 | game_list->PopulateAsync(UISettings::values.game_dirs); | ||
| 1693 | FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + | ||
| 1694 | "game_list"); | ||
| 1695 | ui.action_Install_File_NAND->setEnabled(true); | ||
| 1696 | } | ||
| 1697 | |||
| 1698 | InstallResult GMainWindow::InstallNSPXCI(const QString& filename, bool overwrite_files, | ||
| 1699 | QProgressDialog& install_progress) { | ||
| 1700 | const auto qt_raw_copy = [this, &install_progress](const FileSys::VirtualFile& src, | ||
| 1701 | const FileSys::VirtualFile& dest, | ||
| 1702 | std::size_t block_size) { | ||
| 1624 | if (src == nullptr || dest == nullptr) { | 1703 | if (src == nullptr || dest == nullptr) { |
| 1625 | return false; | 1704 | return false; |
| 1626 | } | 1705 | } |
| @@ -1629,204 +1708,154 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 1629 | } | 1708 | } |
| 1630 | 1709 | ||
| 1631 | std::array<u8, 0x1000> buffer{}; | 1710 | std::array<u8, 0x1000> buffer{}; |
| 1632 | const int progress_maximum = static_cast<int>(src->GetSize() / buffer.size()); | ||
| 1633 | |||
| 1634 | if (!is_progressdialog_created) { | ||
| 1635 | ui.action_Install_File_NAND->setEnabled(false); | ||
| 1636 | install_progress = new QProgressDialog( | ||
| 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 | } | ||
| 1652 | 1711 | ||
| 1653 | for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { | 1712 | for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { |
| 1654 | if (install_progress->wasCanceled()) { | 1713 | if (install_progress.wasCanceled()) { |
| 1655 | dest->Resize(0); | 1714 | dest->Resize(0); |
| 1656 | return false; | 1715 | return false; |
| 1657 | } | 1716 | } |
| 1658 | 1717 | ||
| 1659 | const int progress_value = static_cast<int>(i / buffer.size()); | ||
| 1660 | install_progress->setValue(progress_value); | ||
| 1661 | |||
| 1662 | const auto read = src->Read(buffer.data(), buffer.size(), i); | 1718 | const auto read = src->Read(buffer.data(), buffer.size(), i); |
| 1663 | dest->Write(buffer.data(), read, i); | 1719 | dest->Write(buffer.data(), read, i); |
| 1664 | } | 1720 | } |
| 1665 | |||
| 1666 | return true; | 1721 | return true; |
| 1667 | }; | 1722 | }; |
| 1668 | 1723 | ||
| 1669 | const auto success = [this, &count, &is_progressdialog_created]() { | 1724 | std::shared_ptr<FileSys::NSP> nsp; |
| 1670 | if (is_progressdialog_created) { | 1725 | if (filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { |
| 1671 | install_progress->close(); | 1726 | nsp = std::make_shared<FileSys::NSP>( |
| 1672 | } | 1727 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); |
| 1673 | QMessageBox::information(this, tr("Successfully Installed"), | 1728 | if (nsp->IsExtractedType()) { |
| 1674 | tr("%n file(s) successfully installed", "", count)); | 1729 | return InstallResult::Failure; |
| 1675 | game_list->PopulateAsync(UISettings::values.game_dirs); | ||
| 1676 | FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + | ||
| 1677 | DIR_SEP + "game_list"); | ||
| 1678 | ui.action_Install_File_NAND->setEnabled(true); | ||
| 1679 | }; | ||
| 1680 | |||
| 1681 | const auto failed = [this, &is_progressdialog_created](const QString& file) { | ||
| 1682 | if (is_progressdialog_created) { | ||
| 1683 | install_progress->close(); | ||
| 1684 | } | 1730 | } |
| 1685 | QMessageBox::warning( | 1731 | } else { |
| 1686 | this, tr("Failed to Install %1").arg(QFileInfo(file).fileName()), | 1732 | const auto xci = std::make_shared<FileSys::XCI>( |
| 1687 | tr("There was an error while attempting to install the provided file. It " | 1733 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); |
| 1688 | "could have an incorrect format or be missing metadata. Please " | 1734 | nsp = xci->GetSecurePartitionNSP(); |
| 1689 | "double-check your file and try again.")); | 1735 | } |
| 1690 | game_list->PopulateAsync(UISettings::values.game_dirs); | 1736 | |
| 1691 | ui.action_Install_File_NAND->setEnabled(true); | 1737 | if (nsp->GetStatus() != Loader::ResultStatus::Success) { |
| 1692 | }; | 1738 | return InstallResult::Failure; |
| 1693 | 1739 | } | |
| 1694 | const auto overwrite = [this](const QString& file) { | 1740 | const auto res = |
| 1695 | return QMessageBox::question( | 1741 | Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->InstallEntry( |
| 1696 | this, tr("Failed to Install %1").arg(QFileInfo(file).fileName()), | 1742 | *nsp, false, qt_raw_copy); |
| 1697 | tr("The file you are attempting to install already exists " | 1743 | if (res == FileSys::InstallResult::Success) { |
| 1698 | "in the cache. Would you like to overwrite it?")) == QMessageBox::Yes; | 1744 | return InstallResult::Success; |
| 1699 | }; | 1745 | } else if (res == FileSys::InstallResult::ErrorAlreadyExists) { |
| 1700 | 1746 | if (overwrite_files) { | |
| 1701 | for (const QString& filename : filenames) { | 1747 | const auto res2 = Core::System::GetInstance() |
| 1702 | if (filename.endsWith(QStringLiteral("xci"), Qt::CaseInsensitive) || | 1748 | .GetFileSystemController() |
| 1703 | filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { | 1749 | .GetUserNANDContents() |
| 1704 | std::shared_ptr<FileSys::NSP> nsp; | 1750 | ->InstallEntry(*nsp, true, qt_raw_copy); |
| 1705 | if (filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { | 1751 | if (res2 != FileSys::InstallResult::Success) { |
| 1706 | nsp = std::make_shared<FileSys::NSP>( | 1752 | return InstallResult::Failure; |
| 1707 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); | ||
| 1708 | if (nsp->IsExtractedType()) { | ||
| 1709 | failed(filename); | ||
| 1710 | break; | ||
| 1711 | } | ||
| 1712 | } else { | ||
| 1713 | const auto xci = std::make_shared<FileSys::XCI>( | ||
| 1714 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); | ||
| 1715 | nsp = xci->GetSecurePartitionNSP(); | ||
| 1716 | } | ||
| 1717 | |||
| 1718 | if (nsp->GetStatus() != Loader::ResultStatus::Success) { | ||
| 1719 | failed(filename); | ||
| 1720 | break; | ||
| 1721 | } | ||
| 1722 | const auto res = Core::System::GetInstance() | ||
| 1723 | .GetFileSystemController() | ||
| 1724 | .GetUserNANDContents() | ||
| 1725 | ->InstallEntry(*nsp, false, qt_raw_copy); | ||
| 1726 | if (res == FileSys::InstallResult::Success) { | ||
| 1727 | ++count; | ||
| 1728 | } else if (res == FileSys::InstallResult::ErrorAlreadyExists) { | ||
| 1729 | if (overwrite_files && overwrite(filename)) { | ||
| 1730 | const auto res2 = Core::System::GetInstance() | ||
| 1731 | .GetFileSystemController() | ||
| 1732 | .GetUserNANDContents() | ||
| 1733 | ->InstallEntry(*nsp, true, qt_raw_copy); | ||
| 1734 | if (res2 != FileSys::InstallResult::Success) { | ||
| 1735 | failed(filename); | ||
| 1736 | break; | ||
| 1737 | } | ||
| 1738 | ++count; | ||
| 1739 | } else { | ||
| 1740 | --total_count; | ||
| 1741 | } | ||
| 1742 | } else { | ||
| 1743 | failed(filename); | ||
| 1744 | break; | ||
| 1745 | } | 1753 | } |
| 1754 | return InstallResult::Overwrite; | ||
| 1746 | } else { | 1755 | } else { |
| 1747 | const auto nca = std::make_shared<FileSys::NCA>( | 1756 | return InstallResult::AlreadyExists; |
| 1748 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); | 1757 | } |
| 1749 | const auto id = nca->GetStatus(); | 1758 | } else { |
| 1750 | 1759 | return InstallResult::Failure; | |
| 1751 | // Game updates necessary are missing base RomFS | 1760 | } |
| 1752 | if (id != Loader::ResultStatus::Success && | 1761 | } |
| 1753 | id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { | ||
| 1754 | failed(filename); | ||
| 1755 | break; | ||
| 1756 | } | ||
| 1757 | 1762 | ||
| 1758 | const QStringList tt_options{tr("System Application"), | 1763 | InstallResult GMainWindow::InstallNCA(const QString& filename, bool overwrite_files, |
| 1759 | tr("System Archive"), | 1764 | QProgressDialog& install_progress) { |
| 1760 | tr("System Application Update"), | 1765 | const auto qt_raw_copy = [this, &install_progress](const FileSys::VirtualFile& src, |
| 1761 | tr("Firmware Package (Type A)"), | 1766 | const FileSys::VirtualFile& dest, |
| 1762 | tr("Firmware Package (Type B)"), | 1767 | std::size_t block_size) { |
| 1763 | tr("Game"), | 1768 | if (src == nullptr || dest == nullptr) { |
| 1764 | tr("Game Update"), | 1769 | return false; |
| 1765 | tr("Game DLC"), | 1770 | } |
| 1766 | tr("Delta Title")}; | 1771 | if (!dest->Resize(src->GetSize())) { |
| 1767 | bool ok; | 1772 | return false; |
| 1768 | const auto item = QInputDialog::getItem( | 1773 | } |
| 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 | } | ||
| 1780 | 1774 | ||
| 1781 | // If index is equal to or past Game, add the jump in TitleType. | 1775 | std::array<u8, 0x1000> buffer{}; |
| 1782 | if (index >= 5) { | ||
| 1783 | index += static_cast<size_t>(FileSys::TitleType::Application) - | ||
| 1784 | static_cast<size_t>(FileSys::TitleType::FirmwarePackageB); | ||
| 1785 | } | ||
| 1786 | 1776 | ||
| 1787 | FileSys::InstallResult res; | 1777 | for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { |
| 1788 | if (index >= static_cast<s32>(FileSys::TitleType::Application)) { | 1778 | if (install_progress.wasCanceled()) { |
| 1789 | res = Core::System::GetInstance() | 1779 | dest->Resize(0); |
| 1790 | .GetFileSystemController() | 1780 | return false; |
| 1791 | .GetUserNANDContents() | ||
| 1792 | ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false, | ||
| 1793 | qt_raw_copy); | ||
| 1794 | } else { | ||
| 1795 | res = Core::System::GetInstance() | ||
| 1796 | .GetFileSystemController() | ||
| 1797 | .GetSystemNANDContents() | ||
| 1798 | ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false, | ||
| 1799 | qt_raw_copy); | ||
| 1800 | } | 1781 | } |
| 1801 | 1782 | ||
| 1802 | if (res == FileSys::InstallResult::Success) { | 1783 | const auto read = src->Read(buffer.data(), buffer.size(), i); |
| 1803 | ++count; | 1784 | dest->Write(buffer.data(), read, i); |
| 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; | ||
| 1817 | } else { | ||
| 1818 | --total_count; | ||
| 1819 | } | ||
| 1820 | } else { | ||
| 1821 | failed(filename); | ||
| 1822 | break; | ||
| 1823 | } | ||
| 1824 | } | 1785 | } |
| 1786 | return true; | ||
| 1787 | }; | ||
| 1825 | 1788 | ||
| 1826 | // Return success only on the last file | 1789 | const auto nca = |
| 1827 | if (filename == filenames.last()) { | 1790 | std::make_shared<FileSys::NCA>(vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); |
| 1828 | success(); | 1791 | const auto id = nca->GetStatus(); |
| 1792 | |||
| 1793 | // Game updates necessary are missing base RomFS | ||
| 1794 | if (id != Loader::ResultStatus::Success && | ||
| 1795 | id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { | ||
| 1796 | return InstallResult::Failure; | ||
| 1797 | } | ||
| 1798 | |||
| 1799 | const QStringList tt_options{tr("System Application"), | ||
| 1800 | tr("System Archive"), | ||
| 1801 | tr("System Application Update"), | ||
| 1802 | tr("Firmware Package (Type A)"), | ||
| 1803 | tr("Firmware Package (Type B)"), | ||
| 1804 | tr("Game"), | ||
| 1805 | tr("Game Update"), | ||
| 1806 | tr("Game DLC"), | ||
| 1807 | tr("Delta Title")}; | ||
| 1808 | bool ok; | ||
| 1809 | const auto item = QInputDialog::getItem( | ||
| 1810 | this, tr("Select NCA Install Type..."), | ||
| 1811 | tr("Please select the type of title you would like to install this NCA as:\n(In " | ||
| 1812 | "most instances, the default 'Game' is fine.)"), | ||
| 1813 | tt_options, 5, false, &ok); | ||
| 1814 | |||
| 1815 | auto index = tt_options.indexOf(item); | ||
| 1816 | if (!ok || index == -1) { | ||
| 1817 | QMessageBox::warning(this, tr("Failed to Install"), | ||
| 1818 | tr("The title type you selected for the NCA is invalid.")); | ||
| 1819 | return InstallResult::Failure; | ||
| 1820 | } | ||
| 1821 | |||
| 1822 | // If index is equal to or past Game, add the jump in TitleType. | ||
| 1823 | if (index >= 5) { | ||
| 1824 | index += static_cast<size_t>(FileSys::TitleType::Application) - | ||
| 1825 | static_cast<size_t>(FileSys::TitleType::FirmwarePackageB); | ||
| 1826 | } | ||
| 1827 | |||
| 1828 | FileSys::InstallResult res; | ||
| 1829 | if (index >= static_cast<s32>(FileSys::TitleType::Application)) { | ||
| 1830 | res = Core::System::GetInstance() | ||
| 1831 | .GetFileSystemController() | ||
| 1832 | .GetUserNANDContents() | ||
| 1833 | ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy); | ||
| 1834 | } else { | ||
| 1835 | res = Core::System::GetInstance() | ||
| 1836 | .GetFileSystemController() | ||
| 1837 | .GetSystemNANDContents() | ||
| 1838 | ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy); | ||
| 1839 | } | ||
| 1840 | |||
| 1841 | if (res == FileSys::InstallResult::Success) { | ||
| 1842 | return InstallResult::Success; | ||
| 1843 | } else if (res == FileSys::InstallResult::ErrorAlreadyExists) { | ||
| 1844 | if (overwrite_files) { | ||
| 1845 | const auto res2 = | ||
| 1846 | Core::System::GetInstance() | ||
| 1847 | .GetFileSystemController() | ||
| 1848 | .GetUserNANDContents() | ||
| 1849 | ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy); | ||
| 1850 | if (res2 != FileSys::InstallResult::Success) { | ||
| 1851 | return InstallResult::Failure; | ||
| 1852 | } | ||
| 1853 | return InstallResult::Overwrite; | ||
| 1854 | } else { | ||
| 1855 | return InstallResult::AlreadyExists; | ||
| 1829 | } | 1856 | } |
| 1857 | } else { | ||
| 1858 | return InstallResult::Failure; | ||
| 1830 | } | 1859 | } |
| 1831 | } | 1860 | } |
| 1832 | 1861 | ||
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 55d072e96..deea8170d 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -48,6 +48,13 @@ enum class EmulatedDirectoryTarget { | |||
| 48 | SDMC, | 48 | SDMC, |
| 49 | }; | 49 | }; |
| 50 | 50 | ||
| 51 | enum class InstallResult { | ||
| 52 | Success, | ||
| 53 | Overwrite, | ||
| 54 | AlreadyExists, | ||
| 55 | Failure, | ||
| 56 | }; | ||
| 57 | |||
| 51 | enum class ReinitializeKeyBehavior { | 58 | enum class ReinitializeKeyBehavior { |
| 52 | NoWarning, | 59 | NoWarning, |
| 53 | Warning, | 60 | Warning, |
| @@ -219,6 +226,10 @@ private slots: | |||
| 219 | 226 | ||
| 220 | private: | 227 | private: |
| 221 | std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); | 228 | std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); |
| 229 | InstallResult InstallNSPXCI(const QString& filename, bool overwrite_files, | ||
| 230 | QProgressDialog& install_progress); | ||
| 231 | InstallResult InstallNCA(const QString& filename, bool overwrite_files, | ||
| 232 | QProgressDialog& install_progress); | ||
| 222 | void UpdateWindowTitle(const std::string& title_name = {}, | 233 | void UpdateWindowTitle(const std::string& title_name = {}, |
| 223 | const std::string& title_version = {}); | 234 | const std::string& title_version = {}); |
| 224 | void UpdateStatusBar(); | 235 | void UpdateStatusBar(); |
| @@ -273,9 +284,6 @@ private: | |||
| 273 | 284 | ||
| 274 | HotkeyRegistry hotkey_registry; | 285 | HotkeyRegistry hotkey_registry; |
| 275 | 286 | ||
| 276 | // Install to NAND progress dialog | ||
| 277 | QProgressDialog* install_progress; | ||
| 278 | |||
| 279 | protected: | 287 | protected: |
| 280 | void dropEvent(QDropEvent* event) override; | 288 | void dropEvent(QDropEvent* event) override; |
| 281 | void dragEnterEvent(QDragEnterEvent* event) override; | 289 | void dragEnterEvent(QDragEnterEvent* event) override; |