Add libarchive decompression backend

This commit is contained in:
BEN ENGLISCH 2021-11-19 23:06:38 -06:00 committed by Luis Ángel San Martín
parent a0dfa4e447
commit 862c220069
14 changed files with 288 additions and 14 deletions

3
.gitignore vendored
View File

@ -67,3 +67,6 @@ YACReaderLibrary/YACReaderLibrary\.xcodeproj/
.DS_Store
compressed_archive/libp7zip
c_x86_64.pch
compile_commands.json
.ccls-cache

View File

@ -156,13 +156,16 @@ SOURCES += ../common/comic.cpp \
}
include(../custom_widgets/custom_widgets_yacreader.pri)
CONFIG(7zip){
CONFIG(7zip) {
include(../compressed_archive/wrapper.pri)
} else:CONFIG(unarr){
} else:CONFIG(unarr) {
include(../compressed_archive/unarr/unarr-wrapper.pri)
} else:CONFIG(libarchive) {
include(../compressed_archive/libarchive/libarchive-wrapper.pri)
} else {
error(No compression backend specified. Did you mess with the build system?)
}
}
include(../shortcuts_management/shortcuts_management.pri)
RESOURCES += yacreader_images.qrc \

View File

@ -250,6 +250,8 @@ CONFIG(7zip){
include(../compressed_archive/wrapper.pri)
} else:CONFIG(unarr) {
include(../compressed_archive/unarr/unarr-wrapper.pri)
} else:CONFIG(libarchive) {
include(../compressed_archive/libarchive/libarchive-wrapper.pri)
} else {
error(No compression backend specified. Did you mess with the build system?)
}

View File

@ -108,7 +108,7 @@ void LibraryCreator::processLibrary(const QString &source, const QString &target
void LibraryCreator::run()
{
stopRunning = false;
#ifndef use_unarr
#if !defined use_unarr && !defined use_libarchive
// check for 7z lib
#if defined Q_OS_UNIX && !defined Q_OS_MAC
QLibrary *sevenzLib = new QLibrary(QString(LIBDIR) + "/p7zip/7z.so");

View File

@ -7,7 +7,7 @@
#include <QDir>
#include <QSysInfo>
#include <QFileInfo>
#ifndef use_unarr
#if !defined use_unarr && !defined use_libarchive
#include <QLibrary>
#endif
#include <QCommandLineParser>
@ -40,7 +40,7 @@ void logSystemAndConfig()
QLOG_INFO() << "OS:" << QSysInfo::prettyProductName() << "Version: " << QSysInfo::productVersion();
QLOG_INFO() << "Kernel:" << QSysInfo::kernelType() << QSysInfo::kernelVersion() << "Architecture:" << QSysInfo::currentCpuArchitecture();
#ifndef use_unarr
#if !defined use_unarr && !defined use_libarchive
#ifdef Q_OS_WIN
if (QLibrary::isLibrary(QApplication::applicationDirPath() + "/utils/7z.dll"))
#elif defined Q_OS_UNIX && !defined Q_OS_MAC
@ -51,6 +51,8 @@ void logSystemAndConfig()
QLOG_INFO() << "7z : found";
else
QLOG_ERROR() << "7z : not found";
#elif defined use_libarchive
QLOG_INFO() << "using libarchive decompression backend";
#else // use_unarr
QLOG_INFO() << "using unarr decompression backend";
#endif // use_unarr

View File

@ -28,7 +28,7 @@ void XMLInfoLibraryScanner::scanLibrary(const QString &source, const QString &ta
void XMLInfoLibraryScanner::run()
{
#ifndef use_unarr
#if !defined use_unarr && !defined use_libarchive
// check for 7z lib
#if defined Q_OS_UNIX && !defined Q_OS_MAC
QLibrary *sevenzLib = new QLibrary(QString(LIBDIR) + "/p7zip/7z.so");

View File

@ -94,10 +94,13 @@ SOURCES += ../YACReaderLibrary/library_creator.cpp \
libraries_updater.cpp
include(../YACReaderLibrary/server/server.pri)
CONFIG(7zip) {
include(../compressed_archive/wrapper.pri)
} else:CONFIG(unarr) {
include(../compressed_archive/unarr/unarr-wrapper.pri)
} else:CONFIG(libarchive) {
include(../compressed_archive/libarchive/libarchive-wrapper.pri)
} else {
error(No compression backend specified. Did you mess with the build system?)
}

View File

@ -0,0 +1,24 @@
* Introduction
TODO
* Using
qmake CONFIG+=libarchive
* Supported Archives
TODO
* Limitations
TODO
v4 solid archives, slow 7z, etc.
libarchive is stream based so it reads the archive in linear order. talk about
advantages of properly ordered archives (1.jpg..10.jpg vs 01.jpg..10.jpg)
* Future Work
fallback to unarr for v4 solid archives?

View File

@ -0,0 +1,155 @@
#include "compressed_archive.h"
#define archive_error(msg) msg << ": [" << archive_errno(a) << "]" << archive_error_string(a)
CompressedArchive::CompressedArchive(const QString &filePath, QObject *parent)
: QObject(parent), a(nullptr), num_entries(0), valid(false), idx(0), filename(filePath)
{
if (!open_archive()) {
qWarning() << "error opening archive:" << filename;
return;
}
archive_entry *entry;
int result;
while ((result = archive_read_next_header(a, &entry)) == ARCHIVE_OK) {
entries.append(archive_entry_pathname(entry));
archive_read_data_skip(a);
idx++;
}
num_entries = entries.size();
if (result != ARCHIVE_EOF) {
qDebug() << "finished reading archive with result of:" << result;
qWarning() << archive_error("error reading archive");
} else if (num_entries == 0) {
qWarning() << "no entries read from archive.";
} else {
qDebug() << "# of pages in archive:" << num_entries;
valid = true;
}
close_archive();
}
CompressedArchive::~CompressedArchive()
{
close_archive();
}
bool CompressedArchive::open_archive()
{
qDebug() << "opening archive:" << filename;
idx = 0;
if (a != nullptr) {
close_archive();
}
a = archive_read_new();
archive_read_support_format_all(a);
archive_read_support_filter_all(a);
if (archive_read_open_filename(a, filename.toStdString().c_str(), 10240) != ARCHIVE_OK) {
qWarning() << archive_error("error opening archive");
close_archive();
return false;
}
return true;
}
void CompressedArchive::close_archive()
{
qDebug() << "closing archive.";
archive_read_free(a);
a = nullptr;
}
bool CompressedArchive::archive_seek(quint32 index)
{
if (idx == index) {
return true;
}
// libarchive uses a streaming architecture so we cannot read files after our current position.
// because of this, when we need to seek to an index before our current position,
// we must reopen the archive.
if (idx > index) {
qDebug() << "asked for index [" << index << "] less than position [" << idx << "]."
<< "reopening archive.";
close_archive();
if (!open_archive()) {
return false;
}
}
qDebug() << "current pos = [" << idx << "] seeking to [" << index << "]";
archive_entry *entry;
for (; idx < index; idx++) {
if (archive_read_next_header(a, &entry) != ARCHIVE_OK) {
qWarning() << archive_error("error reading header");
return false;
}
if (archive_read_data_skip(a) != ARCHIVE_OK) {
qWarning() << archive_error("error skipping data");
return false;
}
}
return true;
}
void CompressedArchive::getAllData(const QVector<quint32> &indexes,
ExtractDelegate *delegate)
{
qDebug() << "called getAllData: [" << indexes << "]";
if (indexes.isEmpty())
return;
for (int i = 0; i < indexes.count(); i++) {
if (delegate == nullptr || delegate->isCancelled())
return;
quint32 index = indexes[i];
QByteArray bytes = getRawDataAtIndex(index);
if (bytes.size() > 0) {
delegate->fileExtracted(index, bytes);
} else {
qWarning() << "getAllData error at index: [" << index << "]";
delegate->unknownError(index);
return;
}
}
}
QByteArray CompressedArchive::read_entry()
{
QByteArray bytes;
archive_entry *entry;
if (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
int64_t size = archive_entry_size(entry);
bytes.resize(size);
archive_read_data(a, bytes.data(), size);
} else {
qWarning() << archive_error("error reading entry");
}
idx++;
return bytes;
}
QByteArray CompressedArchive::getRawDataAtIndex(int index)
{
QByteArray bytes;
if (archive_seek(index)) {
bytes = read_entry();
} else {
qWarning() << "error reading data from archive. index:" << index;
}
return bytes;
}

View File

@ -0,0 +1,44 @@
#ifndef COMPRESSED_ARCHIVE_H
#define COMPRESSED_ARCHIVE_H
#include "extract_delegate.h"
#include <QObject>
#include <QDebug>
extern "C" {
#include <archive.h>
#include <archive_entry.h>
}
class CompressedArchive : public QObject
{
Q_OBJECT
public:
explicit CompressedArchive(const QString &filePath, QObject *parent = nullptr);
~CompressedArchive() override;
public slots:
void getAllData(const QVector<quint32> &indexes, ExtractDelegate *delegate = nullptr);
QByteArray getRawDataAtIndex(int index);
int getNumFiles() { return num_entries; }
QList<QString> getFileNames() { return entries; }
bool isValid() { return valid; }
bool toolsLoaded() { return true; }
private:
archive *a;
QStringList entries;
int num_entries;
bool valid;
quint32 idx;
QString filename;
bool open_archive();
void close_archive();
bool archive_seek(quint32 index);
QByteArray read_entry();
};
#endif // COMPRESSED_ARCHIVE_H

View File

@ -0,0 +1,15 @@
#ifndef EXTRACT_DELEGATE_H
#define EXTRACT_DELEGATE_H
#include <QByteArray>
class ExtractDelegate
{
public:
virtual void fileExtracted(int index, const QByteArray &rawData) = 0;
virtual void crcError(int index) = 0;
virtual void unknownError(int index) = 0;
virtual bool isCancelled() = 0;
};
#endif // EXTRACT_DELEGATE_H

View File

@ -0,0 +1,22 @@
INCLUDEPATH += $$PWD
DEPENDPATH += $$PWD
HEADERS += $$PWD/extract_delegate.h \
$$PWD/compressed_archive.h
SOURCES += $$PWD/compressed_archive.cpp
if(mingw|unix):!contains(QT_CONFIG, no-pkg-config):packagesExist(libarchive) {
message(Using system provided libarchive installation found by pkg-config.)
CONFIG += link_pkgconfig
PKGCONFIG += libarchive
DEFINES += use_libarchive
}
else:unix:exists(/usr/include/archive.h) {
message(Using system provided libarchive installation.)
LIBS += -larchive
DEFINES += use_libarchive
}
else {
error(Missing dependency: libarchive decompression backend. Please install libarchive on your system)
}

View File

@ -56,7 +56,7 @@ CONFIG(no_opengl) {
}
# default value for comic archive decompression backend
unix:!macx:!CONFIG(unarr):!CONFIG(7zip) {
unix:!macx:!CONFIG(unarr):!CONFIG(7zip):!CONFIG(libarchive) {
CONFIG += unarr
}

View File

@ -15,11 +15,12 @@ win32 {
CONFIG -= embed_manifest_exe
}
!CONFIG(unarr){
include(../../compressed_archive/wrapper.pri)
CONFIG(7zip) {
include(../../compressed_archive/wrapper.pri)
} else:CONFIG(unarr) {
include(../../compressed_archive/unarr/unarr-wrapper.pri)
} else:CONFIG(libarchive) {
include(../../compressed_archive/libarchive/libarchive-wrapper.pri)
} else {
include(../../compressed_archive/unarr/unarr-wrapper.pri)
include(../../compressed_archive/wrapper.pri)
}