mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-13 16:34:14 +02:00
Merge pull request #1467 from ispyisail/fix/qetz-zip-entry-date
Fix the dates of the files inside a .qetz project
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
#include <QFile>
|
||||
#include <QSaveFile>
|
||||
#include <QSet>
|
||||
#include <QtEndian>
|
||||
|
||||
namespace {
|
||||
void setError(QString *error, const QString &text)
|
||||
@@ -44,6 +45,42 @@ bool safeName(const QString &name)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//Built with MINIZ_NO_TIME, miniz leaves each entry's DOS date at 0,
|
||||
//day 0 of month 0, which is no date: unzip shows 30 Nov 1979, other
|
||||
//tools 13 Dec 1901. Give every entry 1 Jan 1980 00:00 instead, the
|
||||
//first valid DOS date, so the bytes still do not depend on the time.
|
||||
constexpr quint16 EntryDosDate = (1 << 5) | 1;
|
||||
|
||||
bool setEntryDates(QByteArray *bytes)
|
||||
{
|
||||
char *data = bytes->data();
|
||||
mz_zip_archive zip;
|
||||
mz_zip_zero_struct(&zip);
|
||||
if (!mz_zip_reader_init_mem(&zip, data, size_t(bytes->size()), 0)) return false;
|
||||
|
||||
bool ok = true;
|
||||
const mz_uint count = mz_zip_reader_get_num_files(&zip);
|
||||
for (mz_uint i = 0 ; i < count && ok ; ++i)
|
||||
{
|
||||
mz_zip_archive_file_stat stat;
|
||||
if (!mz_zip_reader_file_stat(&zip, i, &stat)) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
//date at offset 12 of the local header, 14 of the central one
|
||||
const quint64 local = stat.m_local_header_ofs + 12;
|
||||
const quint64 central = zip.m_central_directory_file_ofs + stat.m_central_dir_ofs + 14;
|
||||
if (local + 2 > quint64(bytes->size()) || central + 2 > quint64(bytes->size())) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
qToLittleEndian<quint16>(EntryDosDate, data + local);
|
||||
qToLittleEndian<quint16>(EntryDosDate, data + central);
|
||||
}
|
||||
mz_zip_reader_end(&zip);
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,8 +124,12 @@ QByteArray QetZip::toBytes(const QList<Entry> &entries, QString *error)
|
||||
mz_zip_writer_end(&zip);
|
||||
return {};
|
||||
}
|
||||
const QByteArray bytes(static_cast<const char *>(buffer), qsizetype(size));
|
||||
QByteArray bytes(static_cast<const char *>(buffer), qsizetype(size));
|
||||
mz_zip_writer_end(&zip); //frees buffer
|
||||
if (!setEntryDates(&bytes)) {
|
||||
setError(error, QStringLiteral("cannot date the zip entries"));
|
||||
return {};
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
megabytes, not gigabytes -- and written through QSaveFile, so a failed
|
||||
save leaves the previous file as it was.
|
||||
|
||||
Entries carry no timestamp: saving the same project twice gives the
|
||||
same bytes.
|
||||
Every entry is dated 1 Jan 1980 00:00, not the time of saving: saving
|
||||
the same project twice gives the same bytes.
|
||||
*/
|
||||
namespace QetZip
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <QtTest>
|
||||
|
||||
#include <QTemporaryDir>
|
||||
#include <QtEndian>
|
||||
|
||||
#include "container/qetzip.h"
|
||||
#include "container/miniz/miniz.h"
|
||||
@@ -72,6 +73,33 @@ private slots:
|
||||
QCOMPARE(QetZip::toBytes(sample()), QetZip::toBytes(sample()));
|
||||
}
|
||||
|
||||
// every entry carries a valid date in both of its headers, not the
|
||||
// 0 miniz writes without a clock (shown as 1979 or 1901 by unzip tools)
|
||||
void entriesHaveValidDate()
|
||||
{
|
||||
const QByteArray zip = QetZip::toBytes(sample());
|
||||
QVERIFY(!zip.isEmpty());
|
||||
mz_zip_archive archive;
|
||||
mz_zip_zero_struct(&archive);
|
||||
QVERIFY(mz_zip_reader_init_mem(&archive, zip.constData(), size_t(zip.size()), 0));
|
||||
const mz_uint count = mz_zip_reader_get_num_files(&archive);
|
||||
QCOMPARE(int(count), sample().size());
|
||||
for (mz_uint i = 0 ; i < count ; ++i) {
|
||||
mz_zip_archive_file_stat stat;
|
||||
QVERIFY(mz_zip_reader_file_stat(&archive, i, &stat));
|
||||
const char *local = zip.constData() + stat.m_local_header_ofs;
|
||||
const char *central = zip.constData() + archive.m_central_directory_file_ofs + stat.m_central_dir_ofs;
|
||||
QCOMPARE(qFromLittleEndian<quint16>(local), quint16(0x50 | 0x4b << 8)); // "PK"
|
||||
QCOMPARE(qFromLittleEndian<quint16>(central), quint16(0x50 | 0x4b << 8));
|
||||
// 1 Jan 1980: day 1, month 1, year 0 since 1980; time 00:00:00
|
||||
QCOMPARE(qFromLittleEndian<quint16>(local + 12), quint16(0x21));
|
||||
QCOMPARE(qFromLittleEndian<quint16>(central + 14), quint16(0x21));
|
||||
QCOMPARE(qFromLittleEndian<quint16>(local + 10), quint16(0));
|
||||
QCOMPARE(qFromLittleEndian<quint16>(central + 12), quint16(0));
|
||||
}
|
||||
mz_zip_reader_end(&archive);
|
||||
}
|
||||
|
||||
void refusesUnsafeNamesOnWrite_data()
|
||||
{
|
||||
QTest::addColumn<QString>("name");
|
||||
|
||||
Reference in New Issue
Block a user