468 lines
11 KiB
C++
468 lines
11 KiB
C++
|
|
#include "osziparchive.h"
|
|
|
|
#include "zlib/zlib.h"
|
|
|
|
#include <qbytearray.h>
|
|
#include <qdatastream.h>
|
|
#include <qtextstream.h>
|
|
|
|
|
|
void CZipFileHeader::clear()
|
|
{
|
|
m_uVersionMadeBy = 0;
|
|
m_uVersionNeeded = 0;
|
|
m_uFlags = 0;
|
|
m_uCompression = 0;
|
|
m_uTime = 0;
|
|
m_uDate = 0;
|
|
m_uCRC = 0;
|
|
m_uSizeCompressed = 0;
|
|
m_uSizeUncompressed = 0;
|
|
m_uDiskNumberStart = 0;
|
|
m_uInternalFileAttributes = 0;
|
|
m_uExternalFileAttributes = 0;
|
|
m_uRelativeOffsetLocalHeader = 0;
|
|
m_strFileName.clear();
|
|
m_strComment.clear();
|
|
m_baExtraField.clear();
|
|
|
|
// helpers
|
|
m_uDataPosition = 0;
|
|
m_uLocalHeaderPosition = 0;
|
|
m_uCentralHeaderPosition = 0;
|
|
}
|
|
|
|
void CZipFileHeader::integrate(const CZipFileHeader& head)
|
|
{
|
|
if (m_uVersionMadeBy == 0) m_uVersionMadeBy = head.m_uVersionMadeBy;
|
|
if (m_uVersionNeeded == 0) m_uVersionNeeded = head.m_uVersionNeeded;
|
|
if (m_uFlags == 0) m_uFlags = head.m_uFlags;
|
|
if (m_uCompression == 0) m_uCompression = head.m_uCompression;
|
|
if (m_uTime == 0) m_uTime = head.m_uTime;
|
|
if (m_uDate == 0) m_uDate = head.m_uDate;
|
|
if (m_uCRC == 0) m_uCRC = head.m_uCRC;
|
|
if (m_uSizeCompressed == 0) m_uSizeCompressed = head.m_uSizeCompressed;
|
|
if (m_uSizeUncompressed == 0) m_uSizeUncompressed = head.m_uSizeUncompressed;
|
|
if (m_uDiskNumberStart == 0) m_uDiskNumberStart = head.m_uDiskNumberStart;
|
|
if (m_uInternalFileAttributes == 0) m_uInternalFileAttributes = head.m_uInternalFileAttributes;
|
|
if (m_uExternalFileAttributes == 0) m_uExternalFileAttributes = head.m_uExternalFileAttributes;
|
|
if (m_uRelativeOffsetLocalHeader == 0) m_uRelativeOffsetLocalHeader = head.m_uRelativeOffsetLocalHeader;
|
|
|
|
if (m_strFileName.isEmpty()) m_strFileName = head.m_strFileName;
|
|
if (m_baExtraField.isEmpty()) m_baExtraField = head.m_baExtraField;
|
|
if (m_strComment.isEmpty()) m_strComment = head.m_strComment;
|
|
|
|
if (m_uDataPosition == 0) m_uDataPosition = head.m_uDataPosition;
|
|
if (m_uLocalHeaderPosition == 0) m_uLocalHeaderPosition = head.m_uLocalHeaderPosition;
|
|
if (m_uCentralHeaderPosition == 0) m_uCentralHeaderPosition = head.m_uCentralHeaderPosition;
|
|
}
|
|
|
|
bool CZipFileHeader::readLocalFileHeader(QFile& file)
|
|
{
|
|
QDataStream in(&file);
|
|
quint16 uFileNameLength=0, uExtraFieldLength=0;
|
|
|
|
clear();
|
|
in.setByteOrder(QDataStream::LittleEndian);
|
|
|
|
m_uLocalHeaderPosition = file.pos();
|
|
|
|
in >> m_uVersionNeeded;
|
|
in >> m_uFlags;
|
|
in >> m_uCompression;
|
|
in >> m_uTime;
|
|
in >> m_uDate;
|
|
in >> m_uCRC;
|
|
in >> m_uSizeCompressed;
|
|
in >> m_uSizeUncompressed;
|
|
in >> uFileNameLength;
|
|
in >> uExtraFieldLength;
|
|
|
|
if (uFileNameLength != 0)
|
|
{
|
|
QByteArray a (uFileNameLength, 0);
|
|
in.readRawData(a.data(), uFileNameLength);
|
|
m_strFileName = a;
|
|
}
|
|
if (uExtraFieldLength != 0)
|
|
{
|
|
m_baExtraField.resize(uExtraFieldLength);
|
|
in.readRawData(m_baExtraField.data(), uExtraFieldLength);
|
|
}
|
|
|
|
m_uDataPosition = file.pos();
|
|
in.skipRawData(m_uSizeCompressed);
|
|
|
|
if (hasDataDescriptor())
|
|
{
|
|
in >> m_uCRC;
|
|
in >> m_uSizeCompressed;
|
|
in >> m_uSizeUncompressed;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CZipFileHeader::writeLocalFileHeader(QFile& file)
|
|
{
|
|
Q_UNUSED(file);
|
|
}
|
|
|
|
bool CZipFileHeader::readCentralDirectoryHeader(QFile& file)
|
|
{
|
|
QDataStream in(&file);
|
|
quint16 uFileNameLength=0, uExtraFieldLength=0, uCommentLength=0;
|
|
|
|
clear();
|
|
in.setByteOrder(QDataStream::LittleEndian);
|
|
|
|
m_uCentralHeaderPosition = file.pos();
|
|
|
|
in >> m_uVersionMadeBy;
|
|
in >> m_uVersionNeeded;
|
|
in >> m_uFlags;
|
|
in >> m_uCompression;
|
|
in >> m_uTime;
|
|
in >> m_uDate;
|
|
in >> m_uCRC;
|
|
in >> m_uSizeCompressed;
|
|
in >> m_uSizeUncompressed;
|
|
in >> uFileNameLength;
|
|
in >> uExtraFieldLength;
|
|
in >> uCommentLength;
|
|
in >> m_uDiskNumberStart;
|
|
in >> m_uInternalFileAttributes;
|
|
in >> m_uExternalFileAttributes;
|
|
in >> m_uRelativeOffsetLocalHeader;
|
|
|
|
if (uFileNameLength != 0)
|
|
{
|
|
QByteArray a (uFileNameLength, 0);
|
|
in.readRawData(a.data(), uFileNameLength);
|
|
m_strFileName = a;
|
|
}
|
|
if (uExtraFieldLength != 0)
|
|
{
|
|
m_baExtraField.resize(uExtraFieldLength);
|
|
in.readRawData(m_baExtraField.data(), uExtraFieldLength);
|
|
}
|
|
|
|
if (uCommentLength != 0)
|
|
{
|
|
QByteArray a (uCommentLength, 0);
|
|
in.readRawData(a.data(), uCommentLength);
|
|
m_strComment = a;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CZipFileHeader::writeCentralDirectoryHeader(QFile& file)
|
|
{
|
|
Q_UNUSED(file);
|
|
}
|
|
|
|
void CZipEndRecord::clear()
|
|
{
|
|
m_uDisk = 0;
|
|
m_uDiskCentralDir = 0;
|
|
m_uEntriesCentralDirDisk = 0;
|
|
m_uEntriesCentralDir = 0;
|
|
m_uCentralDirSize = 0;
|
|
m_uCentralDirOffset = 0;
|
|
m_strComment.clear();
|
|
}
|
|
|
|
bool CZipEndRecord::read (QFile& file)
|
|
{
|
|
QDataStream in(&file);
|
|
quint16 uCommentLength=0;
|
|
|
|
clear();
|
|
in.setByteOrder(QDataStream::LittleEndian);
|
|
|
|
in >> m_uDisk;
|
|
in >> m_uDiskCentralDir;
|
|
in >> m_uEntriesCentralDirDisk;
|
|
in >> m_uEntriesCentralDir;
|
|
in >> m_uCentralDirSize;
|
|
in >> m_uCentralDirOffset;
|
|
in >> uCommentLength;
|
|
|
|
if (uCommentLength != 0)
|
|
{
|
|
QByteArray a (uCommentLength, 0);
|
|
in.readRawData(a.data(), uCommentLength);
|
|
m_strComment = a;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CZipEndRecord::write (QFile& file)
|
|
{
|
|
Q_UNUSED(file);
|
|
}
|
|
|
|
|
|
CZipFile::CZipFile(CZipArchive *pArchive)
|
|
{
|
|
m_pArchive = pArchive;
|
|
clear();
|
|
}
|
|
|
|
CZipFile::~CZipFile()
|
|
{
|
|
}
|
|
|
|
void CZipFile::clear()
|
|
{
|
|
}
|
|
|
|
bool CZipFile::readHeader ()
|
|
{
|
|
QFile& file = m_pArchive->m_file;
|
|
|
|
return (m_head.readLocalFileHeader(file));
|
|
}
|
|
|
|
QByteArray CZipFile::deflateToByteArray()
|
|
{
|
|
/*QByteArray a, b;
|
|
|
|
if (m_head.m_uDataPosition == 0 || m_head.m_uSizeUncompressed == 0) return QByteArray();
|
|
|
|
m_pArchive->m_file.seek (m_head.m_uDataPosition);
|
|
a = m_pArchive->m_file.read (m_head.m_uSizeCompressed);
|
|
|
|
b.resize(m_head.m_uSizeUncompressed);
|
|
quint32 u = m_head.m_uSizeUncompressed;
|
|
Bytef* src = (Bytef*)a.data();
|
|
Bytef* dst = (Bytef*)b.data();
|
|
if (uncompress(dst, (uLongf*) &u, src, m_head.m_uSizeCompressed) != Z_OK)
|
|
return QByteArray();
|
|
|
|
return b;*/
|
|
|
|
QByteArray a;
|
|
z_stream strm;
|
|
char *pIn=0, *pOut=0;
|
|
int ret;
|
|
|
|
memset(&strm, 0, sizeof(z_stream));
|
|
if (m_head.m_uDataPosition == 0 || m_head.m_uSizeUncompressed == 0) return QByteArray();
|
|
|
|
// prepare zip-stream
|
|
/* windowBits is passed < 0 to tell that there is no zlib header.
|
|
* Note that in this case inflate *requires* an extra "dummy" byte
|
|
* after the compressed stream in order to complete decompression and
|
|
* return Z_STREAM_END.
|
|
*/
|
|
if (inflateInit2(&strm, -MAX_WBITS) != Z_OK) return QByteArray();
|
|
|
|
// read data
|
|
pIn = new char[m_head.m_uSizeCompressed+1];
|
|
memset (pIn, 0, m_head.m_uSizeCompressed+1);
|
|
m_pArchive->m_file.seek (m_head.m_uDataPosition);
|
|
m_pArchive->m_file.read (pIn, m_head.m_uSizeCompressed);
|
|
|
|
// prepare output
|
|
pOut = new char[m_head.m_uSizeUncompressed];
|
|
memset(pOut, 0, m_head.m_uSizeUncompressed);
|
|
|
|
/*
|
|
// DEBUG START
|
|
FILE *fpt;
|
|
fpt = fopen ("questions.xml", "rt");
|
|
fread (pOut, m_head.m_uSizeUncompressed, 1, fpt);
|
|
fclose (fpt);
|
|
|
|
unsigned u = m_head.m_uSizeCompressed;
|
|
fpt = fopen ("test2.gz", "wb");
|
|
compress2((Bytef*)pIn, (uLongf*) &u, (Bytef*)pOut, m_head.m_uSizeUncompressed, 9);
|
|
fwrite (pIn, u, 1, fpt);
|
|
fclose (fpt);
|
|
// DEBUG ENDE
|
|
*/
|
|
do
|
|
{
|
|
strm.avail_in = m_head.m_uSizeCompressed+1;
|
|
strm.next_in = (Bytef*) pIn;
|
|
do
|
|
{
|
|
strm.avail_out = m_head.m_uSizeUncompressed;
|
|
strm.next_out = (Bytef*) pOut;
|
|
ret = inflate (&strm, Z_SYNC_FLUSH);
|
|
switch (ret)
|
|
{
|
|
case Z_NEED_DICT:
|
|
ret = Z_DATA_ERROR; /* and fall through */
|
|
case Z_DATA_ERROR:
|
|
case Z_MEM_ERROR:
|
|
inflateEnd(&strm);
|
|
delete [] pIn;
|
|
delete [] pOut;
|
|
return QByteArray();
|
|
}
|
|
}
|
|
while (strm.avail_out == 0);
|
|
|
|
/* done when inflate() says it's done */
|
|
}
|
|
while (ret != Z_STREAM_END);
|
|
|
|
inflateEnd(&strm);
|
|
|
|
a = QByteArray (pOut, m_head.m_uSizeUncompressed);
|
|
delete [] pIn;
|
|
delete [] pOut;
|
|
|
|
return a;
|
|
}
|
|
/*
|
|
QString CZipFile::deflateToString()
|
|
{
|
|
QByteArray a = deflateToByteArray();
|
|
QTextStream in (a, QIODevice::ReadOnly);
|
|
//QString str;
|
|
// in >> str;
|
|
return in.readAll();
|
|
}
|
|
*/
|
|
|
|
#define CHUNK (1<<16)
|
|
|
|
bool CZipFile::deflateToFile (QIODevice& dev)
|
|
{
|
|
z_stream strm;
|
|
char cIn[CHUNK], cOut[CHUNK];
|
|
int ret;
|
|
|
|
if ((dev.openMode() & QIODevice::WriteOnly) == 0)
|
|
return false;
|
|
|
|
memset(&strm, 0, sizeof(z_stream));
|
|
if (m_head.m_uDataPosition == 0 || m_head.m_uSizeUncompressed == 0) return false;
|
|
|
|
// prepare zip-stream
|
|
/* windowBits is passed < 0 to tell that there is no zlib header.
|
|
* Note that in this case inflate *requires* an extra "dummy" byte
|
|
* after the compressed stream in order to complete decompression and
|
|
* return Z_STREAM_END.
|
|
*/
|
|
if (inflateInit2(&strm, -MAX_WBITS) != Z_OK) return false;
|
|
|
|
// prepare archive
|
|
m_pArchive->m_file.seek (m_head.m_uDataPosition);
|
|
|
|
// unzip
|
|
unsigned uRead=0, uBytes=0;
|
|
do
|
|
{
|
|
uBytes = m_pArchive->m_file.read(cIn, CHUNK);
|
|
if (uRead + uBytes > m_head.m_uSizeCompressed)
|
|
uBytes = m_head.m_uSizeCompressed - uRead + 1; // one dummy byte extra
|
|
uRead += uBytes;
|
|
|
|
strm.avail_in = uBytes;
|
|
if (strm.avail_in == 0) break;
|
|
strm.next_in = (Bytef*) cIn;
|
|
|
|
do
|
|
{
|
|
strm.avail_out = CHUNK;
|
|
strm.next_out = (Bytef*) cOut;
|
|
ret = inflate (&strm, Z_NO_FLUSH);
|
|
switch (ret)
|
|
{
|
|
case Z_NEED_DICT:
|
|
ret = Z_DATA_ERROR; /* and fall through */
|
|
case Z_DATA_ERROR:
|
|
case Z_MEM_ERROR:
|
|
inflateEnd(&strm);
|
|
return false;
|
|
}
|
|
unsigned uHave = CHUNK - strm.avail_out;
|
|
dev.write(cOut, uHave);
|
|
}
|
|
while (strm.avail_out == 0);
|
|
|
|
/* done when inflate() says it's done */
|
|
}
|
|
while (ret != Z_STREAM_END);
|
|
|
|
inflateEnd(&strm);
|
|
return true;
|
|
}
|
|
|
|
|
|
CZipArchive::CZipArchive()
|
|
{
|
|
}
|
|
|
|
CZipArchive::~CZipArchive()
|
|
{
|
|
qDeleteAll(m_listFiles);
|
|
qDeleteAll(m_listEndRecords);
|
|
}
|
|
|
|
bool CZipArchive::open (const QString& strFileName, const OpenMode om)
|
|
{
|
|
unsigned uSignature=0;
|
|
CZipFile *pzf=0;
|
|
|
|
if (strFileName.isEmpty()) return false;
|
|
if (om != OpenReadOnly) return false;
|
|
|
|
m_strFileName = strFileName;
|
|
m_file.close();
|
|
m_file.setFileName(strFileName);
|
|
if (!m_file.open(QIODevice::ReadOnly)) return false;
|
|
QDataStream in(&m_file);
|
|
in.setByteOrder(QDataStream::LittleEndian);
|
|
|
|
while (!in.atEnd())
|
|
{
|
|
uSignature = 0;
|
|
in >> uSignature;
|
|
if (uSignature == 0x04034b50)
|
|
{ // local file header with data
|
|
pzf = new CZipFile(this);
|
|
if (pzf->readHeader())
|
|
m_listFiles.append(pzf);
|
|
//qDebug("%7i %7i %s", pzf->m_head.m_uSizeUncompressed, pzf->m_head.m_uSizeCompressed, qPrintable(pzf->fileName()));
|
|
// file.deflateToFile("test.tmp");
|
|
}
|
|
else if (uSignature == 0x02014b50)
|
|
{ // central directory file header
|
|
CZipFileHeader head;
|
|
head.readCentralDirectoryHeader(m_file);
|
|
pzf = findFile(head.m_strFileName);
|
|
if (pzf) pzf->m_head.integrate(head);
|
|
}
|
|
else if (uSignature == 0x06054b50)
|
|
{ // End of central directory record
|
|
CZipEndRecord *pzer = new CZipEndRecord();
|
|
pzer->read(m_file);
|
|
m_listEndRecords.append(pzer);
|
|
}
|
|
else
|
|
{
|
|
qDebug("Unknown signature: %X", uSignature);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
CZipFile* CZipArchive::findFile (const QString& strFileName)
|
|
{
|
|
for (int i=0; i<m_listFiles.size(); i++)
|
|
{
|
|
if (m_listFiles.at(i)->fileName() == strFileName)
|
|
return m_listFiles.at(i);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|