diff options
| author | 2024-02-17 23:18:00 -0500 | |
|---|---|---|
| committer | 2024-02-17 23:18:00 -0500 | |
| commit | bdf8aca7509534f817644ce05e95d5f5c1f53941 (patch) | |
| tree | e2ebd062a528b0ace705130763569851632accf9 /src | |
| parent | Merge pull request #13054 from t895/lifecycle-utils (diff) | |
| parent | Add check for corrupted firmware files after install. (diff) | |
| download | yuzu-bdf8aca7509534f817644ce05e95d5f5c1f53941.tar.gz yuzu-bdf8aca7509534f817644ce05e95d5f5c1f53941.tar.xz yuzu-bdf8aca7509534f817644ce05e95d5f5c1f53941.zip | |
Merge pull request #13047 from anpilley/import-firmware
Import firmware from folder of loose NCA files
Diffstat (limited to 'src')
| -rw-r--r-- | src/frontend_common/content_manager.h | 5 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 143 | ||||
| -rw-r--r-- | src/yuzu/main.h | 1 | ||||
| -rw-r--r-- | src/yuzu/main.ui | 18 |
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& | |||
| 251 | * \param callback Callback to report the progress of the installation. The first size_t | 251 | * \param callback Callback to report the progress of the installation. The first size_t |
| 252 | * parameter is the total size of the installed contents and the second is the current progress. If | 252 | * parameter is the total size of the installed contents and the second is the current progress. If |
| 253 | * you return true to the callback, it will cancel the installation as soon as possible. | 253 | * you return true to the callback, it will cancel the installation as soon as possible. |
| 254 | * \param firmware_only Set to true to only scan system nand NCAs (firmware), post firmware install. | ||
| 254 | * \return A list of entries that failed to install. Returns an empty vector if successful. | 255 | * \return A list of entries that failed to install. Returns an empty vector if successful. |
| 255 | */ | 256 | */ |
| 256 | inline std::vector<std::string> VerifyInstalledContents( | 257 | inline std::vector<std::string> VerifyInstalledContents( |
| 257 | Core::System& system, FileSys::ManualContentProvider& provider, | 258 | Core::System& system, FileSys::ManualContentProvider& provider, |
| 258 | const std::function<bool(size_t, size_t)>& callback) { | 259 | const std::function<bool(size_t, size_t)>& callback, bool firmware_only = false) { |
| 259 | // Get content registries. | 260 | // Get content registries. |
| 260 | auto bis_contents = system.GetFileSystemController().GetSystemNANDContents(); | 261 | auto bis_contents = system.GetFileSystemController().GetSystemNANDContents(); |
| 261 | auto user_contents = system.GetFileSystemController().GetUserNANDContents(); | 262 | auto user_contents = system.GetFileSystemController().GetUserNANDContents(); |
| @@ -264,7 +265,7 @@ inline std::vector<std::string> VerifyInstalledContents( | |||
| 264 | if (bis_contents) { | 265 | if (bis_contents) { |
| 265 | content_providers.push_back(bis_contents); | 266 | content_providers.push_back(bis_contents); |
| 266 | } | 267 | } |
| 267 | if (user_contents) { | 268 | if (user_contents && !firmware_only) { |
| 268 | content_providers.push_back(user_contents); | 269 | content_providers.push_back(user_contents); |
| 269 | } | 270 | } |
| 270 | 271 | ||
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() { | |||
| 1603 | // Help | 1603 | // Help |
| 1604 | connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder); | 1604 | connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder); |
| 1605 | connect_menu(ui->action_Verify_installed_contents, &GMainWindow::OnVerifyInstalledContents); | 1605 | connect_menu(ui->action_Verify_installed_contents, &GMainWindow::OnVerifyInstalledContents); |
| 1606 | connect_menu(ui->action_Install_Firmware, &GMainWindow::OnInstallFirmware); | ||
| 1606 | connect_menu(ui->action_About, &GMainWindow::OnAbout); | 1607 | connect_menu(ui->action_About, &GMainWindow::OnAbout); |
| 1607 | } | 1608 | } |
| 1608 | 1609 | ||
| @@ -1631,6 +1632,8 @@ void GMainWindow::UpdateMenuState() { | |||
| 1631 | action->setEnabled(emulation_running); | 1632 | action->setEnabled(emulation_running); |
| 1632 | } | 1633 | } |
| 1633 | 1634 | ||
| 1635 | ui->action_Install_Firmware->setEnabled(!emulation_running); | ||
| 1636 | |||
| 1634 | for (QAction* action : applet_actions) { | 1637 | for (QAction* action : applet_actions) { |
| 1635 | action->setEnabled(is_firmware_available && !emulation_running); | 1638 | action->setEnabled(is_firmware_available && !emulation_running); |
| 1636 | } | 1639 | } |
| @@ -4150,6 +4153,146 @@ void GMainWindow::OnVerifyInstalledContents() { | |||
| 4150 | } | 4153 | } |
| 4151 | } | 4154 | } |
| 4152 | 4155 | ||
| 4156 | void GMainWindow::OnInstallFirmware() { | ||
| 4157 | // Don't do this while emulation is running, that'd probably be a bad idea. | ||
| 4158 | if (emu_thread != nullptr && emu_thread->IsRunning()) { | ||
| 4159 | return; | ||
| 4160 | } | ||
| 4161 | |||
| 4162 | // Check for installed keys, error out, suggest restart? | ||
| 4163 | if (!ContentManager::AreKeysPresent()) { | ||
| 4164 | QMessageBox::information( | ||
| 4165 | this, tr("Keys not installed"), | ||
| 4166 | tr("Install decryption keys and restart yuzu before attempting to install firmware.")); | ||
| 4167 | return; | ||
| 4168 | } | ||
| 4169 | |||
| 4170 | QString firmware_source_location = | ||
| 4171 | QFileDialog::getExistingDirectory(this, tr("Select Dumped Firmware Source Location"), | ||
| 4172 | QString::fromStdString(""), QFileDialog::ShowDirsOnly); | ||
| 4173 | if (firmware_source_location.isEmpty()) { | ||
| 4174 | return; | ||
| 4175 | } | ||
| 4176 | |||
| 4177 | QProgressDialog progress(tr("Installing Firmware..."), tr("Cancel"), 0, 100, this); | ||
| 4178 | progress.setWindowModality(Qt::WindowModal); | ||
| 4179 | progress.setMinimumDuration(100); | ||
| 4180 | progress.setAutoClose(false); | ||
| 4181 | progress.setAutoReset(false); | ||
| 4182 | progress.show(); | ||
| 4183 | |||
| 4184 | // Declare progress callback. | ||
| 4185 | auto QtProgressCallback = [&](size_t total_size, size_t processed_size) { | ||
| 4186 | progress.setValue(static_cast<int>((processed_size * 100) / total_size)); | ||
| 4187 | return progress.wasCanceled(); | ||
| 4188 | }; | ||
| 4189 | |||
| 4190 | LOG_INFO(Frontend, "Installing firmware from {}", firmware_source_location.toStdString()); | ||
| 4191 | |||
| 4192 | // Check for a reasonable number of .nca files (don't hardcode them, just see if there's some in | ||
| 4193 | // there.) | ||
| 4194 | std::filesystem::path firmware_source_path = firmware_source_location.toStdString(); | ||
| 4195 | if (!Common::FS::IsDir(firmware_source_path)) { | ||
| 4196 | progress.close(); | ||
| 4197 | return; | ||
| 4198 | } | ||
| 4199 | |||
| 4200 | std::vector<std::filesystem::path> out; | ||
| 4201 | const Common::FS::DirEntryCallable callback = | ||
| 4202 | [&out](const std::filesystem::directory_entry& entry) { | ||
| 4203 | if (entry.path().has_extension() && entry.path().extension() == ".nca") | ||
| 4204 | out.emplace_back(entry.path()); | ||
| 4205 | |||
| 4206 | return true; | ||
| 4207 | }; | ||
| 4208 | |||
| 4209 | QtProgressCallback(100, 10); | ||
| 4210 | |||
| 4211 | Common::FS::IterateDirEntries(firmware_source_path, callback, Common::FS::DirEntryFilter::File); | ||
| 4212 | if (out.size() <= 0) { | ||
| 4213 | progress.close(); | ||
| 4214 | QMessageBox::warning(this, tr("Firmware install failed"), | ||
| 4215 | tr("Unable to locate potential firmware NCA files")); | ||
| 4216 | return; | ||
| 4217 | } | ||
| 4218 | |||
| 4219 | // Locate and erase the content of nand/system/Content/registered/*.nca, if any. | ||
| 4220 | auto sysnand_content_vdir = system->GetFileSystemController().GetSystemNANDContentDirectory(); | ||
| 4221 | if (!sysnand_content_vdir->CleanSubdirectoryRecursive("registered")) { | ||
| 4222 | progress.close(); | ||
| 4223 | QMessageBox::critical(this, tr("Firmware install failed"), | ||
| 4224 | tr("Failed to delete one or more firmware file.")); | ||
| 4225 | return; | ||
| 4226 | } | ||
| 4227 | |||
| 4228 | LOG_INFO(Frontend, | ||
| 4229 | "Cleaned nand/system/Content/registered folder in preparation for new firmware."); | ||
| 4230 | |||
| 4231 | QtProgressCallback(100, 20); | ||
| 4232 | |||
| 4233 | auto firmware_vdir = sysnand_content_vdir->GetDirectoryRelative("registered"); | ||
| 4234 | |||
| 4235 | bool success = true; | ||
| 4236 | bool cancelled = false; | ||
| 4237 | int i = 0; | ||
| 4238 | for (const auto& firmware_src_path : out) { | ||
| 4239 | i++; | ||
| 4240 | auto firmware_src_vfile = | ||
| 4241 | vfs->OpenFile(firmware_src_path.generic_string(), FileSys::OpenMode::Read); | ||
| 4242 | auto firmware_dst_vfile = | ||
| 4243 | firmware_vdir->CreateFileRelative(firmware_src_path.filename().string()); | ||
| 4244 | |||
| 4245 | if (!VfsRawCopy(firmware_src_vfile, firmware_dst_vfile)) { | ||
| 4246 | LOG_ERROR(Frontend, "Failed to copy firmware file {} to {} in registered folder!", | ||
| 4247 | firmware_src_path.generic_string(), firmware_src_path.filename().string()); | ||
| 4248 | success = false; | ||
| 4249 | } | ||
| 4250 | |||
| 4251 | if (QtProgressCallback(100, 20 + (int)(((float)(i) / (float)out.size()) * 70.0))) { | ||
| 4252 | success = false; | ||
| 4253 | cancelled = true; | ||
| 4254 | break; | ||
| 4255 | } | ||
| 4256 | } | ||
| 4257 | |||
| 4258 | if (!success && !cancelled) { | ||
| 4259 | progress.close(); | ||
| 4260 | QMessageBox::critical(this, tr("Firmware install failed"), | ||
| 4261 | tr("One or more firmware files failed to copy into NAND.")); | ||
| 4262 | return; | ||
| 4263 | } else if (cancelled) { | ||
| 4264 | progress.close(); | ||
| 4265 | QMessageBox::warning(this, tr("Firmware install failed"), | ||
| 4266 | tr("Firmware installation cancelled, firmware may be in bad state, " | ||
| 4267 | "restart yuzu or re-install firmware.")); | ||
| 4268 | return; | ||
| 4269 | } | ||
| 4270 | |||
| 4271 | // Re-scan VFS for the newly placed firmware files. | ||
| 4272 | system->GetFileSystemController().CreateFactories(*vfs); | ||
| 4273 | |||
| 4274 | auto VerifyFirmwareCallback = [&](size_t total_size, size_t processed_size) { | ||
| 4275 | progress.setValue(90 + static_cast<int>((processed_size * 10) / total_size)); | ||
| 4276 | return progress.wasCanceled(); | ||
| 4277 | }; | ||
| 4278 | |||
| 4279 | auto result = | ||
| 4280 | ContentManager::VerifyInstalledContents(*system, *provider, VerifyFirmwareCallback, true); | ||
| 4281 | |||
| 4282 | if (result.size() > 0) { | ||
| 4283 | const auto failed_names = | ||
| 4284 | QString::fromStdString(fmt::format("{}", fmt::join(result, "\n"))); | ||
| 4285 | progress.close(); | ||
| 4286 | QMessageBox::critical( | ||
| 4287 | this, tr("Firmware integrity verification failed!"), | ||
| 4288 | tr("Verification failed for the following files:\n\n%1").arg(failed_names)); | ||
| 4289 | return; | ||
| 4290 | } | ||
| 4291 | |||
| 4292 | progress.close(); | ||
| 4293 | OnCheckFirmwareDecryption(); | ||
| 4294 | } | ||
| 4295 | |||
| 4153 | void GMainWindow::OnAbout() { | 4296 | void GMainWindow::OnAbout() { |
| 4154 | AboutDialog aboutDialog(this); | 4297 | AboutDialog aboutDialog(this); |
| 4155 | aboutDialog.exec(); | 4298 | 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: | |||
| 380 | void OnLoadAmiibo(); | 380 | void OnLoadAmiibo(); |
| 381 | void OnOpenYuzuFolder(); | 381 | void OnOpenYuzuFolder(); |
| 382 | void OnVerifyInstalledContents(); | 382 | void OnVerifyInstalledContents(); |
| 383 | void OnInstallFirmware(); | ||
| 383 | void OnAbout(); | 384 | void OnAbout(); |
| 384 | void OnToggleFilterBar(); | 385 | void OnToggleFilterBar(); |
| 385 | void OnToggleStatusBar(); | 386 | 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 @@ | |||
| 25 | </property> | 25 | </property> |
| 26 | <widget class="QWidget" name="centralwidget"> | 26 | <widget class="QWidget" name="centralwidget"> |
| 27 | <layout class="QHBoxLayout" name="horizontalLayout"> | 27 | <layout class="QHBoxLayout" name="horizontalLayout"> |
| 28 | <property name="margin" stdset="0"> | 28 | <property name="leftMargin"> |
| 29 | <number>0</number> | ||
| 30 | </property> | ||
| 31 | <property name="topMargin"> | ||
| 32 | <number>0</number> | ||
| 33 | </property> | ||
| 34 | <property name="rightMargin"> | ||
| 35 | <number>0</number> | ||
| 36 | </property> | ||
| 37 | <property name="bottomMargin"> | ||
| 29 | <number>0</number> | 38 | <number>0</number> |
| 30 | </property> | 39 | </property> |
| 31 | </layout> | 40 | </layout> |
| @@ -156,8 +165,8 @@ | |||
| 156 | <addaction name="separator"/> | 165 | <addaction name="separator"/> |
| 157 | <addaction name="action_Configure_Tas"/> | 166 | <addaction name="action_Configure_Tas"/> |
| 158 | </widget> | 167 | </widget> |
| 159 | <addaction name="action_Rederive"/> | ||
| 160 | <addaction name="action_Verify_installed_contents"/> | 168 | <addaction name="action_Verify_installed_contents"/> |
| 169 | <addaction name="action_Install_Firmware"/> | ||
| 161 | <addaction name="separator"/> | 170 | <addaction name="separator"/> |
| 162 | <addaction name="menu_cabinet_applet"/> | 171 | <addaction name="menu_cabinet_applet"/> |
| 163 | <addaction name="action_Load_Album"/> | 172 | <addaction name="action_Load_Album"/> |
| @@ -455,6 +464,11 @@ | |||
| 455 | <string>Open &Controller Menu</string> | 464 | <string>Open &Controller Menu</string> |
| 456 | </property> | 465 | </property> |
| 457 | </action> | 466 | </action> |
| 467 | <action name="action_Install_Firmware"> | ||
| 468 | <property name="text"> | ||
| 469 | <string>Install Firmware</string> | ||
| 470 | </property> | ||
| 471 | </action> | ||
| 458 | </widget> | 472 | </widget> |
| 459 | <resources> | 473 | <resources> |
| 460 | <include location="yuzu.qrc"/> | 474 | <include location="yuzu.qrc"/> |