mirror of
https://github.com/YACReader/yacreader
synced 2025-05-25 18:00:46 -04:00
Add libarchive decompression backend
This commit is contained in:
parent
a0dfa4e447
commit
862c220069
3
.gitignore
vendored
3
.gitignore
vendored
@ -67,3 +67,6 @@ YACReaderLibrary/YACReaderLibrary\.xcodeproj/
|
||||
.DS_Store
|
||||
compressed_archive/libp7zip
|
||||
c_x86_64.pch
|
||||
|
||||
compile_commands.json
|
||||
.ccls-cache
|
||||
|
@ -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 \
|
||||
|
@ -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?)
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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?)
|
||||
}
|
||||
|
24
compressed_archive/libarchive/README.txt
Normal file
24
compressed_archive/libarchive/README.txt
Normal 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?
|
155
compressed_archive/libarchive/compressed_archive.cpp
Normal file
155
compressed_archive/libarchive/compressed_archive.cpp
Normal 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;
|
||||
}
|
44
compressed_archive/libarchive/compressed_archive.h
Normal file
44
compressed_archive/libarchive/compressed_archive.h
Normal 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
|
15
compressed_archive/libarchive/extract_delegate.h
Normal file
15
compressed_archive/libarchive/extract_delegate.h
Normal 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
|
22
compressed_archive/libarchive/libarchive-wrapper.pri
Normal file
22
compressed_archive/libarchive/libarchive-wrapper.pri
Normal 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)
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user