move some functions from MMCZip to use libarchive
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
This commit is contained in:
170
launcher/archive/ArchiveReader.cpp
Normal file
170
launcher/archive/ArchiveReader.cpp
Normal 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
|
||||
53
launcher/archive/ArchiveReader.h
Normal file
53
launcher/archive/ArchiveReader.h
Normal 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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {};
|
||||
|
||||
Reference in New Issue
Block a user