diff --git a/CMakeLists.txt b/CMakeLists.txt index 929e1b394..f43eed7e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -362,6 +362,15 @@ if(NOT Launcher_FORCE_BUNDLED_LIBS) # Find qrcodegencpp-cmake find_package(qrcodegencpp QUIET) + + find_package(LibArchive 3.7.8 QUIET) + # Fallback to pkg-config (if available) if CMake files aren't found + if(NOT LibArchive_FOUND) + find_package(PkgConfig) + if(PkgConfig_FOUND) + pkg_check_modules(LibArchive IMPORTED_TARGET LibArchive>=3.7.8) + endif() + endif() endif() include(ECMQtDeclareLoggingCategory) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 194694d7f..1c2042f8f 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -26,6 +26,10 @@ set(CORE_SOURCES NullInstance.h MMCZip.h MMCZip.cpp + archive/ArchiveWriter.cpp + archive/ArchiveWriter.h + archive/ExportToZipTask.cpp + archive/ExportToZipTask.h Untar.h Untar.cpp StringUtils.h @@ -1329,6 +1333,12 @@ else() target_link_libraries(Launcher_logic tomlplusplus::tomlplusplus) endif() +if(TARGET PkgConfig::LibArchive) + target_link_libraries(Launcher_logic PkgConfig::LibArchive) +else() + target_link_libraries(Launcher_logic LibArchive::LibArchive) +endif() + if (UNIX AND NOT CYGWIN AND NOT APPLE) target_link_libraries(Launcher_logic gamemode diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index 0b1a2b39e..3a077e1e5 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -35,6 +35,7 @@ */ #include "MMCZip.h" +#include #include #include #include @@ -47,19 +48,32 @@ #if defined(LAUNCHER_APPLICATION) #include +#include "archive/ArchiveWriter.h" #endif namespace MMCZip { // ours -bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet& contained, const FilterFunction& filter) +using FilterFunction = std::function; +#if defined(LAUNCHER_APPLICATION) +bool mergeZipFiles(ArchiveWriter& into, QFileInfo from, QSet& contained, const FilterFunction& filter = nullptr) { - QuaZip modZip(from.filePath()); - modZip.open(QuaZip::mdUnzip); + std::unique_ptr modZip(archive_read_new(), archive_read_free); + if (!modZip) { + qCritical() << "Failed to create archive"; + } + archive_read_support_format_all(modZip.get()); - QuaZipFile fileInsideMod(&modZip); - QuaZipFile zipOutFile(into); - for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile()) { - QString filename = modZip.getCurrentFileName(); + auto fromUtf8 = from.absoluteFilePath().toUtf8(); + if (archive_read_open_filename(modZip.get(), fromUtf8.constData(), 10240) != ARCHIVE_OK) { + qCritical() << "Failed to open file:" << from << ": " << archive_error_string(modZip.get()); + return false; + } + + archive_entry* entry; + + while (archive_read_next_header(modZip.get(), &entry) == ARCHIVE_OK) { + auto name = archive_entry_pathname(entry); + auto filename = QString::fromStdString(name); if (filter && !filter(filename)) { qDebug() << "Skipping file " << filename << " from " << from.fileName() << " - filtered"; continue; @@ -69,32 +83,16 @@ bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet& contained, const continue; } contained.insert(filename); - - if (!fileInsideMod.open(QIODevice::ReadOnly)) { - qCritical() << "Failed to open " << filename << " from " << from.fileName(); - return false; - } - - QuaZipNewInfo info_out(fileInsideMod.getActualFileName()); - - if (!zipOutFile.open(QIODevice::WriteOnly, info_out)) { - qCritical() << "Failed to open " << filename << " in the jar"; - fileInsideMod.close(); - return false; - } - if (!JlCompress::copyData(fileInsideMod, zipOutFile)) { - zipOutFile.close(); - fileInsideMod.close(); + if (!into.addFile(modZip.get(), entry)) { qCritical() << "Failed to copy data of " << filename << " into the jar"; return false; } - zipOutFile.close(); - fileInsideMod.close(); } + return true; } -bool compressDirFiles(QuaZip* zip, QString dir, QFileInfoList files, bool followSymlinks) +bool compressDirFiles(ArchiveWriter& zip, QString dir, QFileInfoList files) { QDir directory(dir); if (!directory.exists()) @@ -103,48 +101,18 @@ bool compressDirFiles(QuaZip* zip, QString dir, QFileInfoList files, bool follow for (auto e : files) { auto filePath = directory.relativeFilePath(e.absoluteFilePath()); auto srcPath = e.absoluteFilePath(); - if (followSymlinks) { - if (e.isSymLink()) { - srcPath = e.symLinkTarget(); - } else { - srcPath = e.canonicalFilePath(); - } - } - if (!JlCompress::compressFile(zip, srcPath, filePath)) + if (!zip.addFile(srcPath, filePath)) return false; } return true; } -bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks) -{ - QuaZip zip(fileCompressed); - zip.setUtf8Enabled(true); - QDir().mkpath(QFileInfo(fileCompressed).absolutePath()); - if (!zip.open(QuaZip::mdCreate)) { - FS::deletePath(fileCompressed); - return false; - } - - auto result = compressDirFiles(&zip, dir, files, followSymlinks); - - zip.close(); - if (zip.getZipError() != 0) { - FS::deletePath(fileCompressed); - return false; - } - - return result; -} - -#if defined(LAUNCHER_APPLICATION) // ours bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList& mods) { - QuaZip zipOut(targetJarPath); - zipOut.setUtf8Enabled(true); - if (!zipOut.open(QuaZip::mdCreate)) { + ArchiveWriter zipOut(targetJarPath); + if (!zipOut.open()) { FS::deletePath(targetJarPath); qCritical() << "Failed to open the minecraft.jar for modding"; return false; @@ -161,7 +129,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QListenabled()) continue; if (mod->type() == ResourceType::ZIPFILE) { - if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles)) { + if (!mergeZipFiles(zipOut, mod->fileinfo(), addedFiles)) { zipOut.close(); FS::deletePath(targetJarPath); qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; @@ -170,7 +138,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QListtype() == ResourceType::SINGLEFILE) { // FIXME: buggy - does not work with addedFiles auto filename = mod->fileinfo(); - if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) { + if (!zipOut.addFile(filename.absoluteFilePath(), filename.fileName())) { zipOut.close(); FS::deletePath(targetJarPath); qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; @@ -193,7 +161,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QListfileinfo().fileName() << "to the jar."; @@ -209,7 +177,7 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList extractSubDir(QuaZip* zip, const QString& subdir, const QString& target) { @@ -455,84 +406,6 @@ bool collectFileListRecursively(const QString& rootDir, const QString& subDir, Q } #if defined(LAUNCHER_APPLICATION) -void ExportToZipTask::executeTask() -{ - setStatus("Adding files..."); - setProgress(0, m_files.length()); - m_build_zip_future = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { return exportZip(); }); - connect(&m_build_zip_watcher, &QFutureWatcher::finished, this, &ExportToZipTask::finish); - m_build_zip_watcher.setFuture(m_build_zip_future); -} - -auto ExportToZipTask::exportZip() -> ZipResult -{ - if (!m_dir.exists()) { - return ZipResult(tr("Folder doesn't exist")); - } - if (!m_output.isOpen() && !m_output.open(QuaZip::mdCreate)) { - return ZipResult(tr("Could not create file")); - } - - for (auto fileName : m_extra_files.keys()) { - if (m_build_zip_future.isCanceled()) - return ZipResult(); - QuaZipFile indexFile(&m_output); - if (!indexFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileName))) { - return ZipResult(tr("Could not create:") + fileName); - } - indexFile.write(m_extra_files[fileName]); - } - - for (const QFileInfo& file : m_files) { - if (m_build_zip_future.isCanceled()) - return ZipResult(); - - auto absolute = file.absoluteFilePath(); - auto relative = m_dir.relativeFilePath(absolute); - setStatus("Compressing: " + relative); - setProgress(m_progress + 1, m_progressTotal); - if (m_follow_symlinks) { - if (file.isSymLink()) - absolute = file.symLinkTarget(); - else - absolute = file.canonicalFilePath(); - } - - if (!m_exclude_files.contains(relative) && !JlCompress::compressFile(&m_output, absolute, m_destination_prefix + relative)) { - return ZipResult(tr("Could not read and compress %1").arg(relative)); - } - } - - m_output.close(); - if (m_output.getZipError() != 0) { - return ZipResult(tr("A zip error occurred")); - } - return ZipResult(); -} - -void ExportToZipTask::finish() -{ - if (m_build_zip_future.isCanceled()) { - FS::deletePath(m_output_path); - emitAborted(); - } else if (auto result = m_build_zip_future.result(); result.has_value()) { - FS::deletePath(m_output_path); - emitFailed(result.value()); - } else { - emitSucceeded(); - } -} - -bool ExportToZipTask::abort() -{ - if (m_build_zip_future.isRunning()) { - m_build_zip_future.cancel(); - // NOTE: Here we don't do `emitAborted()` because it will be done when `m_build_zip_future` actually cancels, which may not occur - // immediately. - return true; - } - return false; -} void ExtractZipTask::executeTask() { diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h index fe0c79de2..452a50f4a 100644 --- a/launcher/MMCZip.h +++ b/launcher/MMCZip.h @@ -55,34 +55,8 @@ #include "tasks/Task.h" namespace MMCZip { -using FilterFunction = std::function; using FilterFileFunction = std::function; -/** - * Merge two zip files, using a filter function - */ -bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet& contained, const FilterFunction& filter = nullptr); - -/** - * Compress directory, by providing a list of files to compress - * \param zip target archive - * \param dir directory that will be compressed (to compress with relative paths) - * \param files list of files to compress - * \param followSymlinks should follow symlinks when compressing file data - * \return true for success or false for failure - */ -bool compressDirFiles(QuaZip* zip, QString dir, QFileInfoList files, bool followSymlinks = false); - -/** - * Compress directory, by providing a list of files to compress - * \param fileCompressed target archive file - * \param dir directory that will be compressed (to compress with relative paths) - * \param files list of files to compress - * \param followSymlinks should follow symlinks when compressing file data - * \return true for success or false for failure - */ -bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false); - #if defined(LAUNCHER_APPLICATION) /** * take a source jar, add mods to it, resulting in target jar @@ -98,14 +72,6 @@ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList; - - protected: - virtual void executeTask() override; - bool abort() override; - - ZipResult exportZip(); - void finish(); - - private: - QString m_output_path; - QuaZip m_output; - QDir m_dir; - QFileInfoList m_files; - QString m_destination_prefix; - bool m_follow_symlinks; - QStringList m_exclude_files; - QHash m_extra_files; - - QFuture m_build_zip_future; - QFutureWatcher m_build_zip_watcher; -}; class ExtractZipTask : public Task { Q_OBJECT diff --git a/launcher/archive/ArchiveWriter.cpp b/launcher/archive/ArchiveWriter.cpp new file mode 100644 index 000000000..cfae8a8ff --- /dev/null +++ b/launcher/archive/ArchiveWriter.cpp @@ -0,0 +1,211 @@ + +#include "ArchiveWriter.h" +#include + +#include +#include + +#include + +namespace MMCZip { + +ArchiveWriter::ArchiveWriter(const QString& archiveName) : m_filename(archiveName) {} + +ArchiveWriter::~ArchiveWriter() +{ + close(); +} + +bool ArchiveWriter::open() +{ + if (m_filename.isEmpty()) { + qCritical() << "Archive m_filename not set."; + return false; + } + + m_archive = archive_write_new(); + if (!m_archive) { + qCritical() << "Archive not initialized."; + return false; + } + + QString lowerName = m_filename.toLower(); + if (lowerName.endsWith(".tar.gz") || lowerName.endsWith(".tgz")) { + archive_write_set_format_pax_restricted(m_archive); + archive_write_add_filter_gzip(m_archive); + } else if (lowerName.endsWith(".tar.bz2") || lowerName.endsWith(".tbz")) { + archive_write_set_format_pax_restricted(m_archive); + archive_write_add_filter_bzip2(m_archive); + } else if (lowerName.endsWith(".tar.xz") || lowerName.endsWith(".txz")) { + archive_write_set_format_pax_restricted(m_archive); + archive_write_add_filter_xz(m_archive); + } else if (lowerName.endsWith(".zip") || lowerName.endsWith(".jar")) { + archive_write_set_format_zip(m_archive); + } else if (lowerName.endsWith(".tar")) { + archive_write_set_format_pax_restricted(m_archive); + } else { + qCritical() << "Unknown archive format:" << m_filename; + return false; + } + + auto archiveNameUtf8 = m_filename.toUtf8(); + if (archive_write_open_filename(m_archive, archiveNameUtf8.constData()) != ARCHIVE_OK) { + qCritical() << "Failed to open archive file:" << m_filename << "-" << archive_error_string(m_archive); + return false; + } + + return true; +} + +bool ArchiveWriter::close() +{ + bool success = true; + if (m_archive) { + if (archive_write_close(m_archive) != ARCHIVE_OK) { + qCritical() << "Failed to close archive" << m_filename << "-" << archive_error_string(m_archive); + success = false; + } + if (archive_write_free(m_archive) != ARCHIVE_OK) { + qCritical() << "Failed to free archive" << m_filename << "-" << archive_error_string(m_archive); + success = false; + } + m_archive = nullptr; + } + return success; +} + +bool ArchiveWriter::addFile(const QString& fileName, const QString& fileDest) +{ + QFileInfo fileInfo(fileName); + if (!fileInfo.exists()) { + qCritical() << "File does not exists:" << fileInfo.filePath(); + return false; + } + + std::unique_ptr entry_ptr(archive_entry_new(), archive_entry_free); + auto entry = entry_ptr.get(); + if (!entry) { + qCritical() << "Failed to create archive entry"; + return false; + } + + auto fileDestUtf8 = fileDest.toUtf8(); + archive_entry_set_pathname(entry, fileDestUtf8.constData()); + archive_entry_set_perm(entry, fileInfo.permissions() & 0777); + + if (fileInfo.isSymLink()) { + auto target = fileInfo.symLinkTarget().toUtf8(); + archive_entry_set_filetype(entry, AE_IFLNK); + archive_entry_set_symlink(entry, target.constData()); + archive_entry_set_size(entry, 0); + + if (archive_write_header(m_archive, entry) != ARCHIVE_OK) { + qCritical() << "Failed to write symlink header for:" << fileDest << "-" << archive_error_string(m_archive); + return false; + } + return true; + } + + if (!fileInfo.isFile()) { + qCritical() << "Unsupported file type:" << fileInfo.filePath(); + return false; + } + + QFile file(fileInfo.absoluteFilePath()); + if (!file.open(QIODevice::ReadOnly)) { + qCritical() << "Failed to open file: " << fileInfo.filePath(); + return false; + } + + archive_entry_set_filetype(entry, AE_IFREG); + archive_entry_set_size(entry, file.size()); + + if (archive_write_header(m_archive, entry) != ARCHIVE_OK) { + qCritical() << "Failed to write header for: " << fileDest; + return false; + } + + constexpr qint64 chunkSize = 8192; + QByteArray buffer; + buffer.resize(chunkSize); + + while (!file.atEnd()) { + auto bytesRead = file.read(buffer.data(), chunkSize); + if (bytesRead < 0) { + qCritical() << "Read error in file: " << fileInfo.filePath(); + return false; + } + + if (archive_write_data(m_archive, buffer.constData(), bytesRead) < 0) { + qCritical() << "Write error in archive for: " << fileDest; + return false; + } + } + return true; +} + +bool ArchiveWriter::addFile(const QString& fileDest, const QByteArray& data) +{ + std::unique_ptr entry_ptr(archive_entry_new(), archive_entry_free); + auto entry = entry_ptr.get(); + if (!entry) { + qCritical() << "Failed to create archive entry"; + return false; + } + + auto fileDestUtf8 = fileDest.toUtf8(); + archive_entry_set_pathname(entry, fileDestUtf8.constData()); + archive_entry_set_perm(entry, 0644); + + archive_entry_set_filetype(entry, AE_IFREG); + archive_entry_set_size(entry, data.size()); + + if (archive_write_header(m_archive, entry) != ARCHIVE_OK) { + qCritical() << "Failed to write header for: " << fileDest; + return false; + } + + if (archive_write_data(m_archive, data.constData(), data.size()) < 0) { + qCritical() << "Write error in archive for: " << fileDest; + return false; + } + return true; +} + +bool ArchiveWriter::addFile(archive* src, archive_entry* entry) +{ + if (!src) { + qCritical() << "Invalid source archive"; + return false; + } + if (!entry) { // if is empty read next header + if (auto r = archive_read_next_header(src, &entry); r == ARCHIVE_EOF) { + return false; + } else if (r != ARCHIVE_OK) { + qCritical() << "Failed to read entry from source archive:" << archive_error_string(src); + return false; + } + } + + if (archive_write_header(m_archive, entry) != ARCHIVE_OK) { + qCritical() << "Failed to write header to entry:" << archive_entry_pathname(entry) << "-" << archive_error_string(m_archive); + return false; + } + const void* buff; + size_t size; + la_int64_t offset; + + int status; + while ((status = archive_read_data_block(src, &buff, &size, &offset)) == ARCHIVE_OK) { + if (archive_write_data(m_archive, buff, size) < 0) { + qCritical() << "Failed writing data block:" << archive_error_string(m_archive); + return false; + } + } + if (status != ARCHIVE_EOF) { + qCritical() << "Failed reading data block:" << archive_error_string(m_archive); + return false; + } + return true; +} +} // namespace MMCZip \ No newline at end of file diff --git a/launcher/archive/ArchiveWriter.h b/launcher/archive/ArchiveWriter.h new file mode 100644 index 000000000..e8b5ca348 --- /dev/null +++ b/launcher/archive/ArchiveWriter.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +#include +#include + +namespace MMCZip { + +class ArchiveWriter { + public: + ArchiveWriter(const QString& archiveName); + ~ArchiveWriter(); + + bool open(); + bool close(); + + bool addFile(const QString& fileName, const QString& fileDest); + bool addFile(const QString& fileDest, const QByteArray& data); + bool addFile(archive* src, archive_entry* entry = nullptr); + + private: + struct archive* m_archive = nullptr; + QString m_filename; +}; +} // namespace MMCZip \ No newline at end of file diff --git a/launcher/archive/ExportToZipTask.cpp b/launcher/archive/ExportToZipTask.cpp new file mode 100644 index 000000000..b85bfd896 --- /dev/null +++ b/launcher/archive/ExportToZipTask.cpp @@ -0,0 +1,84 @@ + +#include "ExportToZipTask.h" + +#include + +#include "FileSystem.h" + +namespace MMCZip { +void ExportToZipTask::executeTask() +{ + setStatus("Adding files..."); + setProgress(0, m_files.length()); + m_build_zip_future = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { return exportZip(); }); + connect(&m_build_zip_watcher, &QFutureWatcher::finished, this, &ExportToZipTask::finish); + m_build_zip_watcher.setFuture(m_build_zip_future); +} + +auto ExportToZipTask::exportZip() -> ZipResult +{ + if (!m_dir.exists()) { + return ZipResult(tr("Folder doesn't exist")); + } + if (!m_output.open()) { + return ZipResult(tr("Could not create file")); + } + + for (auto fileName : m_extra_files.keys()) { + if (m_build_zip_future.isCanceled()) + return ZipResult(); + if (!m_output.addFile(fileName, m_extra_files[fileName])) { + return ZipResult(tr("Could not add:") + fileName); + } + } + + for (const QFileInfo& file : m_files) { + if (m_build_zip_future.isCanceled()) + return ZipResult(); + + auto absolute = file.absoluteFilePath(); + auto relative = m_dir.relativeFilePath(absolute); + setStatus("Compressing: " + relative); + setProgress(m_progress + 1, m_progressTotal); + if (m_follow_symlinks) { + if (file.isSymLink()) + absolute = file.symLinkTarget(); + else + absolute = file.canonicalFilePath(); + } + + if (!m_exclude_files.contains(relative) && !m_output.addFile(absolute, m_destination_prefix + relative)) { + return ZipResult(tr("Could not read and compress %1").arg(relative)); + } + } + + if (!m_output.close()) { + return ZipResult(tr("A zip error occurred")); + } + return ZipResult(); +} + +void ExportToZipTask::finish() +{ + if (m_build_zip_future.isCanceled()) { + FS::deletePath(m_output_path); + emitAborted(); + } else if (auto result = m_build_zip_future.result(); result.has_value()) { + FS::deletePath(m_output_path); + emitFailed(result.value()); + } else { + emitSucceeded(); + } +} + +bool ExportToZipTask::abort() +{ + if (m_build_zip_future.isRunning()) { + m_build_zip_future.cancel(); + // NOTE: Here we don't do `emitAborted()` because it will be done when `m_build_zip_future` actually cancels, which may not occur + // immediately. + return true; + } + return false; +} +} // namespace MMCZip \ No newline at end of file diff --git a/launcher/archive/ExportToZipTask.h b/launcher/archive/ExportToZipTask.h new file mode 100644 index 000000000..12fbdcd2b --- /dev/null +++ b/launcher/archive/ExportToZipTask.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include +#include + +#include "ArchiveWriter.h" +#include "tasks/Task.h" + +namespace MMCZip { +class ExportToZipTask : public Task { + Q_OBJECT + public: + ExportToZipTask(QString outputPath, QDir dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false) + : m_output_path(outputPath) + , m_output(outputPath) + , m_dir(dir) + , m_files(files) + , m_destination_prefix(destinationPrefix) + , m_follow_symlinks(followSymlinks) + { + setAbortable(true); + // m_output.setUtf8Enabled(utf8Enabled); // ignore for now + // need to test: + // - https://github.com/PrismLauncher/PrismLauncher/pull/2225 + // - https://github.com/PrismLauncher/PrismLauncher/pull/2353 + }; + ExportToZipTask(QString outputPath, QString dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false) + : ExportToZipTask(outputPath, QDir(dir), files, destinationPrefix, followSymlinks) {}; + + virtual ~ExportToZipTask() = default; + + void setExcludeFiles(QStringList excludeFiles) { m_exclude_files = excludeFiles; } + void addExtraFile(QString fileName, QByteArray data) { m_extra_files.insert(fileName, data); } + + using ZipResult = std::optional; + + protected: + virtual void executeTask() override; + bool abort() override; + + ZipResult exportZip(); + void finish(); + + private: + QString m_output_path; + ArchiveWriter m_output; + QDir m_dir; + QFileInfoList m_files; + QString m_destination_prefix; + bool m_follow_symlinks; + QStringList m_exclude_files; + QHash m_extra_files; + + QFuture m_build_zip_future; + QFutureWatcher m_build_zip_watcher; +}; +} // namespace MMCZip \ No newline at end of file diff --git a/launcher/modplatform/flame/FlamePackExportTask.cpp b/launcher/modplatform/flame/FlamePackExportTask.cpp index 900fd1a87..5f03b69f4 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.cpp +++ b/launcher/modplatform/flame/FlamePackExportTask.cpp @@ -31,6 +31,7 @@ #include "Application.h" #include "Json.h" #include "MMCZip.h" +#include "archive/ExportToZipTask.h" #include "minecraft/PackProfile.h" #include "minecraft/mod/ModFolderModel.h" #include "modplatform/ModIndex.h" @@ -318,7 +319,7 @@ void FlamePackExportTask::buildZip() setStatus(tr("Adding files...")); setProgress(4, 5); - auto zipTask = makeShared(m_options.output, m_gameRoot, files, "overrides/", true, false); + auto zipTask = makeShared(m_options.output, m_gameRoot, files, "overrides/", true); zipTask->addExtraFile("manifest.json", generateIndex()); zipTask->addExtraFile("modlist.html", generateHTML()); diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp index 9ee4101e6..c638e8db0 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp @@ -25,6 +25,7 @@ #include #include "Json.h" #include "MMCZip.h" +#include "archive/ExportToZipTask.h" #include "minecraft/PackProfile.h" #include "minecraft/mod/MetadataHandler.h" #include "minecraft/mod/ModFolderModel.h" @@ -200,7 +201,7 @@ void ModrinthPackExportTask::buildZip() { setStatus(tr("Adding files...")); - auto zipTask = makeShared(output, gameRoot, files, "overrides/", true, true); + auto zipTask = makeShared(output, gameRoot, files, "overrides/", true); zipTask->addExtraFile("modrinth.index.json", generateIndex()); zipTask->setExcludeFiles(resolvedFiles.keys()); diff --git a/launcher/ui/dialogs/ExportInstanceDialog.cpp b/launcher/ui/dialogs/ExportInstanceDialog.cpp index 8d98b0513..21c16f01a 100644 --- a/launcher/ui/dialogs/ExportInstanceDialog.cpp +++ b/launcher/ui/dialogs/ExportInstanceDialog.cpp @@ -43,6 +43,7 @@ #include #include "FileIgnoreProxy.h" #include "QObjectPtr.h" +#include "archive/ExportToZipTask.h" #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/ProgressDialog.h" #include "ui_ExportInstanceDialog.h" @@ -150,7 +151,7 @@ void ExportInstanceDialog::doExport() return; } - auto task = makeShared(output, m_instance->instanceRoot(), files, "", true, true); + auto task = makeShared(output, m_instance->instanceRoot(), files, "", true); connect(task.get(), &Task::failed, this, [this, output](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); diff --git a/vcpkg.json b/vcpkg.json index 4abf8d1b7..35dd3d67f 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -2,9 +2,16 @@ "dependencies": [ "bzip2", "cmark", - { "name": "ecm", "host": true }, - { "name": "pkgconf", "host": true }, + { + "name": "ecm", + "host": true + }, + { + "name": "pkgconf", + "host": true + }, "tomlplusplus", - "zlib" + "zlib", + "libarchive" ] }