move some functions from MMCZip to use libarchive

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
This commit is contained in:
Trial97
2025-07-10 13:57:14 +03:00
parent fa930afe4b
commit 60b2585711
12 changed files with 383 additions and 252 deletions

View File

@@ -0,0 +1,170 @@
#include "ArchiveReader.h"
#include <archive_entry.h>
namespace MMCZip {
QStringList ArchiveReader::getFiles()
{
return m_fileNames;
}
bool ArchiveReader::collectFiles(bool onlyFiles)
{
return parse([this, onlyFiles](File* f) {
if (!onlyFiles || f->isFile())
m_fileNames << f->filename();
return f->skip();
});
}
QString ArchiveReader::File::filename()
{
return QString::fromUtf8(archive_entry_pathname(m_entry));
}
QByteArray ArchiveReader::File::readAll(int* outStatus)
{
QByteArray data;
const void* buff;
size_t size;
la_int64_t offset;
int status;
while ((status = archive_read_data_block(m_archive.get(), &buff, &size, &offset)) == ARCHIVE_OK) {
data.append(static_cast<const char*>(buff), static_cast<qsizetype>(size));
}
if (status != ARCHIVE_EOF && status != ARCHIVE_OK) {
qWarning() << "libarchive read error: " << archive_error_string(m_archive.get());
}
if (outStatus) {
*outStatus = status;
}
archive_read_close(m_archive.get());
return data;
}
QDateTime ArchiveReader::File::dateTime()
{
auto mtime = archive_entry_mtime(m_entry);
auto mtime_nsec = archive_entry_mtime_nsec(m_entry);
auto dt = QDateTime::fromSecsSinceEpoch(mtime);
return dt.addMSecs(mtime_nsec / 1e6);
}
int ArchiveReader::File::readNextHeader()
{
return archive_read_next_header(m_archive.get(), &m_entry);
}
auto ArchiveReader::goToFile(QString filename) -> std::unique_ptr<File>
{
auto f = std::make_unique<File>();
auto a = f->m_archive.get();
archive_read_support_format_all(a);
archive_read_support_filter_all(a);
auto fileName = m_archivePath.toUtf8();
if (archive_read_open_filename(a, fileName.constData(), 10240) != ARCHIVE_OK) {
qCritical() << "Failed to open archive file:" << m_archivePath << "-" << archive_error_string(a);
return nullptr;
}
while (f->readNextHeader() == ARCHIVE_OK) {
if (f->filename() == filename) {
return f;
}
f->skip();
}
archive_read_close(a);
return nullptr;
}
static int copy_data(struct archive* ar, struct archive* aw, bool notBlock = false)
{
int r;
const void* buff;
size_t size;
la_int64_t offset;
for (;;) {
r = archive_read_data_block(ar, &buff, &size, &offset);
if (r == ARCHIVE_EOF)
return (ARCHIVE_OK);
if (r < ARCHIVE_OK) {
qCritical() << "Failed reading data block:" << archive_error_string(ar);
return (r);
}
if (notBlock) {
r = archive_write_data(aw, buff, size);
} else {
r = archive_write_data_block(aw, buff, size, offset);
}
if (r < ARCHIVE_OK) {
qCritical() << "Failed writing data block:" << archive_error_string(aw);
return (r);
}
}
}
bool ArchiveReader::File::writeFile(archive* out, QString targetFileName, bool notBlock)
{
auto entry = m_entry;
if (!targetFileName.isEmpty()) {
entry = archive_entry_clone(m_entry);
auto nameUtf8 = targetFileName.toUtf8();
archive_entry_set_pathname(entry, nameUtf8.constData());
}
if (archive_write_header(out, entry) < ARCHIVE_OK) {
qCritical() << "Failed to write header to entry:" << filename() << "-" << archive_error_string(out);
return false;
} else if (archive_entry_size(m_entry) > 0) {
auto r = copy_data(m_archive.get(), out, notBlock);
if (r < ARCHIVE_OK)
qCritical() << "Failed reading data block:" << archive_error_string(out);
if (r < ARCHIVE_WARN)
return false;
}
auto r = archive_write_finish_entry(out);
if (r < ARCHIVE_OK)
qCritical() << "Failed dinish entry:" << archive_error_string(out);
return (r > ARCHIVE_WARN);
}
bool ArchiveReader::parse(std::function<bool(File*)> doStuff)
{
auto f = std::make_unique<File>();
auto a = f->m_archive.get();
archive_read_support_format_all(a);
archive_read_support_filter_all(a);
auto fileName = m_archivePath.toUtf8();
if (archive_read_open_filename(a, fileName.constData(), 10240) != ARCHIVE_OK) {
qCritical() << "Failed to open archive file:" << m_archivePath << "-" << f->error();
return false;
}
while (f->readNextHeader() == ARCHIVE_OK) {
if (!doStuff(f.get())) {
qCritical() << "Failed to parse file:" << f->filename() << "-" << f->error();
return false;
}
}
archive_read_close(a);
return true;
}
bool ArchiveReader::File::isFile()
{
return (archive_entry_filetype(m_entry) & AE_IFMT) == AE_IFREG;
}
bool ArchiveReader::File::skip()
{
return archive_read_data_skip(m_archive.get()) == ARCHIVE_OK;
}
const char* ArchiveReader::File::error()
{
return archive_error_string(m_archive.get());
}
QString ArchiveReader::getZipName()
{
return m_archivePath;
}
} // namespace MMCZip

View File

@@ -0,0 +1,53 @@
#pragma once
#include <archive.h>
#include <archive_entry.h>
#include <QByteArray>
#include <QDateTime>
#include <QStringList>
#include <memory>
namespace MMCZip {
class ArchiveReader {
public:
using ArchivePtr = std::unique_ptr<struct archive, int (*)(struct archive*)>;
ArchiveReader(QString fileName) : m_archivePath(fileName) {}
virtual ~ArchiveReader() = default;
QStringList getFiles();
QString getZipName();
bool collectFiles(bool onlyFiles = true);
class File {
public:
File() : m_archive(ArchivePtr(archive_read_new(), archive_read_free)) {}
virtual ~File() {}
QString filename();
bool isFile();
QDateTime dateTime();
const char* error();
QByteArray readAll(int* outStatus = nullptr);
bool skip();
bool writeFile(archive* out, QString targetFileName = "", bool notBlock = false);
private:
int readNextHeader();
private:
friend ArchiveReader;
ArchivePtr m_archive;
archive_entry* m_entry;
};
std::unique_ptr<File> goToFile(QString filename);
bool parse(std::function<bool(File*)>);
private:
QString m_archivePath;
QStringList m_fileNames = {};
};
} // namespace MMCZip

View File

@@ -172,40 +172,8 @@ bool ArchiveWriter::addFile(const QString& fileDest, const QByteArray& data)
return true;
}
bool ArchiveWriter::addFile(archive* src, archive_entry* entry)
bool ArchiveWriter::addFile(ArchiveReader::File* f)
{
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;
return f->writeFile(m_archive, "", true);
}
} // namespace MMCZip

View File

@@ -5,20 +5,21 @@
#include <QByteArray>
#include <QFileDevice>
#include "archive/ArchiveReader.h"
namespace MMCZip {
class ArchiveWriter {
public:
ArchiveWriter(const QString& archiveName);
~ArchiveWriter();
virtual ~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);
bool addFile(ArchiveReader::File* f);
private:
struct archive* m_archive = nullptr;

View File

@@ -21,10 +21,6 @@ class ExportToZipTask : public Task {
, 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) {};