3#include <QtCore/QCollator>
6#include <QtCore/QFileInfo>
7#include <QtCore/QLocale>
8#include <QtCore/QMimeDatabase>
9#include <QtCore/QMimeType>
10#include <QtCore/QtEndian>
31constexpr size_t kMinMagicBytes = 6;
34constexpr size_t kTarUstarOffset = 257;
35constexpr size_t kTarUstarMagicLen = 5;
36constexpr size_t kMinBytesForTar = 263;
39constexpr unsigned char kMagicZip[] = {0x50, 0x4B};
40constexpr unsigned char kZipLocalFile = 0x03;
41constexpr unsigned char kZipEmptyArchive = 0x05;
42constexpr unsigned char kZipSpannedArchive = 0x07;
43constexpr unsigned char kMagic7z[] = {0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C};
44constexpr unsigned char kMagicGzip[] = {0x1F, 0x8B};
45constexpr unsigned char kMagicXz[] = {0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00};
46constexpr unsigned char kMagicZstd[] = {0x28, 0xB5, 0x2F, 0xFD};
47constexpr unsigned char kMagicBzip2[] = {0x42, 0x5A, 0x68};
48constexpr unsigned char kMagicLz4[] = {0x04, 0x22, 0x4D, 0x18};
53Format formatFromMimeType(
const QString& mimeType)
56 if (mimeType == QLatin1String(
"application/zip") || mimeType == QLatin1String(
"application/x-zip-compressed")) {
59 if (mimeType == QLatin1String(
"application/x-7z-compressed")) {
60 return Format::SEVENZ;
62 if (mimeType == QLatin1String(
"application/x-tar")) {
67 if (mimeType == QLatin1String(
"application/gzip") || mimeType == QLatin1String(
"application/x-gzip")) {
70 if (mimeType == QLatin1String(
"application/x-xz")) {
73 if (mimeType == QLatin1String(
"application/zstd") || mimeType == QLatin1String(
"application/x-zstd")) {
76 if (mimeType == QLatin1String(
"application/x-bzip2") || mimeType == QLatin1String(
"application/bzip2")) {
79 if (mimeType == QLatin1String(
"application/x-lz4")) {
84 if (mimeType == QLatin1String(
"application/x-compressed-tar")) {
85 return Format::TAR_GZ;
87 if (mimeType == QLatin1String(
"application/x-xz-compressed-tar")) {
88 return Format::TAR_XZ;
90 if (mimeType == QLatin1String(
"application/x-zstd-compressed-tar")) {
91 return Format::TAR_ZSTD;
93 if (mimeType == QLatin1String(
"application/x-bzip2-compressed-tar") ||
94 mimeType == QLatin1String(
"application/x-bzip-compressed-tar")) {
95 return Format::TAR_BZ2;
97 if (mimeType == QLatin1String(
"application/x-lz4-compressed-tar")) {
98 return Format::TAR_LZ4;
120thread_local ThreadState t_state;
124 t_state.error = Error::None;
125 t_state.errorString.clear();
128void setError(Error
error,
const QString& message = QString())
130 t_state.error =
error;
131 t_state.errorString = message;
134void setFormatInfo(
const QString& format,
const QString& filter)
136 t_state.formatName =
format;
137 t_state.filterName = filter;
140void clearFormatInfo()
142 t_state.formatName.clear();
143 t_state.filterName.clear();
154 return t_state.error;
159 if (!t_state.errorString.isEmpty()) {
160 return t_state.errorString;
169 return QStringLiteral(
"No error");
170 case Error::FileNotFound:
171 return QStringLiteral(
"File not found");
172 case Error::PermissionDenied:
173 return QStringLiteral(
"Permission denied");
174 case Error::InvalidArchive:
175 return QStringLiteral(
"Invalid or corrupt archive");
176 case Error::UnsupportedFormat:
177 return QStringLiteral(
"Unsupported format");
178 case Error::SizeLimitExceeded:
179 return QStringLiteral(
"Size limit exceeded");
180 case Error::Cancelled:
181 return QStringLiteral(
"Operation cancelled");
182 case Error::FileNotInArchive:
183 return QStringLiteral(
"File not found in archive");
185 return QStringLiteral(
"I/O error");
186 case Error::InternalError:
187 return QStringLiteral(
"Internal error");
189 return QStringLiteral(
"Unknown error");
194 return t_state.formatName;
199 return t_state.filterName;
216 qCWarning(QGCCompressionLog) <<
"File does not exist:" << filePath;
217 setError(Error::FileNotFound, QStringLiteral(
"File does not exist: ") + filePath);
220 if (format == Format::Auto) {
222 if (format == Format::Auto) {
223 qCWarning(QGCCompressionLog) <<
"Could not detect format for:" << filePath;
224 setError(Error::UnsupportedFormat, QStringLiteral(
"Could not detect format: ") + filePath);
238 qCWarning(QGCCompressionLog) <<
"Not an archive format:" <<
formatName(format);
239 setError(Error::UnsupportedFormat,
formatName(format) + QStringLiteral(
" is not an archive format"));
251 if (!device || !device->isOpen() || !device->isReadable()) {
252 qCWarning(QGCCompressionLog) <<
"Device is null, not open, or not readable";
253 setError(Error::IoError, QStringLiteral(
"Device is null, not open, or not readable"));
272 const QString lower = filePath.toLower();
275 if (lower.endsWith(
".tar.gz") || lower.endsWith(
".tgz")) {
276 return Format::TAR_GZ;
278 if (lower.endsWith(
".tar.xz") || lower.endsWith(
".txz")) {
279 return Format::TAR_XZ;
281 if (lower.endsWith(
".tar.zst") || lower.endsWith(
".tar.zstd")) {
282 return Format::TAR_ZSTD;
284 if (lower.endsWith(
".tar.bz2") || lower.endsWith(
".tbz2") || lower.endsWith(
".tbz")) {
285 return Format::TAR_BZ2;
287 if (lower.endsWith(
".tar.lz4")) {
288 return Format::TAR_LZ4;
292 if (lower.endsWith(
".zip")) {
295 if (lower.endsWith(
".7z")) {
296 return Format::SEVENZ;
298 if (lower.endsWith(
".gz") || lower.endsWith(
".gzip")) {
301 if (lower.endsWith(
".xz") || lower.endsWith(
".lzma")) {
304 if (lower.endsWith(
".zst") || lower.endsWith(
".zstd")) {
307 if (lower.endsWith(
".bz2") || lower.endsWith(
".bzip2")) {
308 return Format::BZIP2;
310 if (lower.endsWith(
".lz4")) {
313 if (lower.endsWith(
".tar")) {
324 if (format != Format::Auto) {
331 if (format != Format::Auto) {
332 qCDebug(QGCCompressionLog) <<
"Format detected from content:" <<
formatName(format) <<
"for" << filePath;
343 QFile file(filePath);
344 if (!file.open(QIODevice::ReadOnly)) {
351 QMimeDatabase mimeDb;
352 QMimeType mimeType = mimeDb.mimeTypeForFile(filePath);
354 if (mimeType.isValid() && mimeType.name() != QLatin1String(
"application/octet-stream")) {
355 Format format = formatFromMimeType(mimeType.name());
356 if (format != Format::Auto) {
357 qCDebug(QGCCompressionLog) <<
"MIME detection:" << mimeType.name() <<
"->" <<
formatName(format);
363 QFile file(filePath);
364 if (!file.open(QIODevice::ReadOnly)) {
372 if (
static_cast<size_t>(data.size()) < kMinMagicBytes) {
376 const auto* bytes =
reinterpret_cast<const unsigned char*
>(data.constData());
379 if (bytes[0] == kMagicZip[0] && bytes[1] == kMagicZip[1] &&
380 (bytes[2] == kZipLocalFile || bytes[2] == kZipEmptyArchive || bytes[2] == kZipSpannedArchive)) {
385 if (memcmp(bytes, kMagic7z,
sizeof(kMagic7z)) == 0) {
386 return Format::SEVENZ;
390 if (memcmp(bytes, kMagicGzip,
sizeof(kMagicGzip)) == 0) {
395 if (memcmp(bytes, kMagicXz,
sizeof(kMagicXz)) == 0) {
400 if (memcmp(bytes, kMagicZstd,
sizeof(kMagicZstd)) == 0) {
405 if (memcmp(bytes, kMagicBzip2,
sizeof(kMagicBzip2)) == 0) {
406 return Format::BZIP2;
410 if (memcmp(bytes, kMagicLz4,
sizeof(kMagicLz4)) == 0) {
415 if (
static_cast<size_t>(data.size()) >= kMinBytesForTar) {
416 if (data.mid(kTarUstarOffset, kTarUstarMagicLen) ==
"ustar") {
422 QMimeDatabase mimeDb;
423 QMimeType mimeType = mimeDb.mimeTypeForData(data);
424 if (mimeType.isValid() && mimeType.name() != QLatin1String(
"application/octet-stream")) {
425 Format format = formatFromMimeType(mimeType.name());
426 if (format != Format::Auto) {
427 qCDebug(QGCCompressionLog) <<
"MIME fallback detection:" << mimeType.name() <<
"->" <<
formatName(format);
439 return QStringLiteral(
".zip");
441 return QStringLiteral(
".7z");
443 return QStringLiteral(
".gz");
445 return QStringLiteral(
".xz");
447 return QStringLiteral(
".zst");
449 return QStringLiteral(
".bz2");
451 return QStringLiteral(
".lz4");
453 return QStringLiteral(
".tar");
455 return QStringLiteral(
".tar.gz");
457 return QStringLiteral(
".tar.xz");
458 case Format::TAR_ZSTD:
459 return QStringLiteral(
".tar.zst");
460 case Format::TAR_BZ2:
461 return QStringLiteral(
".tar.bz2");
462 case Format::TAR_LZ4:
463 return QStringLiteral(
".tar.lz4");
474 return QStringLiteral(
"Auto");
476 return QStringLiteral(
"ZIP");
478 return QStringLiteral(
"7-Zip");
480 return QStringLiteral(
"GZIP");
482 return QStringLiteral(
"XZ/LZMA");
484 return QStringLiteral(
"Zstandard");
486 return QStringLiteral(
"BZip2");
488 return QStringLiteral(
"LZ4");
490 return QStringLiteral(
"TAR");
492 return QStringLiteral(
"TAR.GZ");
494 return QStringLiteral(
"TAR.XZ");
495 case Format::TAR_ZSTD:
496 return QStringLiteral(
"TAR.ZSTD");
497 case Format::TAR_BZ2:
498 return QStringLiteral(
"TAR.BZ2");
499 case Format::TAR_LZ4:
500 return QStringLiteral(
"TAR.LZ4");
513 case Format::TAR_ZSTD:
514 case Format::TAR_BZ2:
515 case Format::TAR_LZ4:
544 if (filePath.endsWith(ext, Qt::CaseInsensitive)) {
545 return filePath.left(filePath.size() - ext.size());
556 qint64 maxDecompressedBytes)
563 QString actualOutput = outputPath;
564 if (actualOutput.isEmpty()) {
566 if (inputPath.endsWith(ext, Qt::CaseInsensitive)) {
567 actualOutput = inputPath.left(inputPath.size() - ext.size());
569 actualOutput = inputPath +
".decompressed";
573 qCDebug(QGCCompressionLog) <<
"Decompressing" << inputPath <<
"to" << actualOutput <<
"using" <<
formatName(format);
581 setError(Error::IoError, QStringLiteral(
"Decompression failed: ") + inputPath);
588 qCWarning(QGCCompressionLog) <<
formatName(format) <<
"is an archive format; use extractArchive() instead";
589 return extractArchive(inputPath, actualOutput, format, progress, maxDecompressedBytes);
592 qCWarning(QGCCompressionLog) <<
"Unsupported decompression format:" <<
formatName(format);
593 setError(Error::UnsupportedFormat, QStringLiteral(
"Unsupported decompression format: ") +
formatName(format));
605 const QString actualOutput = outputPath.isEmpty() ?
strippedPath(filePath) : outputPath;
609 qCWarning(QGCCompressionLog) <<
"Decompression failed:" << filePath;
613 if (removeOriginal && !QFile::remove(filePath)) {
614 qCWarning(QGCCompressionLog) <<
"Failed to remove original file:" << filePath;
622 if (data.isEmpty()) {
623 qCWarning(QGCCompressionLog) <<
"Cannot decompress empty data";
627 if (format == Format::Auto) {
629 if (format == Format::Auto) {
630 qCWarning(QGCCompressionLog) <<
"Could not detect format from data";
636 qCWarning(QGCCompressionLog) <<
"Invalid decompression format:" <<
formatName(format);
637 setError(Error::UnsupportedFormat,
formatName(format) + QStringLiteral(
" is not a compression format"));
643 if (result.isEmpty()) {
644 setError(Error::IoError, QStringLiteral(
"Failed to decompress data"));
660 qCDebug(QGCCompressionLog) <<
"Extracting" <<
formatName(format) <<
"archive" << archivePath <<
"to"
661 << outputDirectoryPath;
667 setError(Error::IoError, QStringLiteral(
"Failed to extract archive: ") + archivePath);
679 qCDebug(QGCCompressionLog) <<
"Atomically extracting" <<
formatName(format) <<
"archive" << archivePath
680 <<
"to" << outputDirectoryPath;
686 setError(Error::IoError, QStringLiteral(
"Failed to atomically extract archive: ") + archivePath);
695 setError(Error::InternalError, QStringLiteral(
"No filter callback provided"));
698 Format format = Format::Auto;
703 qCDebug(QGCCompressionLog) <<
"Extracting archive with filter:" << archivePath;
709 setError(Error::IoError, QStringLiteral(
"Failed to extract archive: ") + archivePath);
723 QCollator collator{QLocale{QLocale::English}};
724 collator.setNumericMode(
true);
725 collator.setCaseSensitivity(Qt::CaseInsensitive);
726 std::sort(entries.begin(), entries.end(), collator);
740 QCollator collator{QLocale{QLocale::English}};
741 collator.setNumericMode(
true);
742 collator.setCaseSensitivity(Qt::CaseInsensitive);
744 return collator.compare(a.name, b.name) < 0;
763 qCDebug(QGCCompressionLog) <<
"Validating archive:" << archivePath;
769 if (fileName.isEmpty()) {
770 qCWarning(QGCCompressionLog) <<
"File name cannot be empty";
779bool extractFile(
const QString& archivePath,
const QString& fileName,
const QString& outputPath,
Format format)
781 if (fileName.isEmpty()) {
782 qCWarning(QGCCompressionLog) <<
"File name cannot be empty";
789 const QString actualOutput = outputPath.isEmpty() ? QFileInfo(fileName).fileName() : outputPath;
790 qCDebug(QGCCompressionLog) <<
"Extracting" << fileName <<
"from" << archivePath <<
"to" << actualOutput;
796 if (fileName.isEmpty()) {
797 qCWarning(QGCCompressionLog) <<
"File name cannot be empty";
803 qCDebug(QGCCompressionLog) <<
"Extracting" << fileName <<
"from" << archivePath <<
"to memory";
807bool extractFiles(
const QString& archivePath,
const QStringList& fileNames,
const QString& outputDirectoryPath,
810 if (fileNames.isEmpty()) {
816 qCDebug(QGCCompressionLog) <<
"Extracting" << fileNames.size() <<
"files from" << archivePath;
820bool extractByPattern(
const QString& archivePath,
const QStringList& patterns,
const QString& outputDirectoryPath,
821 QStringList* extractedFiles,
Format format)
823 if (patterns.isEmpty()) {
824 setError(Error::FileNotInArchive, QStringLiteral(
"No patterns provided"));
830 qCDebug(QGCCompressionLog) <<
"Extracting files matching patterns" << patterns <<
"from" << archivePath;
833 setError(Error::FileNotInArchive, QStringLiteral(
"No files matched patterns"));
843 qint64 maxDecompressedBytes)
848 qCDebug(QGCCompressionLog) <<
"Decompressing from device to" << outputPath;
852 setError(Error::IoError, QStringLiteral(
"Failed to decompress from device"));
862 qCDebug(QGCCompressionLog) <<
"Decompressing from device to memory";
865 if (result.isEmpty()) {
866 setError(Error::IoError, QStringLiteral(
"Failed to decompress from device"));
872 qint64 maxDecompressedBytes)
877 qCDebug(QGCCompressionLog) <<
"Extracting archive from device to" << outputDirectoryPath;
881 setError(Error::IoError, QStringLiteral(
"Failed to extract archive from device"));
891 if (fileName.isEmpty()) {
892 qCWarning(QGCCompressionLog) <<
"File name cannot be empty";
893 setError(Error::FileNotInArchive, QStringLiteral(
"File name cannot be empty"));
896 qCDebug(QGCCompressionLog) <<
"Extracting" << fileName <<
"from device to memory";
899 if (result.isEmpty()) {
900 setError(Error::FileNotInArchive, QStringLiteral(
"File not found: ") + fileName);
911 if (data.isEmpty() || level == CompressionLevel::None) {
914 return qCompress(data,
static_cast<int>(level));
919 if (data.isEmpty()) {
922 return qUncompress(data);
932 if (level == CompressionLevel::None || data.size() < minSize) {
935 result.reserve(1 + data.size());
941 const QByteArray compressed = qCompress(data,
static_cast<int>(level));
943 if (!compressed.isEmpty() && compressed.size() < data.size()) {
946 result.reserve(1 + compressed.size());
948 result.append(compressed);
954 result.reserve(1 + data.size());
962 if (data.isEmpty()) {
966 const auto header =
static_cast<quint8
>(data[0]);
967 const QByteArray payload = data.mid(1);
974 if (maxDecompressedSize > 0 && payload.size() >= 4) {
975 const quint32 declaredSize = qFromBigEndian<quint32>(payload.constData());
976 if (declaredSize >
static_cast<quint32
>(maxDecompressedSize)) {
977 qCWarning(QGCCompressionLog) <<
"Rejected decompression: declared size"
978 << declaredSize <<
"exceeds limit" << maxDecompressedSize;
983 const QByteArray result = qUncompress(payload);
984 if (result.isEmpty() && !payload.isEmpty()) {
985 qCWarning(QGCCompressionLog) <<
"Decompression failed";
990 qCWarning(QGCCompressionLog) <<
"Unknown compression header byte:" << header;
1011 if (!decompressor.
open(QIODevice::ReadOnly)) {
1013 *
errorString = QObject::tr(
"Failed to open compressed file: %1").arg(filePath);
1018 const QByteArray data = (maxBytes > 0) ? decompressor.read(maxBytes) : decompressor.readAll();
1019 decompressor.
close();
1025 if (filePath.isEmpty()) {
1026 qCWarning(QGCCompressionLog) <<
"computeFileHash: empty file path";
1035 if (!decompressor.
open(QIODevice::ReadOnly)) {
1036 qCWarning(QGCCompressionLog) <<
"computeFileHash: failed to open:" << filePath;
1040 QCryptographicHash hash(algorithm);
1041 constexpr qint64 chunkSize = 65536;
1043 const QByteArray buffer = decompressor.read(chunkSize);
1044 if (buffer.isEmpty()) {
1045 if (decompressor.atEnd()) {
1048 qCWarning(QGCCompressionLog) <<
"computeFileHash: read error";
1049 decompressor.
close();
1052 hash.addData(buffer);
1055 decompressor.
close();
1056 return QString::fromLatin1(hash.result().toHex());
1070 QByteArray jsonData = data;
1074 if (jsonData.isEmpty()) {
1075 if (
error !=
nullptr) {
1076 error->error = QJsonParseError::IllegalValue;
1083 return QJsonDocument::fromJson(jsonData,
error);
QIODevice wrapper for streaming decompression.
#define QGC_LOGGING_CATEGORY(name, categoryStr)
Private implementation details for QGCCompression.
QIODevice wrapper for streaming decompression of single-file formats.
bool open(OpenMode mode) override
bool isCompressionFormat(Format format)
Check if format is a compression format (single stream)
QString computeFileHash(const QString &filePath, QCryptographicHash::Algorithm algorithm)
Hash file contents post-decompression.
bool fileExists(const QString &archivePath, const QString &fileName, Format format)
static bool validateDeviceInput(QIODevice *device)
Validate device input for streaming operations.
QByteArray decompressData(const QByteArray &data, Format format, qint64 maxDecompressedBytes)
bool extractArchiveAtomic(const QString &archivePath, const QString &outputDirectoryPath, Format format, ProgressCallback progress, qint64 maxDecompressedBytes)
static constexpr quint8 kHeaderCompressed
bool extractArchive(const QString &archivePath, const QString &outputDirectoryPath, Format format, ProgressCallback progress, qint64 maxDecompressedBytes)
bool decompressFile(const QString &inputPath, const QString &outputPath, Format format, ProgressCallback progress, qint64 maxDecompressedBytes)
std::function< bool(const ArchiveEntry &entry)> EntryFilter
bool looksLikeCompressedData(const QByteArray &data)
QByteArray uncompressData(const QByteArray &data, qint64 maxDecompressedSize)
int lastCompressionRatio()
Compression ratio from the last compressData() call (thread-local, percentage of original size).
QByteArray compressData(const QByteArray &data, CompressionLevel level, int minSize)
QStringList listArchive(const QString &archivePath, Format format)
bool extractArchiveFiltered(const QString &archivePath, const QString &outputDirectoryPath, EntryFilter filter, ProgressCallback progress, qint64 maxDecompressedBytes)
bool isArchiveFormat(Format format)
Check if format is an archive (contains multiple files)
bool extractByPattern(const QString &archivePath, const QStringList &patterns, const QString &outputDirectoryPath, QStringList *extractedFiles, Format format)
Format detectFormatFromData(const QByteArray &data)
QString lastErrorString()
Get a human-readable error message from the last operation (thread-local)
static thread_local int s_lastCompressionRatio
QString strippedPath(const QString &filePath)
QString detectedFilterName()
bool isCompressedFile(const QString &filePath)
Check if file path indicates a compressed file (.gz, .xz, .zst)
static bool validateArchiveInput(const QString &archivePath, Format &format)
Validate archive input: file exists, format detected, and is archive format.
QString errorName(Error error)
Get a human-readable name for an error code.
bool decompressFromDevice(QIODevice *device, const QString &outputPath, ProgressCallback progress, qint64 maxDecompressedBytes)
Format
Archive and compression format types (for decompression)
QByteArray extractFileData(const QString &archivePath, const QString &fileName, Format format)
QByteArray compress(const QByteArray &data, CompressionLevel level)
static void captureFormatInfo()
Capture format detection info from QGClibarchive after an operation.
QByteArray uncompress(const QByteArray &data)
ArchiveStats getArchiveStats(const QString &archivePath, Format format)
QByteArray readFile(const QString &filePath, QString *errorString, qint64 maxBytes)
Read file contents, transparently decompressing .gz/.xz/.zst/.bz2/.lz4 files.
QList< ArchiveEntry > listArchiveDetailed(const QString &archivePath, Format format)
static bool validateFileInput(const QString &filePath, Format &format)
Error
Error codes for decompression operations.
QJsonDocument parseCompressedJson(const QByteArray &data, QJsonParseError *error)
Parse JSON from data that may be compressed. Auto-detects gzip/xz/zstd/bzip2/lz4.
bool validateArchive(const QString &archivePath, Format format)
bool extractFile(const QString &archivePath, const QString &fileName, const QString &outputPath, Format format)
std::function< bool(qint64 bytesProcessed, qint64 totalBytes)> ProgressCallback
Error lastError()
Get the error code from the last operation (thread-local)
bool isDataCompressed(const QByteArray &data)
Check if data has the compressed framing header.
bool extractFromDevice(QIODevice *device, const QString &outputDirectoryPath, ProgressCallback progress, qint64 maxDecompressedBytes)
QByteArray extractFileDataFromDevice(QIODevice *device, const QString &fileName)
bool extractFiles(const QString &archivePath, const QStringList &fileNames, const QString &outputDirectoryPath, Format format)
QString detectedFormatName()
static Format detectFormatFromExtension(const QString &filePath)
Extension-based format detection (internal helper)
QString decompressIfNeeded(const QString &filePath, const QString &outputPath, bool removeOriginal)
static constexpr quint8 kHeaderUncompressed
QString formatExtension(Format format)
Get file extension for a format.
Format detectFormatFromFile(const QString &filePath)
Format detectFormat(const QString &filePath, bool useContentFallback)
bool isQtResource(const QString &path)
QByteArray readFile(const QString &filePath, QString *errorString, qint64 maxBytes)
Read raw file contents.
bool exists(const QString &path)
QString computeFileHash(const QString &filePath, QCryptographicHash::Algorithm algorithm)
ArchiveStats getArchiveStats(const QString &archivePath)
bool extractMultipleFiles(const QString &archivePath, const QStringList &fileNames, const QString &outputDirectoryPath)
QString lastDetectedFormatName()
bool extractWithFilter(const QString &archivePath, const QString &outputDirectoryPath, EntryFilter filter, ProgressCallback progress, qint64 maxBytes)
QString lastDetectedFilterName()
bool decompressSingleFile(const QString &inputPath, const QString &outputPath, ProgressCallback progress, qint64 maxBytes)
bool extractFromDevice(QIODevice *device, const QString &outputDirectoryPath, ProgressCallback progress, qint64 maxBytes)
QStringList listArchiveEntries(const QString &archivePath)
bool extractSingleFile(const QString &archivePath, const QString &fileName, const QString &outputPath)
QByteArray decompressDataFromDevice(QIODevice *device, qint64 maxBytes)
QList< ArchiveEntry > listArchiveEntriesDetailed(const QString &archivePath)
bool extractArchiveAtomic(const QString &archivePath, const QString &outputDirectoryPath, ProgressCallback progress, qint64 maxBytes)
QByteArray extractFileToMemory(const QString &archivePath, const QString &fileName)
QByteArray decompressDataFromMemory(const QByteArray &data, qint64 maxBytes)
bool validateArchive(const QString &archivePath)
bool fileExistsInArchive(const QString &archivePath, const QString &fileName)
bool decompressFromDevice(QIODevice *device, const QString &outputPath, ProgressCallback progress, qint64 maxBytes)
bool extractAnyArchive(const QString &archivePath, const QString &outputDirectoryPath, ProgressCallback progress, qint64 maxBytes)
QByteArray extractFileDataFromDevice(QIODevice *device, const QString &fileName)
bool extractByPattern(const QString &archivePath, const QStringList &patterns, const QString &outputDirectoryPath, QStringList *extractedFiles)
Metadata for a single entry in an archive.
Summary statistics for an archive.