diff --git a/.ci/patch/0001-quazip-strict.patch b/.ci/patch/0001-quazip-strict.patch new file mode 100644 index 0000000000..8283497230 --- /dev/null +++ b/.ci/patch/0001-quazip-strict.patch @@ -0,0 +1,80 @@ +diff --git a/quazip/quazipdir.cpp b/quazip/quazipdir.cpp +index d43f1c1..eb24bf1 100644 +--- a/quazip/quazipdir.cpp ++++ b/quazip/quazipdir.cpp +@@ -293,8 +293,8 @@ bool QuaZipDirComparator::operator()(const QuaZipFileInfo64 &info1, + } + + template +-bool QuaZipDirPrivate::entryInfoList(QStringList nameFilters, +- QDir::Filters filter, QDir::SortFlags sort, TFileInfoList &result) const ++bool QuaZipDirPrivate::entryInfoList(QStringList _nameFilters, ++ QDir::Filters _filter, QDir::SortFlags sort, TFileInfoList &result) const + { + QString basePath = simplePath(); + if (!basePath.isEmpty()) +@@ -305,12 +305,12 @@ bool QuaZipDirPrivate::entryInfoList(QStringList nameFilters, + if (!zip->goToFirstFile()) { + return zip->getZipError() == UNZ_OK; + } +- QDir::Filters fltr = filter; ++ QDir::Filters fltr = _filter; + if (fltr == QDir::NoFilter) + fltr = this->filter; + if (fltr == QDir::NoFilter) + fltr = QDir::AllEntries; +- QStringList nmfltr = nameFilters; ++ QStringList nmfltr = _nameFilters; + if (nmfltr.isEmpty()) + nmfltr = this->nameFilters; + QSet dirsFound; +diff --git a/quazip/quazipfile.cpp b/quazip/quazipfile.cpp +index 4a5f2f9..f7865f5 100644 +--- a/quazip/quazipfile.cpp ++++ b/quazip/quazipfile.cpp +@@ -241,14 +241,14 @@ void QuaZipFile::setFileName(const QString& fileName, QuaZip::CaseSensitivity cs + p->caseSensitivity=cs; + } + +-void QuaZipFilePrivate::setZipError(int zipError) const ++void QuaZipFilePrivate::setZipError(int _zipError) const + { + QuaZipFilePrivate *fakeThis = const_cast(this); // non-const +- fakeThis->zipError=zipError; +- if(zipError==UNZ_OK) ++ fakeThis->zipError = _zipError; ++ if(_zipError == UNZ_OK) + q->setErrorString(QString()); + else +- q->setErrorString(QuaZipFile::tr("ZIP/UNZIP API error %1").arg(zipError)); ++ q->setErrorString(QuaZipFile::tr("ZIP/UNZIP API error %1").arg(_zipError)); + } + + bool QuaZipFile::open(OpenMode mode) +diff --git a/quazip/unzip.c b/quazip/unzip.c +index a39365d..ee7b487 100644 +--- a/quazip/unzip.c ++++ b/quazip/unzip.c +@@ -1054,7 +1054,7 @@ local int unz64local_GetCurrentFileInfoInternal (unzFile file, + /* ZIP64 extra fields */ + if (headerId == 0x0001) + { +- uLong uL; ++ uLong _uL; + + if(file_info.uncompressed_size == (ZPOS64_T)0xFFFFFFFFu) + { +@@ -1078,7 +1078,7 @@ local int unz64local_GetCurrentFileInfoInternal (unzFile file, + if(file_info.disk_num_start == 0xFFFFFFFFu) + { + /* Disk Start Number */ +- if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) ++ if (unz64local_getLong(&s->z_filefunc, s->filestream, &_uL) != UNZ_OK) + err=UNZ_ERRNO; + } + +@@ -2151,3 +2151,4 @@ int ZEXPORT unzClearFlags(unzFile file, unsigned flags) + s->flags &= ~flags; + return UNZ_OK; + } ++ diff --git a/.gitignore b/.gitignore index 9aaf549512..4417d3f132 100644 --- a/.gitignore +++ b/.gitignore @@ -52,4 +52,3 @@ Thumbs.db eden-windows-msvc artifacts *.AppImage* -*.patch diff --git a/CMakeModules/CPM.cmake b/CMakeModules/CPM.cmake new file mode 100644 index 0000000000..84748734ce --- /dev/null +++ b/CMakeModules/CPM.cmake @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: MIT +# +# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors + +set(CPM_DOWNLOAD_VERSION 0.42.0) +set(CPM_HASH_SUM "2020b4fc42dba44817983e06342e682ecfc3d2f484a581f11cc5731fbe4dce8a") + +if(CPM_SOURCE_CACHE) + set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +elseif(DEFINED ENV{CPM_SOURCE_CACHE}) + set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +else() + set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +endif() + +# Expand relative path. This is important if the provided path contains a tilde (~) +get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) + +file(DOWNLOAD + https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake + ${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM} +) + +include(${CPM_DOWNLOAD_LOCATION}) diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 0dc6f562b8..b5125b19a3 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -494,4 +494,25 @@ if (YUZU_ROOM) target_link_libraries(yuzu PRIVATE yuzu-room) endif() +# Extra deps +set(BUILD_SHARED_LIBS OFF) + +include(CPM) +set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm) + +CPMAddPackage( + URI "gh:stachenov/quazip@1.5" + PATCHES + ${CMAKE_SOURCE_DIR}/.ci/patch/0001-quazip-strict.patch +) + +if (NOT MSVC) + target_compile_options(QuaZip PRIVATE + -Wno-error=shadow + -Wno-error=missing-declarations + ) +endif() + +target_link_libraries(yuzu PRIVATE QuaZip::QuaZip) + create_target_directory_groups(yuzu) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 081f08cf52..634c11bdce 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -12,6 +12,8 @@ #include "core/tools/renderdoc.h" #include "frontend_common/firmware_manager.h" +#include + #ifdef __APPLE__ #include // for chdir #endif @@ -1683,7 +1685,8 @@ void GMainWindow::ConnectMenuEvents() { connect_menu(ui->action_Discord, &GMainWindow::OnOpenDiscord); connect_menu(ui->action_Verify_installed_contents, &GMainWindow::OnVerifyInstalledContents); - connect_menu(ui->action_Install_Firmware, &GMainWindow::OnInstallFirmware); + connect_menu(ui->action_Firmware_From_Folder, &GMainWindow::OnInstallFirmware); + connect_menu(ui->action_Firmware_From_ZIP, &GMainWindow::OnInstallFirmwareFromZIP); connect_menu(ui->action_Install_Keys, &GMainWindow::OnInstallDecryptionKeys); connect_menu(ui->action_About, &GMainWindow::OnAbout); } @@ -1714,7 +1717,8 @@ void GMainWindow::UpdateMenuState() { action->setEnabled(emulation_running); } - ui->action_Install_Firmware->setEnabled(!emulation_running); + ui->action_Firmware_From_Folder->setEnabled(!emulation_running); + ui->action_Firmware_From_ZIP->setEnabled(!emulation_running); ui->action_Install_Keys->setEnabled(!emulation_running); for (QAction* action : applet_actions) { @@ -4239,26 +4243,8 @@ 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 eden before attempting to install firmware.")); - return; - } - - const QString firmware_source_location = QFileDialog::getExistingDirectory( - this, tr("Select Dumped Firmware Source Location"), {}, QFileDialog::ShowDirsOnly); - if (firmware_source_location.isEmpty()) { - return; - } - +void GMainWindow::InstallFirmware(const QString &location, bool recursive) +{ QProgressDialog progress(tr("Installing Firmware..."), tr("Cancel"), 0, 100, this); progress.setWindowModality(Qt::WindowModal); progress.setMinimumDuration(100); @@ -4272,11 +4258,11 @@ void GMainWindow::OnInstallFirmware() { return progress.wasCanceled(); }; - LOG_INFO(Frontend, "Installing firmware from {}", firmware_source_location.toStdString()); + LOG_INFO(Frontend, "Installing firmware from {}", 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(); + std::filesystem::path firmware_source_path = location.toStdString(); if (!Common::FS::IsDir(firmware_source_path)) { progress.close(); return; @@ -4294,7 +4280,12 @@ void GMainWindow::OnInstallFirmware() { QtProgressCallback(100, 10); - Common::FS::IterateDirEntries(firmware_source_path, callback, Common::FS::DirEntryFilter::File); + if (recursive) { + Common::FS::IterateDirEntriesRecursively(firmware_source_path, callback, Common::FS::DirEntryFilter::File); + } else { + Common::FS::IterateDirEntries(firmware_source_path, callback, Common::FS::DirEntryFilter::File); + } + if (out.size() <= 0) { progress.close(); QMessageBox::warning(this, tr("Firmware install failed"), @@ -4377,6 +4368,93 @@ void GMainWindow::OnInstallFirmware() { OnCheckFirmware(); } +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 Eden before attempting to install firmware.")); + return; + } + + const QString firmware_source_location = QFileDialog::getExistingDirectory( + this, tr("Select Dumped Firmware Source Location"), {}, QFileDialog::ShowDirsOnly); + if (firmware_source_location.isEmpty()) { + return; + } + + InstallFirmware(firmware_source_location); +} + +void GMainWindow::OnInstallFirmwareFromZIP() +{ + // 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 Eden before attempting to install firmware.")); + return; + } + + const QString firmware_zip_location = QFileDialog::getOpenFileName( + this, tr("Select Dumped Firmware ZIP"), {}, tr("Zipped Archives (*.zip)")); + if (firmware_zip_location.isEmpty()) { + return; + } + + namespace fs = std::filesystem; + fs::path tmp{std::filesystem::temp_directory_path()}; + + if (!std::filesystem::create_directories(tmp / "eden" / "firmware")) { + goto unzipFailed; + } + + { + tmp /= "eden"; + tmp /= "firmware"; + + QString qCacheDir = QString::fromStdString(tmp.string()); + + QFile zip(firmware_zip_location); + + QStringList result = JlCompress::extractDir(&zip, qCacheDir); + if (result.isEmpty()) { + goto unzipFailed; + } + + // In this case, it has to be done recursively, since sometimes people + // will pack it into a subdirectory after dumping + InstallFirmware(qCacheDir, true); + + std::error_code ec; + std::filesystem::remove_all(tmp, ec); + + if (ec) { + QMessageBox::warning(this, tr("Firmware cleanup failed"), + tr("Failed to clean up extracted firmware cache.\n" + "Check write permissions in the system temp directory and try again.\nOS reported error: %1") + .arg(ec.message())); + } + + return; + } +unzipFailed: + QMessageBox::critical(this, tr("Firmware unzip failed"), + tr("Check write permissions in the system temp directory and try again.")); + return; + +} + void GMainWindow::OnInstallDecryptionKeys() { // Don't do this while emulation is running. if (emu_thread != nullptr && emu_thread->IsRunning()) { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 1f2582098a..7e7c00ec0b 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -394,6 +394,7 @@ private slots: void OnOpenLogFolder(); void OnVerifyInstalledContents(); void OnInstallFirmware(); + void OnInstallFirmwareFromZIP(); void OnInstallDecryptionKeys(); void OnAbout(); void OnToggleFilterBar(); @@ -614,6 +615,8 @@ private: std::string arguments, const bool needs_title); + void InstallFirmware(const QString& location, bool recursive = false); + protected: void dropEvent(QDropEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index e35b7afa5a..970f8d2901 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -182,8 +182,15 @@ + + + Install Firmware + + + + - + @@ -484,11 +491,6 @@ Open &Controller Menu - - - Install Firmware - - Install Decryption Keys @@ -545,6 +547,16 @@ &Log Folder + + + + + From Folder + + + + + From ZIP diff --git a/tools/update-cpm.sh b/tools/update-cpm.sh new file mode 100755 index 0000000000..30e400209d --- /dev/null +++ b/tools/update-cpm.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +wget -O CMakeModules/CPM.cmake https://github.com/cpm-cmake/CPM.cmake/releases/latest/download/get_cpm.cmake