summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/frontend_common/content_manager.h5
-rw-r--r--src/yuzu/main.cpp143
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu/main.ui18
4 files changed, 163 insertions, 4 deletions
diff --git a/src/frontend_common/content_manager.h b/src/frontend_common/content_manager.h
index f3efe3465..c4e97a47b 100644
--- a/src/frontend_common/content_manager.h
+++ b/src/frontend_common/content_manager.h
@@ -251,11 +251,12 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem& vfs, const std::string&
* \param callback Callback to report the progress of the installation. The first size_t
* parameter is the total size of the installed contents and the second is the current progress. If
* you return true to the callback, it will cancel the installation as soon as possible.
+ * \param firmware_only Set to true to only scan system nand NCAs (firmware), post firmware install.
* \return A list of entries that failed to install. Returns an empty vector if successful.
*/
inline std::vector<std::string> VerifyInstalledContents(
Core::System& system, FileSys::ManualContentProvider& provider,
- const std::function<bool(size_t, size_t)>& callback) {
+ const std::function<bool(size_t, size_t)>& callback, bool firmware_only = false) {
// Get content registries.
auto bis_contents = system.GetFileSystemController().GetSystemNANDContents();
auto user_contents = system.GetFileSystemController().GetUserNANDContents();
@@ -264,7 +265,7 @@ inline std::vector<std::string> VerifyInstalledContents(
if (bis_contents) {
content_providers.push_back(bis_contents);
}
- if (user_contents) {
+ if (user_contents && !firmware_only) {
content_providers.push_back(user_contents);
}
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index dfa50006a..0d16bfd65 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1603,6 +1603,7 @@ void GMainWindow::ConnectMenuEvents() {
// Help
connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder);
connect_menu(ui->action_Verify_installed_contents, &GMainWindow::OnVerifyInstalledContents);
+ connect_menu(ui->action_Install_Firmware, &GMainWindow::OnInstallFirmware);
connect_menu(ui->action_About, &GMainWindow::OnAbout);
}
@@ -1631,6 +1632,8 @@ void GMainWindow::UpdateMenuState() {
action->setEnabled(emulation_running);
}
+ ui->action_Install_Firmware->setEnabled(!emulation_running);
+
for (QAction* action : applet_actions) {
action->setEnabled(is_firmware_available && !emulation_running);
}
@@ -4150,6 +4153,146 @@ void GMainWindow::OnVerifyInstalledContents() {
}
}
+void GMainWindow::OnInstallFirmware() {
+ // Don't do this while emulation is running, that'd probably be a bad idea.
+ if (emu_thread != nullptr && emu_thread->IsRunning()) {
+ return;
+ }
+
+ // Check for installed keys, error out, suggest restart?
+ if (!ContentManager::AreKeysPresent()) {
+ QMessageBox::information(
+ this, tr("Keys not installed"),
+ tr("Install decryption keys and restart yuzu before attempting to install firmware."));
+ return;
+ }
+
+ QString firmware_source_location =
+ QFileDialog::getExistingDirectory(this, tr("Select Dumped Firmware Source Location"),
+ QString::fromStdString(""), QFileDialog::ShowDirsOnly);
+ if (firmware_source_location.isEmpty()) {
+ return;
+ }
+
+ QProgressDialog progress(tr("Installing Firmware..."), tr("Cancel"), 0, 100, this);
+ progress.setWindowModality(Qt::WindowModal);
+ progress.setMinimumDuration(100);
+ progress.setAutoClose(false);
+ progress.setAutoReset(false);
+ progress.show();
+
+ // Declare progress callback.
+ auto QtProgressCallback = [&](size_t total_size, size_t processed_size) {
+ progress.setValue(static_cast<int>((processed_size * 100) / total_size));
+ return progress.wasCanceled();
+ };
+
+ LOG_INFO(Frontend, "Installing firmware from {}", firmware_source_location.toStdString());
+
+ // Check for a reasonable number of .nca files (don't hardcode them, just see if there's some in
+ // there.)
+ std::filesystem::path firmware_source_path = firmware_source_location.toStdString();
+ if (!Common::FS::IsDir(firmware_source_path)) {
+ progress.close();
+ return;
+ }
+
+ std::vector<std::filesystem::path> out;
+ const Common::FS::DirEntryCallable callback =
+ [&out](const std::filesystem::directory_entry& entry) {
+ if (entry.path().has_extension() && entry.path().extension() == ".nca")
+ out.emplace_back(entry.path());
+
+ return true;
+ };
+
+ QtProgressCallback(100, 10);
+
+ Common::FS::IterateDirEntries(firmware_source_path, callback, Common::FS::DirEntryFilter::File);
+ if (out.size() <= 0) {
+ progress.close();
+ QMessageBox::warning(this, tr("Firmware install failed"),
+ tr("Unable to locate potential firmware NCA files"));
+ return;
+ }
+
+ // Locate and erase the content of nand/system/Content/registered/*.nca, if any.
+ auto sysnand_content_vdir = system->GetFileSystemController().GetSystemNANDContentDirectory();
+ if (!sysnand_content_vdir->CleanSubdirectoryRecursive("registered")) {
+ progress.close();
+ QMessageBox::critical(this, tr("Firmware install failed"),
+ tr("Failed to delete one or more firmware file."));
+ return;
+ }
+
+ LOG_INFO(Frontend,
+ "Cleaned nand/system/Content/registered folder in preparation for new firmware.");
+
+ QtProgressCallback(100, 20);
+
+ auto firmware_vdir = sysnand_content_vdir->GetDirectoryRelative("registered");
+
+ bool success = true;
+ bool cancelled = false;
+ int i = 0;
+ for (const auto& firmware_src_path : out) {
+ i++;
+ auto firmware_src_vfile =
+ vfs->OpenFile(firmware_src_path.generic_string(), FileSys::OpenMode::Read);
+ auto firmware_dst_vfile =
+ firmware_vdir->CreateFileRelative(firmware_src_path.filename().string());
+
+ if (!VfsRawCopy(firmware_src_vfile, firmware_dst_vfile)) {
+ LOG_ERROR(Frontend, "Failed to copy firmware file {} to {} in registered folder!",
+ firmware_src_path.generic_string(), firmware_src_path.filename().string());
+ success = false;
+ }
+
+ if (QtProgressCallback(100, 20 + (int)(((float)(i) / (float)out.size()) * 70.0))) {
+ success = false;
+ cancelled = true;
+ break;
+ }
+ }
+
+ if (!success && !cancelled) {
+ progress.close();
+ QMessageBox::critical(this, tr("Firmware install failed"),
+ tr("One or more firmware files failed to copy into NAND."));
+ return;
+ } else if (cancelled) {
+ progress.close();
+ QMessageBox::warning(this, tr("Firmware install failed"),
+ tr("Firmware installation cancelled, firmware may be in bad state, "
+ "restart yuzu or re-install firmware."));
+ return;
+ }
+
+ // Re-scan VFS for the newly placed firmware files.
+ system->GetFileSystemController().CreateFactories(*vfs);
+
+ auto VerifyFirmwareCallback = [&](size_t total_size, size_t processed_size) {
+ progress.setValue(90 + static_cast<int>((processed_size * 10) / total_size));
+ return progress.wasCanceled();
+ };
+
+ auto result =
+ ContentManager::VerifyInstalledContents(*system, *provider, VerifyFirmwareCallback, true);
+
+ if (result.size() > 0) {
+ const auto failed_names =
+ QString::fromStdString(fmt::format("{}", fmt::join(result, "\n")));
+ progress.close();
+ QMessageBox::critical(
+ this, tr("Firmware integrity verification failed!"),
+ tr("Verification failed for the following files:\n\n%1").arg(failed_names));
+ return;
+ }
+
+ progress.close();
+ OnCheckFirmwareDecryption();
+}
+
void GMainWindow::OnAbout() {
AboutDialog aboutDialog(this);
aboutDialog.exec();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index aba61e388..1f0e35c67 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -380,6 +380,7 @@ private slots:
void OnLoadAmiibo();
void OnOpenYuzuFolder();
void OnVerifyInstalledContents();
+ void OnInstallFirmware();
void OnAbout();
void OnToggleFilterBar();
void OnToggleStatusBar();
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 6a6b0821f..6ff444a22 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -25,7 +25,16 @@
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
- <property name="margin" stdset="0">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
<number>0</number>
</property>
</layout>
@@ -156,8 +165,8 @@
<addaction name="separator"/>
<addaction name="action_Configure_Tas"/>
</widget>
- <addaction name="action_Rederive"/>
<addaction name="action_Verify_installed_contents"/>
+ <addaction name="action_Install_Firmware"/>
<addaction name="separator"/>
<addaction name="menu_cabinet_applet"/>
<addaction name="action_Load_Album"/>
@@ -455,6 +464,11 @@
<string>Open &amp;Controller Menu</string>
</property>
</action>
+ <action name="action_Install_Firmware">
+ <property name="text">
+ <string>Install Firmware</string>
+ </property>
+ </action>
</widget>
<resources>
<include location="yuzu.qrc"/>