diff --git a/src/gzip.cpp b/src/gzip.cpp index 20d79d6..e47c932 100644 --- a/src/gzip.cpp +++ b/src/gzip.cpp @@ -3,33 +3,57 @@ #include #include "gzip.h" +#define CHUNK 16384 -QByteArray Gzip::uncompress(const QByteArray &data) +QByteArray Gzip::uncompress(QIODevice *device, qint64 limit) { + int ret; + z_stream strm; + unsigned char in[CHUNK]; + unsigned char out[CHUNK]; + qint64 rs; QByteArray uba; - z_stream stream; - quint32 *size = (quint32*)(data.constData() + data.size() - sizeof(quint32)); - uba.resize(qFromLittleEndian(*size)); - stream.zalloc = Z_NULL; - stream.zfree = Z_NULL; - stream.opaque = Z_NULL; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + if (inflateInit2(&strm, MAX_WBITS + 16) != Z_OK) + return QByteArray(); - stream.next_in = (Bytef*)data.constData(); - stream.avail_in = data.size(); - stream.next_out = (Bytef*)uba.data(); - stream.avail_out = uba.size(); + do { + rs = device->read((char*)in, CHUNK); + if (rs < 0) { + (void)inflateEnd(&strm); + return QByteArray(); + } else if (rs == 0) + break; + else + strm.avail_in = (uInt)rs; + strm.next_in = in; - if (inflateInit2(&stream, MAX_WBITS + 16) != Z_OK) - return uba; + do { + strm.avail_out = CHUNK; + strm.next_out = out; + ret = inflate(&strm, Z_NO_FLUSH); + Q_ASSERT(ret != Z_STREAM_ERROR); + switch (ret) { + case Z_NEED_DICT: + case Z_DATA_ERROR: + case Z_MEM_ERROR: + (void)inflateEnd(&strm); + return QByteArray(); + } + uba.append((char*)out, CHUNK - strm.avail_out); + if (limit && uba.size() >= limit) { + (void)inflateEnd(&strm); + return uba; + } + } while (!strm.avail_out); + } while (ret != Z_STREAM_END); - if (inflate(&stream, Z_NO_FLUSH) != Z_STREAM_END) { - qCritical() << "Invalid gzip data"; - uba = QByteArray(); - } - - inflateEnd(&stream); - - return uba; + (void)inflateEnd(&strm); + return (ret == Z_STREAM_END) ? uba : QByteArray(); } diff --git a/src/gzip.h b/src/gzip.h index debf125..f48c52f 100644 --- a/src/gzip.h +++ b/src/gzip.h @@ -3,9 +3,11 @@ #include +class QIODevice; + namespace Gzip { - QByteArray uncompress(const QByteArray &data); + QByteArray uncompress(QIODevice *device, qint64 limit = 0); } #endif // GZIP_H diff --git a/src/pbfhandler.cpp b/src/pbfhandler.cpp index 0a7f793..3728d11 100644 --- a/src/pbfhandler.cpp +++ b/src/pbfhandler.cpp @@ -10,22 +10,22 @@ #define TILE_SIZE 512 -#define GZIP_MAGIC 0x1F8B0800 -#define GZIP_MAGIC_MASK 0xFFFFFF00 -#define PBF_MAGIC 0x1A000000 -#define PBF_MAGIC_MASK 0xFF000000 +#define GZIP_MAGIC 0x1F8B +#define GZIP_MAGIC_MASK 0xFFFF +#define PBF_MAGIC 0x1A00 +#define PBF_MAGIC_MASK 0xFF00 -static bool isMagic(quint32 magic, quint32 mask, quint32 value) +static bool isMagic(quint16 magic, quint16 mask, quint16 value) { return ((qFromBigEndian(value) & mask) == magic); } -static bool isGZIPPBF(quint32 magic) +static bool isGZIPPBF(quint16 magic) { return isMagic(GZIP_MAGIC, GZIP_MAGIC_MASK, magic); } -static bool isPlainPBF(quint32 magic) +static bool isPlainPBF(quint16 magic) { return isMagic(PBF_MAGIC, PBF_MAGIC_MASK, magic); } @@ -42,23 +42,31 @@ bool PBFHandler::canRead() const bool PBFHandler::canRead(QIODevice *device) { - quint32 magic; + quint16 magic; qint64 size = device->peek((char*)&magic, sizeof(magic)); if (size != sizeof(magic)) return false; - return (isGZIPPBF(magic) || isPlainPBF(magic)); + if (isPlainPBF(magic)) + return true; + else if (isGZIPPBF(magic)) { + QByteArray ba(Gzip::uncompress(device, sizeof(magic))); + if (ba.size() < (int)sizeof(magic)) + return false; + return isPlainPBF(*((quint16*)ba.constData())); + } else + return false; } bool PBFHandler::read(QImage *image) { - quint32 magic; + quint16 magic; if (device()->peek((char*)&magic, sizeof(magic)) != sizeof(magic)) return false; QByteArray ba; if (isGZIPPBF(magic)) - ba = Gzip::uncompress(device()->readAll()); + ba = Gzip::uncompress(device()); else if (isPlainPBF(magic)) ba = device()->readAll(); if (ba.isNull())