From d1eaeeed8c2429547e584e1c8ef7abbdef1b7d36 Mon Sep 17 00:00:00 2001 From: Andrew Pilley Date: Tue, 20 Feb 2024 11:18:13 +1100 Subject: Import keys from filesystem. (#13056) * Import keys, re-initialize KeyManager, re-scan vfs, re-populate game list. * <.< spelling. * Update based on feedback on #13047 and this PR * Based on feedback: Don't delete existing files. There's legitimate reasons that someone may want to keep their retail keys and title key handling is resilient to mismatches. * Update src/yuzu/main.cpp Co-authored-by: Tobias * Remove translation of literal filename/filter format. * clang-format. --------- Co-authored-by: Tobias --- src/yuzu/main.cpp | 109 ++++++++++++++++++++++++++++++++++++++++++++++-------- src/yuzu/main.h | 1 + src/yuzu/main.ui | 8 +++- 3 files changed, 101 insertions(+), 17 deletions(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 236642fb9..b2ae3db52 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1604,6 +1604,7 @@ void GMainWindow::ConnectMenuEvents() { 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_Install_Keys, &GMainWindow::OnInstallDecryptionKeys); connect_menu(ui->action_About, &GMainWindow::OnAbout); } @@ -1633,6 +1634,7 @@ void GMainWindow::UpdateMenuState() { } ui->action_Install_Firmware->setEnabled(!emulation_running); + ui->action_Install_Keys->setEnabled(!emulation_running); for (QAction* action : applet_actions) { action->setEnabled(is_firmware_available && !emulation_running); @@ -4169,9 +4171,8 @@ void GMainWindow::OnInstallFirmware() { return; } - QString firmware_source_location = - QFileDialog::getExistingDirectory(this, tr("Select Dumped Firmware Source Location"), - QString::fromStdString(""), QFileDialog::ShowDirsOnly); + const QString firmware_source_location = QFileDialog::getExistingDirectory( + this, tr("Select Dumped Firmware Source Location"), {}, QFileDialog::ShowDirsOnly); if (firmware_source_location.isEmpty()) { return; } @@ -4202,8 +4203,9 @@ void GMainWindow::OnInstallFirmware() { std::vector out; const Common::FS::DirEntryCallable callback = [&out](const std::filesystem::directory_entry& entry) { - if (entry.path().has_extension() && entry.path().extension() == ".nca") + if (entry.path().has_extension() && entry.path().extension() == ".nca") { out.emplace_back(entry.path()); + } return true; }; @@ -4235,7 +4237,6 @@ void GMainWindow::OnInstallFirmware() { 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++; @@ -4250,24 +4251,22 @@ void GMainWindow::OnInstallFirmware() { success = false; } - if (QtProgressCallback(100, 20 + (int)(((float)(i) / (float)out.size()) * 70.0))) { - success = false; - cancelled = true; - break; + if (QtProgressCallback( + 100, 20 + static_cast(((i) / static_cast(out.size())) * 70.0))) { + 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; } } - if (!success && !cancelled) { + if (!success) { 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. @@ -4295,6 +4294,84 @@ void GMainWindow::OnInstallFirmware() { OnCheckFirmwareDecryption(); } +void GMainWindow::OnInstallDecryptionKeys() { + // Don't do this while emulation is running. + if (emu_thread != nullptr && emu_thread->IsRunning()) { + return; + } + + const QString key_source_location = QFileDialog::getOpenFileName( + this, tr("Select Dumped Keys Location"), {}, QStringLiteral("prod.keys (prod.keys)"), {}, + QFileDialog::ReadOnly); + if (key_source_location.isEmpty()) { + return; + } + + // Verify that it contains prod.keys, title.keys and optionally, key_retail.bin + LOG_INFO(Frontend, "Installing key files from {}", key_source_location.toStdString()); + + const std::filesystem::path prod_key_path = key_source_location.toStdString(); + const std::filesystem::path key_source_path = prod_key_path.parent_path(); + if (!Common::FS::IsDir(key_source_path)) { + return; + } + + bool prod_keys_found = false; + std::vector source_key_files; + + if (Common::FS::Exists(prod_key_path)) { + prod_keys_found = true; + source_key_files.emplace_back(prod_key_path); + } + + if (Common::FS::Exists(key_source_path / "title.keys")) { + source_key_files.emplace_back(key_source_path / "title.keys"); + } + + if (Common::FS::Exists(key_source_path / "key_retail.bin")) { + source_key_files.emplace_back(key_source_path / "key_retail.bin"); + } + + // There should be at least prod.keys. + if (source_key_files.empty() || !prod_keys_found) { + QMessageBox::warning(this, tr("Decryption Keys install failed"), + tr("prod.keys is a required decryption key file.")); + return; + } + + const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); + for (auto key_file : source_key_files) { + std::filesystem::path destination_key_file = yuzu_keys_dir / key_file.filename(); + if (!std::filesystem::copy_file(key_file, destination_key_file, + std::filesystem::copy_options::overwrite_existing)) { + LOG_ERROR(Frontend, "Failed to copy file {} to {}", key_file.string(), + destination_key_file.string()); + QMessageBox::critical(this, tr("Decryption Keys install failed"), + tr("One or more keys failed to copy.")); + return; + } + } + + // Reinitialize the key manager, re-read the vfs (for update/dlc files), + // and re-populate the game list in the UI if the user has already added + // game folders. + Core::Crypto::KeyManager::Instance().ReloadKeys(); + system->GetFileSystemController().CreateFactories(*vfs); + game_list->PopulateAsync(UISettings::values.game_dirs); + + if (ContentManager::AreKeysPresent()) { + QMessageBox::information(this, tr("Decryption Keys install succeeded"), + tr("Decryption Keys were successfully installed")); + } else { + QMessageBox::critical( + this, tr("Decryption Keys install failed"), + tr("Decryption Keys failed to initialize. Check that your dumping tools are " + "up to date and re-dump keys.")); + } + + OnCheckFirmwareDecryption(); +} + void GMainWindow::OnAbout() { AboutDialog aboutDialog(this); aboutDialog.exec(); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 1f0e35c67..fce643f3f 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -381,6 +381,7 @@ private slots: void OnOpenYuzuFolder(); void OnVerifyInstalledContents(); void OnInstallFirmware(); + void OnInstallDecryptionKeys(); void OnAbout(); void OnToggleFilterBar(); void OnToggleStatusBar(); diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 6ff444a22..85dc1f2f6 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -165,8 +165,9 @@ - + + @@ -469,6 +470,11 @@ Install Firmware + + + Install Decryption Keys + + -- cgit v1.2.3