File indexing completed on 2024-04-28 11:33:45

0001 /* This file is part of the KDE libraries
0002    SPDX-FileCopyrightText: 2011 Mario Bensi <mbensi@ipsquad.net>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "k7zip.h"
0008 #include "karchive_p.h"
0009 #include "loggingcategory.h"
0010 
0011 #include <QBuffer>
0012 #include <QDebug>
0013 #include <QDir>
0014 #include <QFile>
0015 #include <qplatformdefs.h>
0016 
0017 #include "kcompressiondevice.h"
0018 #include "klimitediodevice_p.h"
0019 #include <kfilterbase.h>
0020 #include <kxzfilter.h>
0021 
0022 #include "zlib.h"
0023 #include <memory>
0024 #include <time.h> // time()
0025 
0026 #ifndef QT_STAT_LNK
0027 #define QT_STAT_LNK 0120000
0028 #endif // QT_STAT_LNK
0029 
0030 ////////////////////////////////////////////////////////////////////////
0031 /////////////////////////// K7Zip //////////////////////////////////////
0032 ////////////////////////////////////////////////////////////////////////
0033 
0034 #define BUFFER_SIZE 8 * 1024
0035 
0036 static const unsigned char k7zip_signature[6] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C};
0037 // static const unsigned char XZ_HEADER_MAGIC[6] = { 0xFD, '7', 'z', 'X', 'Z', 0x00 };
0038 
0039 /* clang-format off */
0040 static QChar GetUi16(const char *p, quint64 offset)
0041 {
0042     return QChar(static_cast<unsigned char>(p[offset + 0])
0043                  | (static_cast<unsigned char>(p[1]) << 8));
0044 }
0045 
0046 static quint32 GetUi32(const char *p, quint64 offset)
0047 {
0048     return (static_cast<unsigned char>(p[offset + 0])
0049             | (static_cast<unsigned char>(p[offset + 1]) << 8)
0050             | (static_cast<unsigned char>(p[offset + 2]) << 16)
0051             | (static_cast<unsigned char>(p[offset + 3]) << 24));
0052 }
0053 
0054 static quint64 GetUi64(const char *p, quint64 offset)
0055 {
0056     return (GetUi32(p, offset)
0057             | (static_cast<quint64>(GetUi32(p, offset + 4)) << 32));
0058 }
0059 
0060 static quint32 lzma2_dic_size_from_prop(int p)
0061 {
0062     return ((static_cast<quint32>(2) | (p & 1)) << ((p / 2) + 11));
0063 }
0064 
0065 /* clang-format on*/
0066 
0067 #define FILE_ATTRIBUTE_READONLY 1
0068 #define FILE_ATTRIBUTE_HIDDEN 2
0069 #define FILE_ATTRIBUTE_SYSTEM 4
0070 #define FILE_ATTRIBUTE_DIRECTORY 16
0071 #define FILE_ATTRIBUTE_ARCHIVE 32
0072 #define FILE_ATTRIBUTE_DEVICE 64
0073 #define FILE_ATTRIBUTE_NORMAL 128
0074 #define FILE_ATTRIBUTE_TEMPORARY 256
0075 #define FILE_ATTRIBUTE_SPARSE_FILE 512
0076 #define FILE_ATTRIBUTE_REPARSE_POINT 1024
0077 #define FILE_ATTRIBUTE_COMPRESSED 2048
0078 #define FILE_ATTRIBUTE_OFFLINE 0x1000
0079 #define FILE_ATTRIBUTE_ENCRYPTED 0x4000
0080 #define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000 /* trick for Unix */
0081 
0082 enum HeaderType {
0083     kEnd,
0084 
0085     kHeader,
0086 
0087     kArchiveProperties,
0088 
0089     kAdditionalStreamsInfo,
0090     kMainStreamsInfo,
0091     kFilesInfo,
0092 
0093     kPackInfo,
0094     kUnpackInfo,
0095     kSubStreamsInfo,
0096 
0097     kSize,
0098     kCRC,
0099 
0100     kFolder,
0101 
0102     kCodersUnpackSize,
0103     kNumUnpackStream,
0104 
0105     kEmptyStream,
0106     kEmptyFile,
0107     kAnti,
0108 
0109     kName,
0110     kCTime,
0111     kATime,
0112     kMTime,
0113     kAttributes,
0114     kComment,
0115 
0116     kEncodedHeader,
0117 
0118     kStartPos,
0119     kDummy,
0120 };
0121 
0122 // Method ID
0123 // static const quint64 k_Copy = 0x00;
0124 // static const quint64 k_Delta = 0x03;
0125 // static const quint64 k_x86 = 0x04; //BCJ
0126 // static const quint64 k_PPC = 0x05; // BIG Endian
0127 // static const quint64 k_IA64 = 0x06;
0128 // static const quint64 k_ARM = 0x07; // little Endian
0129 // static const quint64 k_ARM_Thumb = 0x08; // little Endian
0130 // static const quint64 k_SPARC = 0x09;
0131 static const quint64 k_LZMA2 = 0x21;
0132 // static const quint64 k_Swap2 = 0x020302;
0133 // static const quint64 k_Swap4 = 0x020304;
0134 static const quint64 k_LZMA = 0x030101;
0135 static const quint64 k_BCJ = 0x03030103;
0136 static const quint64 k_BCJ2 = 0x0303011B;
0137 // static const quint64 k_7zPPC = 0x03030205;
0138 // static const quint64 k_Alpha = 0x03030301;
0139 // static const quint64 k_7zIA64 = 0x03030401;
0140 // static const quint64 k_7zARM = 0x03030501;
0141 // static const quint64 k_M68 = 0x03030605; //Big Endian
0142 // static const quint64 k_ARMT = 0x03030701;
0143 // static const quint64 k_7zSPARC = 0x03030805;
0144 static const quint64 k_PPMD = 0x030401;
0145 // static const quint64 k_Experimental = 0x037F01;
0146 // static const quint64 k_Shrink = 0x040101;
0147 // static const quint64 k_Implode = 0x040106;
0148 // static const quint64 k_Deflate = 0x040108;
0149 // static const quint64 k_Deflate64 = 0x040109;
0150 // static const quint64 k_Imploding = 0x040110;
0151 // static const quint64 k_Jpeg = 0x040160;
0152 // static const quint64 k_WavPack = 0x040161;
0153 // static const quint64 k_PPMd = 0x040162;
0154 // static const quint64 k_wzAES = 0x040163;
0155 static const quint64 k_BZip2 = 0x040202;
0156 // static const quint64 k_Rar15 = 0x040301;
0157 // static const quint64 k_Rar20 = 0x040302;
0158 // static const quint64 k_Rar29 = 0x040303;
0159 // static const quint64 k_Arj = 0x040401; //1 2 3
0160 // static const quint64 k_Arj4 = 0x040402;
0161 // static const quint64 k_Z = 0x0405;
0162 // static const quint64 k_Lzh = 0x0406;
0163 // static const quint64 k_Cab = 0x0408;
0164 // static const quint64 k_DeflateNSIS = 0x040901;
0165 // static const quint64 k_Bzip2NSIS = 0x040902;
0166 static const quint64 k_AES = 0x06F10701;
0167 
0168 /**
0169  * A K7ZipFileEntry represents a file in a 7zip archive.
0170  */
0171 class K7ZipFileEntry : public KArchiveFile
0172 {
0173 public:
0174     K7ZipFileEntry(K7Zip *zip,
0175                    const QString &name,
0176                    int access,
0177                    const QDateTime &date,
0178                    const QString &user,
0179                    const QString &group,
0180                    const QString &symlink,
0181                    qint64 pos,
0182                    qint64 size,
0183                    const QByteArray &data);
0184 
0185     ~K7ZipFileEntry() override;
0186 
0187     /**
0188      * @return the content of this file.
0189      * Call data() with care (only once per file), this data isn't cached.
0190      */
0191     QByteArray data() const override;
0192 
0193     /**
0194      * This method returns QIODevice (internal class: KLimitedIODevice)
0195      * on top of the underlying QIODevice. This is obviously for reading only.
0196      *
0197      * WARNING: Note that the ownership of the device is being transferred to the caller,
0198      * who will have to delete it.
0199      *
0200      * The returned device auto-opens (in readonly mode), no need to open it.
0201      * @return the QIODevice of the file
0202      */
0203     QIODevice *createDevice() const override;
0204 
0205 private:
0206     const QByteArray m_data;
0207     QBuffer *m_buffer;
0208 };
0209 
0210 K7ZipFileEntry::K7ZipFileEntry(K7Zip *zip,
0211                                const QString &name,
0212                                int access,
0213                                const QDateTime &date,
0214                                const QString &user,
0215                                const QString &group,
0216                                const QString &symlink,
0217                                qint64 pos,
0218                                qint64 size,
0219                                const QByteArray &data)
0220     : KArchiveFile(zip, name, access, date, user, group, symlink, pos, size)
0221     , m_data(data)
0222     , m_buffer(new QBuffer)
0223 {
0224     m_buffer->setData(m_data);
0225     m_buffer->open(QIODevice::ReadOnly);
0226 }
0227 
0228 K7ZipFileEntry::~K7ZipFileEntry()
0229 {
0230     delete m_buffer;
0231 }
0232 
0233 QByteArray K7ZipFileEntry::data() const
0234 {
0235     return m_data.mid(position(), size());
0236 }
0237 
0238 QIODevice *K7ZipFileEntry::createDevice() const
0239 {
0240     return new KLimitedIODevice(m_buffer, position(), size());
0241 }
0242 
0243 class FileInfo
0244 {
0245 public:
0246     FileInfo()
0247         : size(0)
0248         , attributes(0)
0249         , crc(0)
0250         , attribDefined(false)
0251         , crcDefined(false)
0252         , hasStream(false)
0253         , isDir(false)
0254     {
0255     }
0256 
0257     QString path;
0258     quint64 size;
0259     quint32 attributes;
0260     quint32 crc;
0261     bool attribDefined;
0262     bool crcDefined;
0263     bool hasStream;
0264     bool isDir;
0265 };
0266 
0267 class Folder
0268 {
0269 public:
0270     class FolderInfo
0271     {
0272     public:
0273         FolderInfo()
0274             : numInStreams(0)
0275             , numOutStreams(0)
0276             , methodID(0)
0277         {
0278         }
0279 
0280         bool isSimpleCoder() const
0281         {
0282             return (numInStreams == 1) && (numOutStreams == 1);
0283         }
0284 
0285         int numInStreams;
0286         int numOutStreams;
0287         QVector<unsigned char> properties;
0288         quint64 methodID;
0289     };
0290 
0291     Folder()
0292         : unpackCRCDefined(false)
0293         , unpackCRC(0)
0294     {
0295     }
0296 
0297     ~Folder()
0298     {
0299         qDeleteAll(folderInfos);
0300     }
0301 
0302     Q_DISABLE_COPY(Folder)
0303 
0304     quint64 getUnpackSize() const
0305     {
0306         if (unpackSizes.isEmpty()) {
0307             return 0;
0308         }
0309         for (int i = unpackSizes.size() - 1; i >= 0; i--) {
0310             if (findBindPairForOutStream(i) < 0) {
0311                 return unpackSizes.at(i);
0312             }
0313         }
0314         return 0;
0315     }
0316 
0317     int getNumOutStreams() const
0318     {
0319         int result = 0;
0320         for (int i = 0; i < folderInfos.size(); i++) {
0321             result += folderInfos.at(i)->numOutStreams;
0322         }
0323         return result;
0324     }
0325 
0326     quint32 getCoderInStreamIndex(quint32 coderIndex) const
0327     {
0328         quint32 streamIndex = 0;
0329         for (quint32 i = 0; i < coderIndex; i++) {
0330             streamIndex += folderInfos.at(i)->numInStreams;
0331         }
0332         return streamIndex;
0333     }
0334 
0335     quint32 getCoderOutStreamIndex(quint32 coderIndex) const
0336     {
0337         quint32 streamIndex = 0;
0338         for (quint32 i = 0; i < coderIndex; i++) {
0339             streamIndex += folderInfos.at(i)->numOutStreams;
0340         }
0341         return streamIndex;
0342     }
0343 
0344     int findBindPairForInStream(size_t inStreamIndex) const
0345     {
0346         for (int i = 0; i < inIndexes.size(); i++) {
0347             if (inIndexes[i] == inStreamIndex) {
0348                 return i;
0349             }
0350         }
0351         return -1;
0352     }
0353 
0354     int findBindPairForOutStream(size_t outStreamIndex) const
0355     {
0356         for (int i = 0; i < outIndexes.size(); i++) {
0357             if (outIndexes[i] == outStreamIndex) {
0358                 return i;
0359             }
0360         }
0361         return -1;
0362     }
0363 
0364     int findPackStreamArrayIndex(size_t inStreamIndex) const
0365     {
0366         for (int i = 0; i < packedStreams.size(); i++) {
0367             if (packedStreams[i] == inStreamIndex) {
0368                 return i;
0369             }
0370         }
0371         return -1;
0372     }
0373 
0374     void findInStream(quint32 streamIndex, quint32 &coderIndex, quint32 &coderStreamIndex) const
0375     {
0376         for (coderIndex = 0; coderIndex < (quint32)folderInfos.size(); coderIndex++) {
0377             quint32 curSize = folderInfos[coderIndex]->numInStreams;
0378             if (streamIndex < curSize) {
0379                 coderStreamIndex = streamIndex;
0380                 return;
0381             }
0382             streamIndex -= curSize;
0383         }
0384     }
0385 
0386     void findOutStream(quint32 streamIndex, quint32 &coderIndex, quint32 &coderStreamIndex) const
0387     {
0388         for (coderIndex = 0; coderIndex < (quint32)folderInfos.size(); coderIndex++) {
0389             quint32 curSize = folderInfos[coderIndex]->numOutStreams;
0390             if (streamIndex < curSize) {
0391                 coderStreamIndex = streamIndex;
0392                 return;
0393             }
0394             streamIndex -= curSize;
0395         }
0396     }
0397 
0398     bool isEncrypted() const
0399     {
0400         for (int i = folderInfos.size() - 1; i >= 0; i--) {
0401             if (folderInfos.at(i)->methodID == k_AES) {
0402                 return true;
0403             }
0404         }
0405         return false;
0406     }
0407 
0408     // bool CheckStructure() const;
0409 
0410     bool unpackCRCDefined;
0411     quint32 unpackCRC;
0412     QVector<FolderInfo *> folderInfos;
0413     QVector<quint64> inIndexes;
0414     QVector<quint64> outIndexes;
0415     QVector<quint64> packedStreams;
0416     QVector<quint64> unpackSizes;
0417 };
0418 
0419 class Q_DECL_HIDDEN K7Zip::K7ZipPrivate
0420 {
0421 public:
0422     K7ZipPrivate(K7Zip *parent)
0423         : q(parent)
0424         , packPos(0)
0425         , numPackStreams(0)
0426         , buffer(nullptr)
0427         , pos(0)
0428         , end(0)
0429         , headerSize(0)
0430         , countSize(0)
0431         , m_currentFile(nullptr)
0432     {
0433     }
0434 
0435     ~K7ZipPrivate()
0436     {
0437         qDeleteAll(folders);
0438         qDeleteAll(fileInfos);
0439     }
0440 
0441     K7Zip *q;
0442 
0443     QVector<bool> packCRCsDefined;
0444     QVector<quint32> packCRCs;
0445     QVector<quint64> numUnpackStreamsInFolders;
0446 
0447     QVector<Folder *> folders;
0448     QVector<FileInfo *> fileInfos;
0449     // File information
0450     QVector<bool> cTimesDefined;
0451     QVector<quint64> cTimes;
0452     QVector<bool> aTimesDefined;
0453     QVector<quint64> aTimes;
0454     QVector<bool> mTimesDefined;
0455     QVector<quint64> mTimes;
0456     QVector<bool> startPositionsDefined;
0457     QVector<quint64> startPositions;
0458     QVector<int> fileInfoPopIDs;
0459 
0460     quint64 packPos;
0461     quint64 numPackStreams;
0462     QVector<quint64> packSizes;
0463     QVector<quint64> unpackSizes;
0464     QVector<bool> digestsDefined;
0465     QVector<quint32> digests;
0466 
0467     QVector<bool> isAnti;
0468 
0469     const char *buffer;
0470     quint64 pos;
0471     quint64 end;
0472     quint64 headerSize;
0473     quint64 countSize;
0474 
0475     // Write
0476     QByteArray header;
0477     QByteArray outData; // Store data in this buffer before compress and write in archive.
0478     K7ZipFileEntry *m_currentFile;
0479     QVector<KArchiveEntry *> m_entryList;
0480 
0481     void clear()
0482     {
0483         packCRCsDefined.clear();
0484         packCRCs.clear();
0485         numUnpackStreamsInFolders.clear();
0486         qDeleteAll(folders);
0487         folders.clear();
0488         qDeleteAll(fileInfos);
0489         fileInfos.clear();
0490         cTimesDefined.clear();
0491         cTimes.clear();
0492         aTimesDefined.clear();
0493         aTimes.clear();
0494         mTimesDefined.clear();
0495         mTimes.clear();
0496         startPositionsDefined.clear();
0497         startPositions.clear();
0498         fileInfoPopIDs.clear();
0499         packSizes.clear();
0500         unpackSizes.clear();
0501         digestsDefined.clear();
0502         digests.clear();
0503         isAnti.clear();
0504 
0505         buffer = nullptr;
0506         pos = 0;
0507         end = 0;
0508         headerSize = 0;
0509         countSize = 0;
0510     }
0511 
0512     // Read
0513     int readByte();
0514     quint32 readUInt32();
0515     quint64 readUInt64();
0516     quint64 readNumber();
0517     QString readString();
0518     void readHashDigests(int numItems, QVector<bool> &digestsDefined, QVector<quint32> &digests);
0519     void readBoolVector(int numItems, QVector<bool> &v);
0520     void readBoolVector2(int numItems, QVector<bool> &v);
0521     void skipData(int size);
0522     bool findAttribute(int attribute);
0523     bool readUInt64DefVector(int numFiles, QVector<quint64> &values, QVector<bool> &defined);
0524 
0525     Folder *folderItem();
0526     bool readMainStreamsInfo();
0527     bool readPackInfo();
0528     bool readUnpackInfo();
0529     bool readSubStreamsInfo();
0530     QByteArray readAndDecodePackedStreams(bool readMainStreamInfo = true);
0531 
0532     // Write
0533     void createItemsFromEntities(const KArchiveDirectory *, const QString &, QByteArray &);
0534     void writeByte(unsigned char b);
0535     void writeNumber(quint64 value);
0536     void writeBoolVector(const QVector<bool> &boolVector);
0537     void writeUInt32(quint32 value);
0538     void writeUInt64(quint64 value);
0539     void writeHashDigests(const QVector<bool> &digestsDefined, const QVector<quint32> &digests);
0540     void writeAlignedBoolHeader(const QVector<bool> &v, int numDefined, int type, unsigned itemSize);
0541     void writeUInt64DefVector(const QVector<quint64> &v, const QVector<bool> &defined, int type);
0542     void writeFolder(const Folder *folder);
0543     void writePackInfo(quint64 dataOffset, QVector<quint64> &packedSizes, QVector<bool> &packedCRCsDefined, QVector<quint32> &packedCRCs);
0544     void writeUnpackInfo(const QVector<Folder *> &folderItems);
0545     void writeSubStreamsInfo(const QVector<quint64> &unpackSizes, const QVector<bool> &digestsDefined, const QVector<quint32> &digests);
0546     void writeHeader(quint64 &headerOffset);
0547     void writeSignature();
0548     void writeStartHeader(const quint64 nextHeaderSize, const quint32 nextHeaderCRC, const quint64 nextHeaderOffset);
0549     QByteArray encodeStream(QVector<quint64> &packSizes, QVector<Folder *> &folds);
0550 };
0551 
0552 K7Zip::K7Zip(const QString &fileName)
0553     : KArchive(fileName)
0554     , d(new K7ZipPrivate(this))
0555 {
0556 }
0557 
0558 K7Zip::K7Zip(QIODevice *dev)
0559     : KArchive(dev)
0560     , d(new K7ZipPrivate(this))
0561 {
0562     Q_ASSERT(dev);
0563 }
0564 
0565 K7Zip::~K7Zip()
0566 {
0567     if (isOpen()) {
0568         close();
0569     }
0570 
0571     delete d;
0572 }
0573 
0574 int K7Zip::K7ZipPrivate::readByte()
0575 {
0576     if (!buffer || pos + 1 > end) {
0577         return -1;
0578     }
0579     return buffer[pos++];
0580 }
0581 
0582 quint32 K7Zip::K7ZipPrivate::readUInt32()
0583 {
0584     if (!buffer || (quint64)(pos + 4) > end) {
0585         qCDebug(KArchiveLog) << "error size";
0586         return 0;
0587     }
0588 
0589     quint32 res = GetUi32(buffer, pos);
0590     pos += 4;
0591     return res;
0592 }
0593 
0594 quint64 K7Zip::K7ZipPrivate::readUInt64()
0595 {
0596     if (!buffer || (quint64)(pos + 8) > end) {
0597         qCDebug(KArchiveLog) << "error size";
0598         return 0;
0599     }
0600 
0601     quint64 res = GetUi64(buffer, pos);
0602     pos += 8;
0603     return res;
0604 }
0605 
0606 quint64 K7Zip::K7ZipPrivate::readNumber()
0607 {
0608     if (!buffer || (quint64)(pos + 8) > end) {
0609         return 0;
0610     }
0611 
0612     unsigned char firstByte = buffer[pos++];
0613     unsigned char mask = 0x80;
0614     quint64 value = 0;
0615     for (int i = 0; i < 8; i++) {
0616         if ((firstByte & mask) == 0) {
0617             quint64 highPart = firstByte & (mask - 1);
0618             value += (highPart << (i * 8));
0619             return value;
0620         }
0621         value |= ((unsigned char)buffer[pos++] << (8 * i));
0622         mask >>= 1;
0623     }
0624     return value;
0625 }
0626 
0627 QString K7Zip::K7ZipPrivate::readString()
0628 {
0629     if (!buffer) {
0630         return QString();
0631     }
0632 
0633     const char *buf = buffer + pos;
0634     size_t rem = (end - pos) / 2 * 2;
0635     {
0636         size_t i;
0637         for (i = 0; i < rem; i += 2) {
0638             if (buf[i] == 0 && buf[i + 1] == 0) {
0639                 break;
0640             }
0641         }
0642         if (i == rem) {
0643             qCDebug(KArchiveLog) << "read string error";
0644             return QString();
0645         }
0646         rem = i;
0647     }
0648 
0649     int len = (int)(rem / 2);
0650     if (len < 0 || (size_t)len * 2 != rem) {
0651         qCDebug(KArchiveLog) << "read string unsupported";
0652         return QString();
0653     }
0654 
0655     QString p;
0656     for (int i = 0; i < len; i++, buf += 2) {
0657         p += GetUi16(buf, 0);
0658     }
0659 
0660     pos += rem + 2;
0661     return p;
0662 }
0663 
0664 void K7Zip::K7ZipPrivate::skipData(int size)
0665 {
0666     if (!buffer || pos + size > end) {
0667         return;
0668     }
0669     pos += size;
0670 }
0671 
0672 bool K7Zip::K7ZipPrivate::findAttribute(int attribute)
0673 {
0674     if (!buffer) {
0675         return false;
0676     }
0677 
0678     for (;;) {
0679         int type = readByte();
0680         if (type == attribute) {
0681             return true;
0682         }
0683         if (type == kEnd) {
0684             return false;
0685         }
0686         skipData(readNumber());
0687     }
0688 }
0689 
0690 void K7Zip::K7ZipPrivate::readBoolVector(int numItems, QVector<bool> &v)
0691 {
0692     if (!buffer) {
0693         return;
0694     }
0695 
0696     unsigned char b = 0;
0697     unsigned char mask = 0;
0698     for (int i = 0; i < numItems; i++) {
0699         if (mask == 0) {
0700             b = readByte();
0701             mask = 0x80;
0702         }
0703         v.append((b & mask) != 0);
0704         mask >>= 1;
0705     }
0706 }
0707 
0708 void K7Zip::K7ZipPrivate::readBoolVector2(int numItems, QVector<bool> &v)
0709 {
0710     if (!buffer) {
0711         return;
0712     }
0713 
0714     int allAreDefined = readByte();
0715     if (allAreDefined == 0) {
0716         readBoolVector(numItems, v);
0717         return;
0718     }
0719 
0720     for (int i = 0; i < numItems; i++) {
0721         v.append(true);
0722     }
0723 }
0724 
0725 void K7Zip::K7ZipPrivate::readHashDigests(int numItems, QVector<bool> &digestsDefined, QVector<quint32> &digests)
0726 {
0727     if (!buffer) {
0728         return;
0729     }
0730 
0731     readBoolVector2(numItems, digestsDefined);
0732     for (int i = 0; i < numItems; i++) {
0733         quint32 crc = 0;
0734         if (digestsDefined[i]) {
0735             crc = GetUi32(buffer, pos);
0736             pos += 4;
0737         }
0738         digests.append(crc);
0739     }
0740 }
0741 
0742 Folder *K7Zip::K7ZipPrivate::folderItem()
0743 {
0744     if (!buffer) {
0745         return nullptr;
0746     }
0747 
0748     Folder *folder = new Folder;
0749     int numCoders = readNumber();
0750 
0751     quint64 numInStreamsTotal = 0;
0752     quint64 numOutStreamsTotal = 0;
0753     for (int i = 0; i < numCoders; ++i) {
0754         // BYTE
0755         //    {
0756         //      0:3 CodecIdSize
0757         //      4:  Is Complex Coder
0758         //      5:  There Are Attributes
0759         //      6:  Reserved
0760         //      7:  There are more alternative methods. (Not used
0761         //      anymore, must be 0).
0762         //    }
0763         unsigned char coderInfo = readByte();
0764         int codecIdSize = (coderInfo & 0xF);
0765         if (codecIdSize > 8) {
0766             qCDebug(KArchiveLog) << "unsupported codec id size";
0767             delete folder;
0768             return nullptr;
0769         }
0770         Folder::FolderInfo *info = new Folder::FolderInfo();
0771         std::unique_ptr<unsigned char[]> codecID(new unsigned char[codecIdSize]);
0772         for (int i = 0; i < codecIdSize; ++i) {
0773             codecID[i] = readByte();
0774         }
0775 
0776         int id = 0;
0777         for (int j = 0; j < codecIdSize; j++) {
0778             id |= codecID[codecIdSize - 1 - j] << (8 * j);
0779         }
0780         info->methodID = id;
0781 
0782         // if (Is Complex Coder)
0783         if ((coderInfo & 0x10) != 0) {
0784             info->numInStreams = readNumber();
0785             info->numOutStreams = readNumber();
0786         } else {
0787             info->numInStreams = 1;
0788             info->numOutStreams = 1;
0789         }
0790 
0791         // if (There Are Attributes)
0792         if ((coderInfo & 0x20) != 0) {
0793             int propertiesSize = readNumber();
0794             for (int i = 0; i < propertiesSize; ++i) {
0795                 info->properties.append(readByte());
0796             }
0797         }
0798 
0799         if ((coderInfo & 0x80) != 0) {
0800             qCDebug(KArchiveLog) << "unsupported";
0801             delete info;
0802             delete folder;
0803             return nullptr;
0804         }
0805 
0806         numInStreamsTotal += info->numInStreams;
0807         numOutStreamsTotal += info->numOutStreams;
0808         folder->folderInfos.append(info);
0809     }
0810 
0811     int numBindPairs = numOutStreamsTotal - 1;
0812     for (int i = 0; i < numBindPairs; i++) {
0813         folder->inIndexes.append(readNumber());
0814         folder->outIndexes.append(readNumber());
0815     }
0816 
0817     int numPackedStreams = numInStreamsTotal - numBindPairs;
0818     if (numPackedStreams > 1) {
0819         for (int i = 0; i < numPackedStreams; ++i) {
0820             folder->packedStreams.append(readNumber());
0821         }
0822     } else {
0823         if (numPackedStreams == 1) {
0824             for (quint64 i = 0; i < numInStreamsTotal; i++) {
0825                 if (folder->findBindPairForInStream(i) < 0) {
0826                     folder->packedStreams.append(i);
0827                     break;
0828                 }
0829             }
0830             if (folder->packedStreams.size() != 1) {
0831                 delete folder;
0832                 return nullptr;
0833             }
0834         }
0835     }
0836     return folder;
0837 }
0838 
0839 bool K7Zip::K7ZipPrivate::readUInt64DefVector(int numFiles, QVector<quint64> &values, QVector<bool> &defined)
0840 {
0841     if (!buffer) {
0842         return false;
0843     }
0844 
0845     readBoolVector2(numFiles, defined);
0846 
0847     int external = readByte();
0848     if (external != 0) {
0849         int dataIndex = readNumber();
0850         if (dataIndex < 0 /*|| dataIndex >= dataVector->Size()*/) {
0851             qCDebug(KArchiveLog) << "wrong data index";
0852             return false;
0853         }
0854 
0855         // TODO : go to the new index
0856     }
0857 
0858     for (int i = 0; i < numFiles; i++) {
0859         quint64 t = 0;
0860         if (defined[i]) {
0861             t = readUInt64();
0862         }
0863         values.append(t);
0864     }
0865     return true;
0866 }
0867 
0868 bool K7Zip::K7ZipPrivate::readPackInfo()
0869 {
0870     if (!buffer) {
0871         return false;
0872     }
0873 
0874     packPos = readNumber();
0875     numPackStreams = readNumber();
0876     packSizes.clear();
0877 
0878     packCRCsDefined.clear();
0879     packCRCs.clear();
0880 
0881     if (!findAttribute(kSize)) {
0882         qCDebug(KArchiveLog) << "kSize not found";
0883         return false;
0884     }
0885 
0886     for (quint64 i = 0; i < numPackStreams; ++i) {
0887         packSizes.append(readNumber());
0888     }
0889 
0890     for (;;) {
0891         int type = readByte();
0892         if (type == kEnd) {
0893             break;
0894         }
0895         if (type == kCRC) {
0896             readHashDigests(numPackStreams, packCRCsDefined, packCRCs);
0897             continue;
0898         }
0899         skipData(readNumber());
0900     }
0901 
0902     if (packCRCs.isEmpty()) {
0903         for (quint64 i = 0; i < numPackStreams; ++i) {
0904             packCRCsDefined.append(false);
0905             packCRCs.append(0);
0906         }
0907     }
0908     return true;
0909 }
0910 
0911 bool K7Zip::K7ZipPrivate::readUnpackInfo()
0912 {
0913     if (!buffer) {
0914         return false;
0915     }
0916 
0917     if (!findAttribute(kFolder)) {
0918         qCDebug(KArchiveLog) << "kFolder not found";
0919         return false;
0920     }
0921 
0922     int numFolders = readNumber();
0923     qDeleteAll(folders);
0924     folders.clear();
0925     int external = readByte();
0926     switch (external) {
0927     case 0: {
0928         for (int i = 0; i < numFolders; ++i) {
0929             folders.append(folderItem());
0930         }
0931         break;
0932     }
0933     case 1: {
0934         int dataIndex = readNumber();
0935         if (dataIndex < 0 /*|| dataIndex >= dataVector->Size()*/) {
0936             qCDebug(KArchiveLog) << "wrong data index";
0937         }
0938         // TODO : go to the new index
0939         break;
0940     }
0941     default:
0942         qCDebug(KArchiveLog) << "external error";
0943         return false;
0944     }
0945 
0946     if (!findAttribute(kCodersUnpackSize)) {
0947         qCDebug(KArchiveLog) << "kCodersUnpackSize not found";
0948         return false;
0949     }
0950 
0951     for (int i = 0; i < numFolders; ++i) {
0952         Folder *folder = folders.at(i);
0953         int numOutStreams = folder->getNumOutStreams();
0954         for (int j = 0; j < numOutStreams; ++j) {
0955             folder->unpackSizes.append(readNumber());
0956         }
0957     }
0958 
0959     for (;;) {
0960         int type = readByte();
0961         if (type == kEnd) {
0962             break;
0963         }
0964         if (type == kCRC) {
0965             QVector<bool> crcsDefined;
0966             QVector<quint32> crcs;
0967             readHashDigests(numFolders, crcsDefined, crcs);
0968             for (int i = 0; i < numFolders; i++) {
0969                 Folder *folder = folders.at(i);
0970                 folder->unpackCRCDefined = crcsDefined[i];
0971                 folder->unpackCRC = crcs[i];
0972             }
0973             continue;
0974         }
0975         skipData(readNumber());
0976     }
0977     return true;
0978 }
0979 
0980 bool K7Zip::K7ZipPrivate::readSubStreamsInfo()
0981 {
0982     if (!buffer) {
0983         return false;
0984     }
0985 
0986     numUnpackStreamsInFolders.clear();
0987 
0988     int type;
0989     for (;;) {
0990         type = readByte();
0991         if (type == kNumUnpackStream) {
0992             for (int i = 0; i < folders.size(); i++) {
0993                 numUnpackStreamsInFolders.append(readNumber());
0994             }
0995             continue;
0996         }
0997         if (type == kCRC || type == kSize) {
0998             break;
0999         }
1000         if (type == kEnd) {
1001             break;
1002         }
1003         skipData(readNumber());
1004     }
1005 
1006     if (numUnpackStreamsInFolders.isEmpty()) {
1007         for (int i = 0; i < folders.size(); i++) {
1008             numUnpackStreamsInFolders.append(1);
1009         }
1010     }
1011 
1012     for (int i = 0; i < numUnpackStreamsInFolders.size(); i++) {
1013         quint64 numSubstreams = numUnpackStreamsInFolders.at(i);
1014         if (numSubstreams == 0) {
1015             continue;
1016         }
1017         quint64 sum = 0;
1018         for (quint64 j = 1; j < numSubstreams; j++) {
1019             if (type == kSize) {
1020                 int size = readNumber();
1021                 unpackSizes.append(size);
1022                 sum += size;
1023             }
1024         }
1025         unpackSizes.append(folders.at(i)->getUnpackSize() - sum);
1026     }
1027 
1028     if (type == kSize) {
1029         type = readByte();
1030     }
1031 
1032     int numDigests = 0;
1033     int numDigestsTotal = 0;
1034     for (int i = 0; i < folders.size(); i++) {
1035         quint64 numSubstreams = numUnpackStreamsInFolders.at(i);
1036         if (numSubstreams != 1 || !folders.at(i)->unpackCRCDefined) {
1037             numDigests += numSubstreams;
1038         }
1039         numDigestsTotal += numSubstreams;
1040     }
1041 
1042     for (;;) {
1043         if (type == kCRC) {
1044             QVector<bool> digestsDefined2;
1045             QVector<quint32> digests2;
1046             readHashDigests(numDigests, digestsDefined2, digests2);
1047             int digestIndex = 0;
1048             for (int i = 0; i < folders.size(); i++) {
1049                 quint64 numSubstreams = numUnpackStreamsInFolders.at(i);
1050                 const Folder *folder = folders.at(i);
1051                 if (numSubstreams == 1 && folder->unpackCRCDefined) {
1052                     digestsDefined.append(true);
1053                     digests.append(folder->unpackCRC);
1054                 } else {
1055                     for (quint64 j = 0; j < numSubstreams; j++, digestIndex++) {
1056                         digestsDefined.append(digestsDefined2[digestIndex]);
1057                         digests.append(digests2[digestIndex]);
1058                     }
1059                 }
1060             }
1061         } else if (type == kEnd) {
1062             if (digestsDefined.isEmpty()) {
1063                 for (int i = 0; i < numDigestsTotal; i++) {
1064                     digestsDefined.append(false);
1065                     digests.append(0);
1066                 }
1067             }
1068 
1069             break;
1070         } else {
1071             skipData(readNumber());
1072         }
1073 
1074         type = readByte();
1075     }
1076     return true;
1077 }
1078 
1079 #define TICKSPERSEC 10000000
1080 #define TICKSPERMSEC 10000
1081 #define SECSPERDAY 86400
1082 #define SECSPERHOUR 3600
1083 #define SECSPERMIN 60
1084 #define EPOCHWEEKDAY 1 /* Jan 1, 1601 was Monday */
1085 #define DAYSPERWEEK 7
1086 #define DAYSPERQUADRICENTENNIUM (365 * 400 + 97)
1087 #define DAYSPERNORMALQUADRENNIUM (365 * 4 + 1)
1088 #define TICKS_1601_TO_1970 (SECS_1601_TO_1970 * TICKSPERSEC)
1089 #define SECS_1601_TO_1970 ((369 * 365 + 89) * (unsigned long long)SECSPERDAY)
1090 
1091 static uint toTimeT(const long long liTime)
1092 {
1093     long long time = liTime / TICKSPERSEC;
1094 
1095     /* The native version of RtlTimeToTimeFields does not take leap seconds
1096      * into account */
1097 
1098     /* Split the time into days and seconds within the day */
1099     long int days = time / SECSPERDAY;
1100     int secondsInDay = time % SECSPERDAY;
1101 
1102     /* compute time of day */
1103     short hour = (short)(secondsInDay / SECSPERHOUR);
1104     secondsInDay = secondsInDay % SECSPERHOUR;
1105     short minute = (short)(secondsInDay / SECSPERMIN);
1106     short second = (short)(secondsInDay % SECSPERMIN);
1107 
1108     /* compute year, month and day of month. */
1109     long int cleaps = (3 * ((4 * days + 1227) / DAYSPERQUADRICENTENNIUM) + 3) / 4;
1110     days += 28188 + cleaps;
1111     long int years = (20 * days - 2442) / (5 * DAYSPERNORMALQUADRENNIUM);
1112     long int yearday = days - (years * DAYSPERNORMALQUADRENNIUM) / 4;
1113     long int months = (64 * yearday) / 1959;
1114     /* the result is based on a year starting on March.
1115      * To convert take 12 from Januari and Februari and
1116      * increase the year by one. */
1117 
1118     short month;
1119     short year;
1120     if (months < 14) {
1121         month = (short)(months - 1);
1122         year = (short)(years + 1524);
1123     } else {
1124         month = (short)(months - 13);
1125         year = (short)(years + 1525);
1126     }
1127     /* calculation of day of month is based on the wonderful
1128      * sequence of INT( n * 30.6): it reproduces the·
1129      * 31-30-31-30-31-31 month lengths exactly for small n's */
1130     short day = (short)(yearday - (1959 * months) / 64);
1131 
1132     QDateTime t(QDate(year, month, day), QTime(hour, minute, second));
1133     t.setTimeSpec(Qt::UTC);
1134     return t.toSecsSinceEpoch();
1135 }
1136 
1137 long long rtlSecondsSince1970ToSpecTime(quint32 seconds)
1138 {
1139     long long secs = seconds * (long long)TICKSPERSEC + TICKS_1601_TO_1970;
1140     return secs;
1141 }
1142 
1143 bool K7Zip::K7ZipPrivate::readMainStreamsInfo()
1144 {
1145     if (!buffer) {
1146         return false;
1147     }
1148 
1149     quint32 type;
1150     for (;;) {
1151         type = readByte();
1152         if (type > ((quint32)1 << 30)) {
1153             qCDebug(KArchiveLog) << "type error";
1154             return false;
1155         }
1156         switch (type) {
1157         case kEnd:
1158             return true;
1159         case kPackInfo: {
1160             if (!readPackInfo()) {
1161                 qCDebug(KArchiveLog) << "error during read pack information";
1162                 return false;
1163             }
1164             break;
1165         }
1166         case kUnpackInfo: {
1167             if (!readUnpackInfo()) {
1168                 qCDebug(KArchiveLog) << "error during read pack information";
1169                 return false;
1170             }
1171             break;
1172         }
1173         case kSubStreamsInfo: {
1174             if (!readSubStreamsInfo()) {
1175                 qCDebug(KArchiveLog) << "error during read substreams information";
1176                 return false;
1177             }
1178             break;
1179         }
1180         default:
1181             qCDebug(KArchiveLog) << "Wrong type";
1182             return false;
1183         }
1184     }
1185 
1186     qCDebug(KArchiveLog) << "should not reach";
1187     return false;
1188 }
1189 
1190 static bool getInStream(const Folder *folder, quint32 streamIndex, int &seqInStream, quint32 &coderIndex)
1191 {
1192     for (int i = 0; i < folder->packedStreams.size(); i++) {
1193         if (folder->packedStreams[i] == streamIndex) {
1194             seqInStream = i;
1195             return true;
1196         }
1197     }
1198 
1199     int binderIndex = folder->findBindPairForInStream(streamIndex);
1200     if (binderIndex < 0) {
1201         return false;
1202     }
1203 
1204     quint32 coderStreamIndex;
1205     folder->findOutStream(folder->outIndexes[binderIndex], coderIndex, coderStreamIndex);
1206 
1207     quint32 startIndex = folder->getCoderInStreamIndex(coderIndex);
1208 
1209     if (folder->folderInfos[coderIndex]->numInStreams > 1) {
1210         return false;
1211     }
1212 
1213     for (int i = 0; i < (int)folder->folderInfos[coderIndex]->numInStreams; i++) {
1214         getInStream(folder, startIndex + i, seqInStream, coderIndex);
1215     }
1216 
1217     return true;
1218 }
1219 
1220 static bool getOutStream(const Folder *folder, quint32 streamIndex, int &seqOutStream)
1221 {
1222     QVector<quint32> outStreams;
1223     quint32 outStreamIndex = 0;
1224     for (int i = 0; i < folder->folderInfos.size(); i++) {
1225         const Folder::FolderInfo *coderInfo = folder->folderInfos.at(i);
1226 
1227         for (int j = 0; j < coderInfo->numOutStreams; j++, outStreamIndex++) {
1228             if (folder->findBindPairForOutStream(outStreamIndex) < 0) {
1229                 outStreams.append(outStreamIndex);
1230             }
1231         }
1232     }
1233 
1234     for (int i = 0; i < outStreams.size(); i++) {
1235         if (outStreams[i] == streamIndex) {
1236             seqOutStream = i;
1237             return true;
1238         }
1239     }
1240 
1241     int binderIndex = folder->findBindPairForOutStream(streamIndex);
1242     if (binderIndex < 0) {
1243         return false;
1244     }
1245 
1246     quint32 coderIndex;
1247     quint32 coderStreamIndex;
1248     folder->findInStream(folder->inIndexes[binderIndex], coderIndex, coderStreamIndex);
1249 
1250     quint32 startIndex = folder->getCoderOutStreamIndex(coderIndex);
1251 
1252     if (folder->folderInfos[coderIndex]->numOutStreams > 1) {
1253         return false;
1254     }
1255 
1256     for (int i = 0; i < (int)folder->folderInfos[coderIndex]->numOutStreams; i++) {
1257         getOutStream(folder, startIndex + i, seqOutStream);
1258     }
1259 
1260     return true;
1261 }
1262 
1263 const int kNumTopBits = 24;
1264 const quint32 kTopValue = (1 << kNumTopBits);
1265 
1266 class RangeDecoder
1267 {
1268     int pos;
1269 
1270 public:
1271     QByteArray stream;
1272     quint32 range;
1273     quint32 code;
1274 
1275     RangeDecoder(const QByteArray &s)
1276         : pos(0)
1277         , stream(s)
1278         , range(0xFFFFFFFF)
1279         , code(0)
1280     {
1281         for (int i = 0; i < 5; i++) {
1282             code = (code << 8) | readByte();
1283         }
1284     }
1285 
1286     unsigned char readByte()
1287     {
1288         return stream[pos++];
1289     }
1290 
1291     void normalize()
1292     {
1293         while (range < kTopValue) {
1294             code = (code << 8) | readByte();
1295             range <<= 8;
1296         }
1297     }
1298 
1299     quint32 getThreshold(quint32 total)
1300     {
1301         return (code) / (range /= total);
1302     }
1303 
1304     void decode(quint32 start, quint32 size)
1305     {
1306         code -= start * range;
1307         range *= size;
1308         normalize();
1309     }
1310 
1311     quint32 decodeDirectBits(int numTotalBits)
1312     {
1313         quint32 r = range;
1314         quint32 c = code;
1315         quint32 result = 0;
1316         for (int i = numTotalBits; i != 0; i--) {
1317             r >>= 1;
1318             quint32 t = (c - r) >> 31;
1319             c -= r & (t - 1);
1320             result = (result << 1) | (1 - t);
1321 
1322             if (r < kTopValue) {
1323                 c = (c << 8) | readByte();
1324                 r <<= 8;
1325             }
1326         }
1327         range = r;
1328         code = c;
1329         return result;
1330     }
1331 
1332     quint32 DecodeBit(quint32 size0, quint32 numTotalBits)
1333     {
1334         quint32 newBound = (range >> numTotalBits) * size0;
1335         quint32 symbol;
1336         if (code < newBound) {
1337             symbol = 0;
1338             range = newBound;
1339         } else {
1340             symbol = 1;
1341             code -= newBound;
1342             range -= newBound;
1343         }
1344         normalize();
1345         return symbol;
1346     }
1347 };
1348 
1349 const int kNumBitModelTotalBits = 11;
1350 const quint32 kBitModelTotal = (1 << kNumBitModelTotalBits);
1351 
1352 template<int numMoveBits>
1353 class CBitModel
1354 {
1355 public:
1356     quint32 prob;
1357     void updateModel(quint32 symbol)
1358     {
1359         if (symbol == 0) {
1360             prob += (kBitModelTotal - prob) >> numMoveBits;
1361         } else {
1362             prob -= (prob) >> numMoveBits;
1363         }
1364     }
1365 
1366     void init()
1367     {
1368         prob = kBitModelTotal / 2;
1369     }
1370 };
1371 
1372 template<int numMoveBits>
1373 class CBitDecoder : public CBitModel<numMoveBits>
1374 {
1375 public:
1376     quint32 decode(RangeDecoder *decoder)
1377     {
1378         quint32 newBound = (decoder->range >> kNumBitModelTotalBits) * this->prob;
1379         if (decoder->code < newBound) {
1380             decoder->range = newBound;
1381             this->prob += (kBitModelTotal - this->prob) >> numMoveBits;
1382             if (decoder->range < kTopValue) {
1383                 decoder->code = (decoder->code << 8) | decoder->readByte();
1384                 decoder->range <<= 8;
1385             }
1386             return 0;
1387         } else {
1388             decoder->range -= newBound;
1389             decoder->code -= newBound;
1390             this->prob -= (this->prob) >> numMoveBits;
1391             if (decoder->range < kTopValue) {
1392                 decoder->code = (decoder->code << 8) | decoder->readByte();
1393                 decoder->range <<= 8;
1394             }
1395             return 1;
1396         }
1397     }
1398 };
1399 
1400 inline bool isJcc(unsigned char b0, unsigned char b1)
1401 {
1402     return (b0 == 0x0F && (b1 & 0xF0) == 0x80);
1403 }
1404 inline bool isJ(unsigned char b0, unsigned char b1)
1405 {
1406     return ((b1 & 0xFE) == 0xE8 || isJcc(b0, b1));
1407 }
1408 inline unsigned getIndex(unsigned char b0, unsigned char b1)
1409 {
1410     return ((b1 == 0xE8) ? b0 : ((b1 == 0xE9) ? 256 : 257));
1411 }
1412 
1413 const int kNumMoveBits = 5;
1414 
1415 static QByteArray decodeBCJ2(const QByteArray &mainStream, const QByteArray &callStream, const QByteArray &jumpStream, const QByteArray &rangeBuffer)
1416 {
1417     unsigned char prevByte = 0;
1418     QByteArray outStream;
1419     int mainStreamPos = 0;
1420     int callStreamPos = 0;
1421     int jumpStreamPos = 0;
1422 
1423     RangeDecoder rangeDecoder(rangeBuffer);
1424 
1425     QVector<CBitDecoder<kNumMoveBits>> statusDecoder(256 + 2);
1426 
1427     for (int i = 0; i < 256 + 2; i++) {
1428         statusDecoder[i].init();
1429     }
1430 
1431     for (;;) {
1432         quint32 i;
1433         unsigned char b = 0;
1434         const quint32 kBurstSize = (1 << 18);
1435         for (i = 0; i < kBurstSize; i++) {
1436             if (mainStreamPos == mainStream.size()) {
1437                 return outStream;
1438             }
1439 
1440             b = mainStream[mainStreamPos++];
1441             outStream.append(b);
1442 
1443             if (isJ(prevByte, b)) {
1444                 break;
1445             }
1446             prevByte = b;
1447         }
1448 
1449         if (i == kBurstSize) {
1450             continue;
1451         }
1452 
1453         unsigned index = getIndex(prevByte, b);
1454         if (statusDecoder[index].decode(&rangeDecoder) == 1) {
1455             if (b == 0xE8) {
1456                 if (callStreamPos + 4 > callStream.size()) {
1457                     return QByteArray();
1458                 }
1459             } else {
1460                 if (jumpStreamPos + 4 > jumpStream.size()) {
1461                     return QByteArray();
1462                 }
1463             }
1464             quint32 src = 0;
1465             for (int i = 0; i < 4; i++) {
1466                 unsigned char b0;
1467                 if (b == 0xE8) {
1468                     b0 = callStream[callStreamPos++];
1469                 } else {
1470                     b0 = jumpStream[jumpStreamPos++];
1471                 }
1472                 src <<= 8;
1473                 src |= ((quint32)b0);
1474             }
1475 
1476             quint32 dest = src - (quint32(outStream.size()) + 4);
1477             outStream.append((unsigned char)(dest));
1478             outStream.append((unsigned char)(dest >> 8));
1479             outStream.append((unsigned char)(dest >> 16));
1480             outStream.append((unsigned char)(dest >> 24));
1481             prevByte = (unsigned char)(dest >> 24);
1482         } else {
1483             prevByte = b;
1484         }
1485     }
1486 }
1487 
1488 QByteArray K7Zip::K7ZipPrivate::readAndDecodePackedStreams(bool readMainStreamInfo)
1489 {
1490     if (!buffer) {
1491         return QByteArray();
1492     }
1493 
1494     if (readMainStreamInfo) {
1495         readMainStreamsInfo();
1496     }
1497 
1498     QByteArray inflatedData;
1499 
1500     quint64 startPos = 32 + packPos;
1501     for (int i = 0; i < folders.size(); i++) {
1502         const Folder *folder = folders.at(i);
1503         quint64 unpackSize64 = folder->getUnpackSize();
1504         size_t unpackSize = (size_t)unpackSize64;
1505         if (unpackSize != unpackSize64) {
1506             qCDebug(KArchiveLog) << "unsupported";
1507             return inflatedData;
1508         }
1509 
1510         // Find main coder
1511         quint32 mainCoderIndex = 0;
1512         QVector<int> outStreamIndexed;
1513         int outStreamIndex = 0;
1514         for (int j = 0; j < folder->folderInfos.size(); j++) {
1515             const Folder::FolderInfo *info = folder->folderInfos[j];
1516             for (int k = 0; k < info->numOutStreams; k++, outStreamIndex++) {
1517                 if (folder->findBindPairForOutStream(outStreamIndex) < 0) {
1518                     outStreamIndexed.append(outStreamIndex);
1519                     break;
1520                 }
1521             }
1522         }
1523 
1524         quint32 temp = 0;
1525         if (!outStreamIndexed.isEmpty()) {
1526             folder->findOutStream(outStreamIndexed[0], mainCoderIndex, temp);
1527         }
1528 
1529         quint32 startInIndex = folder->getCoderInStreamIndex(mainCoderIndex);
1530         quint32 startOutIndex = folder->getCoderOutStreamIndex(mainCoderIndex);
1531 
1532         Folder::FolderInfo *mainCoder = folder->folderInfos[mainCoderIndex];
1533 
1534         QVector<int> seqInStreams;
1535         QVector<quint32> coderIndexes;
1536         seqInStreams.reserve(mainCoder->numInStreams);
1537         coderIndexes.reserve(mainCoder->numInStreams);
1538         for (int j = 0; j < (int)mainCoder->numInStreams; j++) {
1539             int seqInStream;
1540             quint32 coderIndex;
1541             getInStream(folder, startInIndex + j, seqInStream, coderIndex);
1542             seqInStreams.append(seqInStream);
1543             coderIndexes.append(coderIndex);
1544         }
1545 
1546         QVector<int> seqOutStreams;
1547         seqOutStreams.reserve(mainCoder->numOutStreams);
1548         for (int j = 0; j < (int)mainCoder->numOutStreams; j++) {
1549             int seqOutStream;
1550             getOutStream(folder, startOutIndex + j, seqOutStream);
1551             seqOutStreams.append(seqOutStream);
1552         }
1553 
1554         QVector<QByteArray> datas;
1555         for (int j = 0; j < (int)mainCoder->numInStreams; j++) {
1556             int size = packSizes[j + i];
1557             std::unique_ptr<char[]> encodedBuffer(new char[size]);
1558             QIODevice *dev = q->device();
1559             dev->seek(startPos);
1560             quint64 n = dev->read(encodedBuffer.get(), size);
1561             if (n != (quint64)size) {
1562                 qCDebug(KArchiveLog) << "Failed read next size, should read " << size << ", read " << n;
1563                 return inflatedData;
1564             }
1565             QByteArray deflatedData(encodedBuffer.get(), size);
1566             datas.append(deflatedData);
1567             startPos += size;
1568             pos += size;
1569             headerSize += size;
1570         }
1571 
1572         QVector<QByteArray> inflatedDatas;
1573         QByteArray deflatedData;
1574         for (int j = 0; j < seqInStreams.size(); ++j) {
1575             Folder::FolderInfo *coder = nullptr;
1576             if ((quint32)j != mainCoderIndex) {
1577                 coder = folder->folderInfos[coderIndexes[j]];
1578             } else {
1579                 coder = folder->folderInfos[mainCoderIndex];
1580             }
1581 
1582             deflatedData = datas[seqInStreams[j]];
1583 
1584             KFilterBase *filter = nullptr;
1585 
1586             switch (coder->methodID) {
1587             case k_LZMA:
1588                 filter = KCompressionDevice::filterForCompressionType(KCompressionDevice::Xz);
1589                 if (!filter) {
1590                     qCDebug(KArchiveLog) << "filter not found";
1591                     return QByteArray();
1592                 }
1593                 static_cast<KXzFilter *>(filter)->init(QIODevice::ReadOnly, KXzFilter::LZMA, coder->properties);
1594                 break;
1595             case k_LZMA2:
1596                 filter = KCompressionDevice::filterForCompressionType(KCompressionDevice::Xz);
1597                 if (!filter) {
1598                     qCDebug(KArchiveLog) << "filter not found";
1599                     return QByteArray();
1600                 }
1601                 static_cast<KXzFilter *>(filter)->init(QIODevice::ReadOnly, KXzFilter::LZMA2, coder->properties);
1602                 break;
1603             case k_PPMD: {
1604                 /*if (coder->properties.size() == 5) {
1605                     //Byte order = *(const Byte *)coder.Props;
1606                     qint32 dicSize = ((unsigned char)coder->properties[1]        |
1607                                      (((unsigned char)coder->properties[2]) <<  8) |
1608                                      (((unsigned char)coder->properties[3]) << 16) |
1609                                      (((unsigned char)coder->properties[4]) << 24));
1610                 }*/
1611                 break;
1612             }
1613             case k_AES:
1614                 if (coder->properties.size() >= 1) {
1615                     // const Byte *data = (const Byte *)coder.Props;
1616                     // Byte firstByte = *data++;
1617                     // UInt32 numCyclesPower = firstByte & 0x3F;
1618                 }
1619                 break;
1620             case k_BCJ:
1621                 filter = KCompressionDevice::filterForCompressionType(KCompressionDevice::Xz);
1622                 if (!filter) {
1623                     qCDebug(KArchiveLog) << "filter not found";
1624                     return QByteArray();
1625                 }
1626                 static_cast<KXzFilter *>(filter)->init(QIODevice::ReadOnly, KXzFilter::BCJ, coder->properties);
1627                 break;
1628             case k_BCJ2: {
1629                 QByteArray bcj2 = decodeBCJ2(inflatedDatas[0], inflatedDatas[1], inflatedDatas[2], deflatedData);
1630                 inflatedDatas.clear();
1631                 inflatedDatas.append(bcj2);
1632                 break;
1633             }
1634             case k_BZip2:
1635                 filter = KCompressionDevice::filterForCompressionType(KCompressionDevice::BZip2);
1636                 if (!filter) {
1637                     qCDebug(KArchiveLog) << "filter not found";
1638                     return QByteArray();
1639                 }
1640                 filter->init(QIODevice::ReadOnly);
1641                 break;
1642             }
1643 
1644             if (coder->methodID == k_BCJ2) {
1645                 continue;
1646             }
1647 
1648             if (!filter) {
1649                 return QByteArray();
1650             }
1651 
1652             filter->setInBuffer(deflatedData.data(), deflatedData.size());
1653 
1654             QByteArray outBuffer;
1655             // reserve memory
1656             outBuffer.resize(unpackSize);
1657 
1658             KFilterBase::Result result = KFilterBase::Ok;
1659             QByteArray inflatedDataTmp;
1660             while (result != KFilterBase::End && result != KFilterBase::Error && !filter->inBufferEmpty()) {
1661                 filter->setOutBuffer(outBuffer.data(), outBuffer.size());
1662                 result = filter->uncompress();
1663                 if (result == KFilterBase::Error) {
1664                     qCDebug(KArchiveLog) << " decode error";
1665                     filter->terminate();
1666                     delete filter;
1667                     return QByteArray();
1668                 }
1669                 int uncompressedBytes = outBuffer.size() - filter->outBufferAvailable();
1670 
1671                 // append the uncompressed data to inflate buffer
1672                 inflatedDataTmp.append(outBuffer.data(), uncompressedBytes);
1673 
1674                 if (result == KFilterBase::End) {
1675                     // qCDebug(KArchiveLog) << "Finished unpacking";
1676                     break; // Finished.
1677                 }
1678             }
1679 
1680             if (result != KFilterBase::End && !filter->inBufferEmpty()) {
1681                 qCDebug(KArchiveLog) << "decode failed result" << result;
1682                 filter->terminate();
1683                 delete filter;
1684                 return QByteArray();
1685             }
1686 
1687             filter->terminate();
1688             delete filter;
1689 
1690             inflatedDatas.append(inflatedDataTmp);
1691         }
1692 
1693         QByteArray inflated;
1694         for (const QByteArray &data : std::as_const(inflatedDatas)) {
1695             inflated.append(data);
1696         }
1697 
1698         inflatedDatas.clear();
1699 
1700         if (folder->unpackCRCDefined) {
1701             if ((size_t)inflated.size() < unpackSize) {
1702                 qCDebug(KArchiveLog) << "wrong crc size data";
1703                 return QByteArray();
1704             }
1705             quint32 crc = crc32(0, (Bytef *)(inflated.data()), unpackSize);
1706             if (crc != folder->unpackCRC) {
1707                 qCDebug(KArchiveLog) << "wrong crc";
1708                 return QByteArray();
1709             }
1710         }
1711 
1712         inflatedData.append(inflated);
1713     }
1714 
1715     return inflatedData;
1716 }
1717 
1718 ///////////////// Write ////////////////////
1719 
1720 void K7Zip::K7ZipPrivate::createItemsFromEntities(const KArchiveDirectory *dir, const QString &path, QByteArray &data)
1721 {
1722     const QStringList l = dir->entries();
1723     QStringList::ConstIterator it = l.begin();
1724     for (; it != l.end(); ++it) {
1725         const KArchiveEntry *entry = dir->entry((*it));
1726 
1727         FileInfo *fileInfo = new FileInfo;
1728         fileInfo->attribDefined = true;
1729 
1730         fileInfo->path = path + entry->name();
1731         mTimesDefined.append(true);
1732         mTimes.append(rtlSecondsSince1970ToSpecTime(entry->date().toSecsSinceEpoch()));
1733 
1734         if (entry->isFile()) {
1735             const K7ZipFileEntry *fileEntry = static_cast<const K7ZipFileEntry *>(entry);
1736 
1737             fileInfo->attributes = FILE_ATTRIBUTE_ARCHIVE;
1738             fileInfo->attributes |= FILE_ATTRIBUTE_UNIX_EXTENSION + ((entry->permissions() & 0xFFFF) << 16);
1739             fileInfo->size = fileEntry->size();
1740             QString symLink = fileEntry->symLinkTarget();
1741             if (fileInfo->size > 0) {
1742                 fileInfo->hasStream = true;
1743                 data.append(outData.mid(fileEntry->position(), fileEntry->size()));
1744                 unpackSizes.append(fileInfo->size);
1745             } else if (!symLink.isEmpty()) {
1746                 fileInfo->hasStream = true;
1747                 data.append(symLink.toUtf8());
1748                 unpackSizes.append(symLink.size());
1749             }
1750             fileInfos.append(fileInfo);
1751         } else if (entry->isDirectory()) {
1752             fileInfo->attributes = FILE_ATTRIBUTE_DIRECTORY;
1753             fileInfo->attributes |= FILE_ATTRIBUTE_UNIX_EXTENSION + ((entry->permissions() & 0xFFFF) << 16);
1754             fileInfo->isDir = true;
1755             fileInfos.append(fileInfo);
1756             createItemsFromEntities((KArchiveDirectory *)entry, path + (*it) + QLatin1Char('/'), data);
1757         }
1758     }
1759 }
1760 
1761 void K7Zip::K7ZipPrivate::writeByte(unsigned char b)
1762 {
1763     header.append(b);
1764     countSize++;
1765 }
1766 
1767 void K7Zip::K7ZipPrivate::writeNumber(quint64 value)
1768 {
1769     int firstByte = 0;
1770     short mask = 0x80;
1771     int i;
1772     for (i = 0; i < 8; i++) {
1773         if (value < ((quint64(1) << (7 * (i + 1))))) {
1774             firstByte |= (int)(value >> (8 * i));
1775             break;
1776         }
1777         firstByte |= mask;
1778         mask >>= 1;
1779     }
1780     writeByte(firstByte);
1781     for (; i > 0; i--) {
1782         writeByte((int)value);
1783         value >>= 8;
1784     }
1785 }
1786 
1787 void K7Zip::K7ZipPrivate::writeBoolVector(const QVector<bool> &boolVector)
1788 {
1789     int b = 0;
1790     short mask = 0x80;
1791     for (int i = 0; i < boolVector.size(); i++) {
1792         if (boolVector[i]) {
1793             b |= mask;
1794         }
1795         mask >>= 1;
1796         if (mask == 0) {
1797             writeByte(b);
1798             mask = 0x80;
1799             b = 0;
1800         }
1801     }
1802     if (mask != 0x80) {
1803         writeByte(b);
1804     }
1805 }
1806 
1807 void K7Zip::K7ZipPrivate::writeUInt32(quint32 value)
1808 {
1809     for (int i = 0; i < 4; i++) {
1810         writeByte((unsigned char)value);
1811         value >>= 8;
1812     }
1813 }
1814 
1815 void K7Zip::K7ZipPrivate::writeUInt64(quint64 value)
1816 {
1817     for (int i = 0; i < 8; i++) {
1818         writeByte((unsigned char)value);
1819         value >>= 8;
1820     }
1821 }
1822 
1823 void K7Zip::K7ZipPrivate::writeAlignedBoolHeader(const QVector<bool> &v, int numDefined, int type, unsigned itemSize)
1824 {
1825     const unsigned bvSize = (numDefined == v.size()) ? 0 : ((unsigned)v.size() + 7) / 8;
1826     const quint64 dataSize = (quint64)numDefined * itemSize + bvSize + 2;
1827     // SkipAlign(3 + (unsigned)bvSize + (unsigned)GetBigNumberSize(dataSize), itemSize);
1828 
1829     writeByte(type);
1830     writeNumber(dataSize);
1831     if (numDefined == v.size()) {
1832         writeByte(1);
1833     } else {
1834         writeByte(0);
1835         writeBoolVector(v);
1836     }
1837     writeByte(0);
1838 }
1839 
1840 void K7Zip::K7ZipPrivate::writeUInt64DefVector(const QVector<quint64> &v, const QVector<bool> &defined, int type)
1841 {
1842     int numDefined = 0;
1843 
1844     for (int i = 0; i < defined.size(); i++) {
1845         if (defined[i]) {
1846             numDefined++;
1847         }
1848     }
1849 
1850     if (numDefined == 0) {
1851         return;
1852     }
1853 
1854     writeAlignedBoolHeader(defined, numDefined, type, 8);
1855 
1856     for (int i = 0; i < defined.size(); i++) {
1857         if (defined[i]) {
1858             writeUInt64(v[i]);
1859         }
1860     }
1861 }
1862 
1863 void K7Zip::K7ZipPrivate::writeHashDigests(const QVector<bool> &digestsDefined, const QVector<quint32> &digests)
1864 {
1865     int numDefined = 0;
1866     int i;
1867     for (i = 0; i < digestsDefined.size(); i++) {
1868         if (digestsDefined[i]) {
1869             numDefined++;
1870         }
1871     }
1872 
1873     if (numDefined == 0) {
1874         return;
1875     }
1876 
1877     writeByte(kCRC);
1878     if (numDefined == digestsDefined.size()) {
1879         writeByte(1);
1880     } else {
1881         writeByte(0);
1882         writeBoolVector(digestsDefined);
1883     }
1884 
1885     for (i = 0; i < digests.size(); i++) {
1886         if (digestsDefined[i]) {
1887             writeUInt32(digests[i]);
1888         }
1889     }
1890 }
1891 
1892 void K7Zip::K7ZipPrivate::writePackInfo(quint64 dataOffset, QVector<quint64> &packedSizes, QVector<bool> &packedCRCsDefined, QVector<quint32> &packedCRCs)
1893 {
1894     if (packedSizes.isEmpty()) {
1895         return;
1896     }
1897     writeByte(kPackInfo);
1898     writeNumber(dataOffset);
1899     writeNumber(packedSizes.size());
1900     writeByte(kSize);
1901 
1902     for (int i = 0; i < packedSizes.size(); i++) {
1903         writeNumber(packedSizes[i]);
1904     }
1905 
1906     writeHashDigests(packedCRCsDefined, packedCRCs);
1907 
1908     writeByte(kEnd);
1909 }
1910 
1911 void K7Zip::K7ZipPrivate::writeFolder(const Folder *folder)
1912 {
1913     writeNumber(folder->folderInfos.size());
1914     for (int i = 0; i < folder->folderInfos.size(); i++) {
1915         const Folder::FolderInfo *info = folder->folderInfos.at(i);
1916         {
1917             size_t propsSize = info->properties.size();
1918 
1919             quint64 id = info->methodID;
1920             size_t idSize;
1921             for (idSize = 1; idSize < sizeof(id); idSize++) {
1922                 if ((id >> (8 * idSize)) == 0) {
1923                     break;
1924                 }
1925             }
1926 
1927             int longID[15];
1928             for (int t = idSize - 1; t >= 0; t--, id >>= 8) {
1929                 longID[t] = (int)(id & 0xFF);
1930             }
1931 
1932             int b;
1933             b = (int)(idSize & 0xF);
1934             bool isComplex = !info->isSimpleCoder();
1935             b |= (isComplex ? 0x10 : 0);
1936             b |= ((propsSize != 0) ? 0x20 : 0);
1937 
1938             writeByte(b);
1939             for (size_t j = 0; j < idSize; ++j) {
1940                 writeByte(longID[j]);
1941             }
1942 
1943             if (isComplex) {
1944                 writeNumber(info->numInStreams);
1945                 writeNumber(info->numOutStreams);
1946             }
1947 
1948             if (propsSize == 0) {
1949                 continue;
1950             }
1951 
1952             writeNumber(propsSize);
1953             for (size_t j = 0; j < propsSize; ++j) {
1954                 writeByte(info->properties[j]);
1955             }
1956         }
1957     }
1958 
1959     for (int i = 0; i < folder->inIndexes.size(); i++) {
1960         writeNumber(folder->inIndexes[i]);
1961         writeNumber(folder->outIndexes[i]);
1962     }
1963 
1964     if (folder->packedStreams.size() > 1) {
1965         for (int i = 0; i < folder->packedStreams.size(); i++) {
1966             writeNumber(folder->packedStreams[i]);
1967         }
1968     }
1969 }
1970 
1971 void K7Zip::K7ZipPrivate::writeUnpackInfo(const QVector<Folder *> &folderItems)
1972 {
1973     if (folderItems.isEmpty()) {
1974         return;
1975     }
1976 
1977     writeByte(kUnpackInfo);
1978 
1979     writeByte(kFolder);
1980     writeNumber(folderItems.size());
1981     {
1982         writeByte(0);
1983         for (int i = 0; i < folderItems.size(); i++) {
1984             writeFolder(folderItems[i]);
1985         }
1986     }
1987 
1988     writeByte(kCodersUnpackSize);
1989     int i;
1990     for (i = 0; i < folderItems.size(); i++) {
1991         const Folder *folder = folderItems[i];
1992         for (int j = 0; j < folder->unpackSizes.size(); j++) {
1993             writeNumber(folder->unpackSizes.at(j));
1994         }
1995     }
1996 
1997     QVector<bool> unpackCRCsDefined;
1998     QVector<quint32> unpackCRCs;
1999     unpackCRCsDefined.reserve(folderItems.size());
2000     unpackCRCs.reserve(folderItems.size());
2001     for (i = 0; i < folderItems.size(); i++) {
2002         const Folder *folder = folderItems[i];
2003         unpackCRCsDefined.append(folder->unpackCRCDefined);
2004         unpackCRCs.append(folder->unpackCRC);
2005     }
2006     writeHashDigests(unpackCRCsDefined, unpackCRCs);
2007 
2008     writeByte(kEnd);
2009 }
2010 
2011 void K7Zip::K7ZipPrivate::writeSubStreamsInfo(const QVector<quint64> &unpackSizes, const QVector<bool> &digestsDefined, const QVector<quint32> &digests)
2012 {
2013     writeByte(kSubStreamsInfo);
2014 
2015     for (int i = 0; i < numUnpackStreamsInFolders.size(); i++) {
2016         if (numUnpackStreamsInFolders.at(i) != 1) {
2017             writeByte(kNumUnpackStream);
2018             for (int j = 0; j < numUnpackStreamsInFolders.size(); j++) {
2019                 writeNumber(numUnpackStreamsInFolders.at(j));
2020             }
2021             break;
2022         }
2023     }
2024 
2025     bool needFlag = true;
2026     int index = 0;
2027     for (int i = 0; i < numUnpackStreamsInFolders.size(); i++) {
2028         for (quint32 j = 0; j < numUnpackStreamsInFolders.at(i); j++) {
2029             if (j + 1 != numUnpackStreamsInFolders.at(i)) {
2030                 if (needFlag) {
2031                     writeByte(kSize);
2032                 }
2033                 needFlag = false;
2034                 writeNumber(unpackSizes[index]);
2035             }
2036             index++;
2037         }
2038     }
2039 
2040     QVector<bool> digestsDefined2;
2041     QVector<quint32> digests2;
2042 
2043     int digestIndex = 0;
2044     for (int i = 0; i < folders.size(); i++) {
2045         int numSubStreams = (int)numUnpackStreamsInFolders.at(i);
2046         if (numSubStreams == 1 && folders.at(i)->unpackCRCDefined) {
2047             digestIndex++;
2048         } else {
2049             for (int j = 0; j < numSubStreams; j++, digestIndex++) {
2050                 digestsDefined2.append(digestsDefined[digestIndex]);
2051                 digests2.append(digests[digestIndex]);
2052             }
2053         }
2054     }
2055     writeHashDigests(digestsDefined2, digests2);
2056     writeByte(kEnd);
2057 }
2058 
2059 QByteArray K7Zip::K7ZipPrivate::encodeStream(QVector<quint64> &packSizes, QVector<Folder *> &folds)
2060 {
2061     Folder *folder = new Folder;
2062     folder->unpackCRCDefined = true;
2063     folder->unpackCRC = crc32(0, (Bytef *)(header.data()), header.size());
2064     folder->unpackSizes.append(header.size());
2065 
2066     Folder::FolderInfo *info = new Folder::FolderInfo();
2067     info->numInStreams = 1;
2068     info->numOutStreams = 1;
2069     info->methodID = k_LZMA2;
2070 
2071     quint32 dictSize = header.size();
2072     const quint32 kMinReduceSize = (1 << 16);
2073     if (dictSize < kMinReduceSize) {
2074         dictSize = kMinReduceSize;
2075     }
2076 
2077     int dict;
2078     for (dict = 0; dict < 40; dict++) {
2079         if (dictSize <= lzma2_dic_size_from_prop(dict)) {
2080             break;
2081         }
2082     }
2083 
2084     info->properties.append(dict);
2085     folder->folderInfos.append(info);
2086 
2087     folds.append(folder);
2088 
2089     // compress data
2090     QByteArray encodedData;
2091     if (!header.isEmpty()) {
2092         QByteArray enc;
2093         QBuffer inBuffer(&enc);
2094 
2095         KCompressionDevice flt(&inBuffer, false, KCompressionDevice::Xz);
2096         flt.open(QIODevice::WriteOnly);
2097 
2098         KFilterBase *filter = flt.filterBase();
2099 
2100         static_cast<KXzFilter *>(filter)->init(QIODevice::WriteOnly, KXzFilter::LZMA2, info->properties);
2101 
2102         const int ret = flt.write(header);
2103         if (ret != header.size()) {
2104             qCDebug(KArchiveLog) << "write error write " << ret << "expected" << header.size();
2105             return encodedData;
2106         }
2107 
2108         flt.close();
2109         encodedData = inBuffer.data();
2110     }
2111 
2112     packSizes.append(encodedData.size());
2113     return encodedData;
2114 }
2115 
2116 void K7Zip::K7ZipPrivate::writeHeader(quint64 &headerOffset)
2117 {
2118     quint64 packedSize = 0;
2119     for (int i = 0; i < packSizes.size(); ++i) {
2120         packedSize += packSizes[i];
2121     }
2122 
2123     headerOffset = packedSize;
2124 
2125     writeByte(kHeader);
2126 
2127     // Archive Properties
2128 
2129     if (!folders.isEmpty()) {
2130         writeByte(kMainStreamsInfo);
2131         writePackInfo(0, packSizes, packCRCsDefined, packCRCs);
2132 
2133         writeUnpackInfo(folders);
2134 
2135         QVector<quint64> unpackFileSizes;
2136         QVector<bool> digestsDefined;
2137         QVector<quint32> digests;
2138         for (int i = 0; i < fileInfos.size(); i++) {
2139             const FileInfo *file = fileInfos.at(i);
2140             if (!file->hasStream) {
2141                 continue;
2142             }
2143             unpackFileSizes.append(file->size);
2144             digestsDefined.append(file->crcDefined);
2145             digests.append(file->crc);
2146         }
2147 
2148         writeSubStreamsInfo(unpackSizes, digestsDefined, digests);
2149         writeByte(kEnd);
2150     }
2151 
2152     if (fileInfos.isEmpty()) {
2153         writeByte(kEnd);
2154         return;
2155     }
2156 
2157     writeByte(kFilesInfo);
2158     writeNumber(fileInfos.size());
2159 
2160     {
2161         /* ---------- Empty Streams ---------- */
2162         QVector<bool> emptyStreamVector;
2163         int numEmptyStreams = 0;
2164         for (int i = 0; i < fileInfos.size(); i++) {
2165             if (fileInfos.at(i)->hasStream) {
2166                 emptyStreamVector.append(false);
2167             } else {
2168                 emptyStreamVector.append(true);
2169                 numEmptyStreams++;
2170             }
2171         }
2172 
2173         if (numEmptyStreams > 0) {
2174             writeByte(kEmptyStream);
2175             writeNumber(((unsigned)emptyStreamVector.size() + 7) / 8);
2176             writeBoolVector(emptyStreamVector);
2177 
2178             QVector<bool> emptyFileVector;
2179             QVector<bool> antiVector;
2180             int numEmptyFiles = 0;
2181             int numAntiItems = 0;
2182             for (int i = 0; i < fileInfos.size(); i++) {
2183                 const FileInfo *file = fileInfos.at(i);
2184                 if (!file->hasStream) {
2185                     emptyFileVector.append(!file->isDir);
2186                     if (!file->isDir) {
2187                         numEmptyFiles++;
2188                         bool isAnti = (i < this->isAnti.size() && this->isAnti[i]);
2189                         antiVector.append(isAnti);
2190                         if (isAnti) {
2191                             numAntiItems++;
2192                         }
2193                     }
2194                 }
2195             }
2196 
2197             if (numEmptyFiles > 0) {
2198                 writeByte(kEmptyFile);
2199                 writeNumber(((unsigned)emptyFileVector.size() + 7) / 8);
2200                 writeBoolVector(emptyFileVector);
2201             }
2202 
2203             if (numAntiItems > 0) {
2204                 writeByte(kAnti);
2205                 writeNumber(((unsigned)antiVector.size() + 7) / 8);
2206                 writeBoolVector(antiVector);
2207             }
2208         }
2209     }
2210 
2211     {
2212         /* ---------- Names ---------- */
2213 
2214         int numDefined = 0;
2215         size_t namesDataSize = 0;
2216         for (int i = 0; i < fileInfos.size(); i++) {
2217             const QString &name = fileInfos.at(i)->path;
2218             if (!name.isEmpty()) {
2219                 numDefined++;
2220                 namesDataSize += (name.length() + 1) * 2;
2221             }
2222         }
2223 
2224         if (numDefined > 0) {
2225             namesDataSize++;
2226             // SkipAlign(2 + GetBigNumberSize(namesDataSize), 2);
2227 
2228             writeByte(kName);
2229             writeNumber(namesDataSize);
2230             writeByte(0);
2231             for (int i = 0; i < fileInfos.size(); i++) {
2232                 const QString &name = fileInfos.at(i)->path;
2233                 for (int t = 0; t < name.length(); t++) {
2234                     wchar_t c = name[t].toLatin1();
2235                     writeByte((unsigned char)c);
2236                     writeByte((unsigned char)(c >> 8));
2237                 }
2238                 // End of string
2239                 writeByte(0);
2240                 writeByte(0);
2241             }
2242         }
2243     }
2244 
2245     writeUInt64DefVector(mTimes, mTimesDefined, kMTime);
2246 
2247     writeUInt64DefVector(startPositions, startPositionsDefined, kStartPos);
2248 
2249     {
2250         /* ---------- Write Attrib ---------- */
2251         QVector<bool> boolVector;
2252         int numDefined = 0;
2253         boolVector.reserve(fileInfos.size());
2254         for (int i = 0; i < fileInfos.size(); i++) {
2255             bool defined = fileInfos.at(i)->attribDefined;
2256             boolVector.append(defined);
2257             if (defined) {
2258                 numDefined++;
2259             }
2260         }
2261 
2262         if (numDefined > 0) {
2263             writeAlignedBoolHeader(boolVector, numDefined, kAttributes, 4);
2264             for (int i = 0; i < fileInfos.size(); i++) {
2265                 const FileInfo *file = fileInfos.at(i);
2266                 if (file->attribDefined) {
2267                     writeUInt32(file->attributes);
2268                 }
2269             }
2270         }
2271     }
2272 
2273     writeByte(kEnd); // for files
2274     writeByte(kEnd); // for headers*/
2275 }
2276 
2277 static void setUInt32(unsigned char *p, quint32 d)
2278 {
2279     for (int i = 0; i < 4; i++, d >>= 8) {
2280         p[i] = (unsigned)d;
2281     }
2282 }
2283 
2284 static void setUInt64(unsigned char *p, quint64 d)
2285 {
2286     for (int i = 0; i < 8; i++, d >>= 8) {
2287         p[i] = (unsigned char)d;
2288     }
2289 }
2290 
2291 void K7Zip::K7ZipPrivate::writeStartHeader(const quint64 nextHeaderSize, const quint32 nextHeaderCRC, const quint64 nextHeaderOffset)
2292 {
2293     unsigned char buf[24];
2294     setUInt64(buf + 4, nextHeaderOffset);
2295     setUInt64(buf + 12, nextHeaderSize);
2296     setUInt32(buf + 20, nextHeaderCRC);
2297     setUInt32(buf, crc32(0, (Bytef *)(buf + 4), 20));
2298     q->device()->write((char *)buf, 24);
2299 }
2300 
2301 void K7Zip::K7ZipPrivate::writeSignature()
2302 {
2303     unsigned char buf[8];
2304     memcpy(buf, k7zip_signature, 6);
2305     buf[6] = 0 /*kMajorVersion*/;
2306     buf[7] = 3;
2307     q->device()->write((char *)buf, 8);
2308 }
2309 
2310 bool K7Zip::openArchive(QIODevice::OpenMode mode)
2311 {
2312     if (!(mode & QIODevice::ReadOnly)) {
2313         return true;
2314     }
2315 
2316     QIODevice *dev = device();
2317 
2318     if (!dev) {
2319         setErrorString(tr("Could not get underlying device"));
2320         return false;
2321     }
2322 
2323     char header[32];
2324     // check signature
2325     qint64 n = dev->read(header, 32);
2326     if (n != 32) {
2327         setErrorString(tr("Read header failed"));
2328         return false;
2329     }
2330 
2331     for (int i = 0; i < 6; ++i) {
2332         if ((unsigned char)header[i] != k7zip_signature[i]) {
2333             setErrorString(tr("Check signature failed"));
2334             return false;
2335         }
2336     }
2337 
2338     // get Archive Version
2339     int major = header[6];
2340     int minor = header[7];
2341 
2342     /*if (major > 0 || minor > 2) {
2343         qCDebug(KArchiveLog) << "wrong archive version";
2344         return false;
2345     }*/
2346 
2347     // get Start Header CRC
2348     quint32 startHeaderCRC = GetUi32(header, 8);
2349     quint64 nextHeaderOffset = GetUi64(header, 12);
2350     quint64 nextHeaderSize = GetUi64(header, 20);
2351     quint32 nextHeaderCRC = GetUi32(header, 28);
2352 
2353     quint32 crc = crc32(0, (Bytef *)(header + 0xC), 20);
2354 
2355     if (crc != startHeaderCRC) {
2356         setErrorString(tr("Bad CRC"));
2357         return false;
2358     }
2359 
2360     if (nextHeaderSize == 0) {
2361         return true;
2362     }
2363 
2364     if (nextHeaderSize > (quint64)0xFFFFFFFF) {
2365         setErrorString(tr("Next header size is too big"));
2366         return false;
2367     }
2368 
2369     if ((qint64)nextHeaderOffset < 0) {
2370         setErrorString(tr("Next header size is less than zero"));
2371         return false;
2372     }
2373 
2374     dev->seek(nextHeaderOffset + 32);
2375 
2376     QByteArray inBuffer;
2377     inBuffer.resize(nextHeaderSize);
2378 
2379     n = dev->read(inBuffer.data(), inBuffer.size());
2380     if (n != (qint64)nextHeaderSize) {
2381         setErrorString(tr("Failed read next header size; should read %1, read %2").arg(nextHeaderSize).arg(n));
2382         return false;
2383     }
2384     d->buffer = inBuffer.data();
2385     d->end = nextHeaderSize;
2386 
2387     d->headerSize = 32 + nextHeaderSize;
2388     // int physSize = 32 + nextHeaderSize + nextHeaderOffset;
2389 
2390     crc = crc32(0, (Bytef *)(d->buffer), (quint32)nextHeaderSize);
2391 
2392     if (crc != nextHeaderCRC) {
2393         setErrorString(tr("Bad next header CRC"));
2394         return false;
2395     }
2396 
2397     int type = d->readByte();
2398     QByteArray decodedData;
2399     if (type != kHeader) {
2400         if (type != kEncodedHeader) {
2401             setErrorString(tr("Error in header"));
2402             return false;
2403         }
2404 
2405         decodedData = d->readAndDecodePackedStreams();
2406 
2407         int external = d->readByte();
2408         if (external != 0) {
2409             int dataIndex = (int)d->readNumber();
2410             if (dataIndex < 0) {
2411                 // qCDebug(KArchiveLog) << "dataIndex error";
2412             }
2413             d->buffer = decodedData.constData();
2414             d->pos = 0;
2415             d->end = decodedData.size();
2416         }
2417 
2418         type = d->readByte();
2419         if (type != kHeader) {
2420             setErrorString(tr("Wrong header type"));
2421             return false;
2422         }
2423     }
2424     // read header
2425 
2426     type = d->readByte();
2427 
2428     if (type == kArchiveProperties) {
2429         // TODO : implement this part
2430         setErrorString(tr("Not implemented"));
2431         return false;
2432     }
2433 
2434     if (type == kAdditionalStreamsInfo) {
2435         // TODO : implement this part
2436         setErrorString(tr("Not implemented"));
2437         return false;
2438     }
2439 
2440     if (type == kMainStreamsInfo) {
2441         if (!d->readMainStreamsInfo()) {
2442             setErrorString(tr("Error while reading main streams information"));
2443             return false;
2444         }
2445         type = d->readByte();
2446     } else {
2447         for (int i = 0; i < d->folders.size(); ++i) {
2448             Folder *folder = d->folders.at(i);
2449             d->unpackSizes.append(folder->getUnpackSize());
2450             d->digestsDefined.append(folder->unpackCRCDefined);
2451             d->digests.append(folder->unpackCRC);
2452         }
2453     }
2454 
2455     if (type == kEnd) {
2456         return true;
2457     }
2458 
2459     if (type != kFilesInfo) {
2460         setErrorString(tr("Error while reading header"));
2461         return false;
2462     }
2463 
2464     // read files info
2465     int numFiles = d->readNumber();
2466     for (int i = 0; i < numFiles; ++i) {
2467         d->fileInfos.append(new FileInfo);
2468     }
2469 
2470     QVector<bool> emptyStreamVector;
2471     QVector<bool> emptyFileVector;
2472     QVector<bool> antiFileVector;
2473     int numEmptyStreams = 0;
2474 
2475     for (;;) {
2476         quint64 type = d->readByte();
2477         if (type == kEnd) {
2478             break;
2479         }
2480 
2481         quint64 size = d->readNumber();
2482 
2483         size_t ppp = d->pos;
2484 
2485         bool addPropIdToList = true;
2486         bool isKnownType = true;
2487 
2488         if (type > ((quint32)1 << 30)) {
2489             isKnownType = false;
2490         } else {
2491             switch (type) {
2492             case kEmptyStream: {
2493                 d->readBoolVector(numFiles, emptyStreamVector);
2494                 for (int i = 0; i < emptyStreamVector.size(); ++i) {
2495                     if (emptyStreamVector[i]) {
2496                         numEmptyStreams++;
2497                     }
2498                 }
2499 
2500                 break;
2501             }
2502             case kEmptyFile:
2503                 d->readBoolVector(numEmptyStreams, emptyFileVector);
2504                 break;
2505             case kAnti:
2506                 d->readBoolVector(numEmptyStreams, antiFileVector);
2507                 break;
2508             case kCTime:
2509                 if (!d->readUInt64DefVector(numFiles, d->cTimes, d->cTimesDefined)) {
2510                     return false;
2511                 }
2512                 break;
2513             case kATime:
2514                 if (!d->readUInt64DefVector(numFiles, d->aTimes, d->aTimesDefined)) {
2515                     return false;
2516                 }
2517                 break;
2518             case kMTime:
2519                 if (!d->readUInt64DefVector(numFiles, d->mTimes, d->mTimesDefined)) {
2520                     setErrorString(tr("Error reading modification time"));
2521                     return false;
2522                 }
2523                 break;
2524             case kName: {
2525                 int external = d->readByte();
2526                 if (external != 0) {
2527                     int dataIndex = d->readNumber();
2528                     if (dataIndex < 0 /*|| dataIndex >= dataVector->Size()*/) {
2529                         qCDebug(KArchiveLog) << "wrong data index";
2530                     }
2531 
2532                     // TODO : go to the new index
2533                 }
2534 
2535                 QString name;
2536                 for (int i = 0; i < numFiles; i++) {
2537                     name = d->readString();
2538                     d->fileInfos.at(i)->path = name;
2539                 }
2540                 break;
2541             }
2542             case kAttributes: {
2543                 QVector<bool> attributesAreDefined;
2544                 d->readBoolVector2(numFiles, attributesAreDefined);
2545                 int external = d->readByte();
2546                 if (external != 0) {
2547                     int dataIndex = d->readNumber();
2548                     if (dataIndex < 0) {
2549                         qCDebug(KArchiveLog) << "wrong data index";
2550                     }
2551 
2552                     // TODO : go to the new index
2553                 }
2554 
2555                 for (int i = 0; i < numFiles; i++) {
2556                     FileInfo *fileInfo = d->fileInfos.at(i);
2557                     fileInfo->attribDefined = attributesAreDefined[i];
2558                     if (fileInfo->attribDefined) {
2559                         fileInfo->attributes = d->readUInt32();
2560                     }
2561                 }
2562                 break;
2563             }
2564             case kStartPos:
2565                 if (!d->readUInt64DefVector(numFiles, d->startPositions, d->startPositionsDefined)) {
2566                     setErrorString(tr("Error reading MTime"));
2567                     return false;
2568                 }
2569                 break;
2570             case kDummy: {
2571                 for (quint64 i = 0; i < size; i++) {
2572                     if (d->readByte() != 0) {
2573                         setErrorString(tr("Invalid"));
2574                         return false;
2575                     }
2576                 }
2577                 addPropIdToList = false;
2578                 break;
2579             }
2580             default:
2581                 addPropIdToList = isKnownType = false;
2582             }
2583         }
2584 
2585         if (isKnownType) {
2586             if (addPropIdToList) {
2587                 d->fileInfoPopIDs.append(type);
2588             }
2589         } else {
2590             d->skipData(d->readNumber());
2591         }
2592 
2593         bool checkRecordsSize = (major > 0 || minor > 2);
2594         if (checkRecordsSize && d->pos - ppp != size) {
2595             setErrorString(tr("Read size failed "
2596                               "(checkRecordsSize: %1, d->pos - ppp: %2, size: %3)")
2597                                .arg(checkRecordsSize)
2598                                .arg(d->pos - ppp)
2599                                .arg(size));
2600             return false;
2601         }
2602     }
2603 
2604     int emptyFileIndex = 0;
2605     int sizeIndex = 0;
2606 
2607     int numAntiItems = 0;
2608 
2609     if (emptyStreamVector.isEmpty()) {
2610         emptyStreamVector.fill(false, numFiles);
2611     }
2612 
2613     if (antiFileVector.isEmpty()) {
2614         antiFileVector.fill(false, numEmptyStreams);
2615     }
2616     if (emptyFileVector.isEmpty()) {
2617         emptyFileVector.fill(false, numEmptyStreams);
2618     }
2619 
2620     for (int i = 0; i < numEmptyStreams; i++) {
2621         if (antiFileVector[i]) {
2622             numAntiItems++;
2623         }
2624     }
2625 
2626     d->outData = d->readAndDecodePackedStreams(false);
2627 
2628     int oldPos = 0;
2629     for (int i = 0; i < numFiles; i++) {
2630         FileInfo *fileInfo = d->fileInfos.at(i);
2631         bool isAnti;
2632         fileInfo->hasStream = !emptyStreamVector[i];
2633         if (fileInfo->hasStream) {
2634             fileInfo->isDir = false;
2635             isAnti = false;
2636             fileInfo->size = d->unpackSizes[sizeIndex];
2637             fileInfo->crc = d->digests[sizeIndex];
2638             fileInfo->crcDefined = d->digestsDefined[sizeIndex];
2639             sizeIndex++;
2640         } else {
2641             fileInfo->isDir = !emptyFileVector[emptyFileIndex];
2642             isAnti = antiFileVector[emptyFileIndex];
2643             emptyFileIndex++;
2644             fileInfo->size = 0;
2645             fileInfo->crcDefined = false;
2646         }
2647         if (numAntiItems != 0) {
2648             d->isAnti.append(isAnti);
2649         }
2650 
2651         int access;
2652         bool symlink = false;
2653         if (fileInfo->attributes & FILE_ATTRIBUTE_UNIX_EXTENSION) {
2654             access = fileInfo->attributes >> 16;
2655             if ((access & QT_STAT_MASK) == QT_STAT_LNK) {
2656                 symlink = true;
2657             }
2658         } else {
2659             if (fileInfo->isDir) {
2660                 access = S_IFDIR | 0755;
2661             } else {
2662                 access = 0100644;
2663             }
2664         }
2665 
2666         qint64 pos = 0;
2667         if (!fileInfo->isDir) {
2668             pos = oldPos;
2669             oldPos += fileInfo->size;
2670         }
2671 
2672         KArchiveEntry *e;
2673         QString entryName;
2674         int index = fileInfo->path.lastIndexOf(QLatin1Char('/'));
2675         if (index == -1) {
2676             entryName = fileInfo->path;
2677         } else {
2678             entryName = fileInfo->path.mid(index + 1);
2679         }
2680         Q_ASSERT(!entryName.isEmpty());
2681 
2682         QDateTime mTime;
2683         if (d->mTimesDefined[i]) {
2684             mTime = KArchivePrivate::time_tToDateTime(toTimeT(d->mTimes[i]));
2685         } else {
2686             mTime = KArchivePrivate::time_tToDateTime(time(nullptr));
2687         }
2688 
2689         if (fileInfo->isDir) {
2690             QString path = QDir::cleanPath(fileInfo->path);
2691             const KArchiveEntry *ent = rootDir()->entry(path);
2692             if (ent && ent->isDirectory()) {
2693                 e = nullptr;
2694             } else {
2695                 e = new KArchiveDirectory(this, entryName, access, mTime, rootDir()->user(), rootDir()->group(), QString() /*symlink*/);
2696             }
2697         } else {
2698             if (!symlink) {
2699                 e = new K7ZipFileEntry(this,
2700                                        entryName,
2701                                        access,
2702                                        mTime,
2703                                        rootDir()->user(),
2704                                        rootDir()->group(),
2705                                        QString() /*symlink*/,
2706                                        pos,
2707                                        fileInfo->size,
2708                                        d->outData);
2709             } else {
2710                 QString target = QFile::decodeName(d->outData.mid(pos, fileInfo->size));
2711                 e = new K7ZipFileEntry(this, entryName, access, mTime, rootDir()->user(), rootDir()->group(), target, 0, 0, nullptr);
2712             }
2713         }
2714 
2715         if (e) {
2716             if (index == -1) {
2717                 rootDir()->addEntry(e);
2718             } else {
2719                 QString path = QDir::cleanPath(fileInfo->path.left(index));
2720                 KArchiveDirectory *d = findOrCreate(path);
2721                 d->addEntry(e);
2722             }
2723         }
2724     }
2725 
2726     return true;
2727 }
2728 
2729 bool K7Zip::closeArchive()
2730 {
2731     // Unnecessary check (already checked by KArchive::close())
2732     if (!isOpen()) {
2733         // qCWarning(KArchiveLog) << "You must open the file before close it\n";
2734         return false;
2735     }
2736 
2737     if ((mode() == QIODevice::ReadOnly)) {
2738         return true;
2739     }
2740 
2741     d->clear();
2742 
2743     Folder *folder = new Folder();
2744 
2745     folder->unpackSizes.clear();
2746     folder->unpackSizes.append(d->outData.size());
2747 
2748     Folder::FolderInfo *info = new Folder::FolderInfo();
2749 
2750     info->numInStreams = 1;
2751     info->numOutStreams = 1;
2752     info->methodID = k_LZMA2;
2753 
2754     quint32 dictSize = d->outData.size();
2755 
2756     const quint32 kMinReduceSize = (1 << 16);
2757     if (dictSize < kMinReduceSize) {
2758         dictSize = kMinReduceSize;
2759     }
2760 
2761     // k_LZMA2 method
2762     int dict;
2763     for (dict = 0; dict < 40; dict++) {
2764         if (dictSize <= lzma2_dic_size_from_prop(dict)) {
2765             break;
2766         }
2767     }
2768     info->properties.append(dict);
2769 
2770     folder->folderInfos.append(info);
2771     d->folders.append(folder);
2772 
2773     const KArchiveDirectory *dir = directory();
2774     QByteArray data;
2775     d->createItemsFromEntities(dir, QString(), data);
2776     d->outData = data;
2777 
2778     folder->unpackCRCDefined = true;
2779     folder->unpackCRC = crc32(0, (Bytef *)(d->outData.data()), d->outData.size());
2780 
2781     // compress data
2782     QByteArray encodedData;
2783     if (!d->outData.isEmpty()) {
2784         QByteArray enc;
2785         QBuffer inBuffer(&enc);
2786 
2787         KCompressionDevice flt(&inBuffer, false, KCompressionDevice::Xz);
2788         flt.open(QIODevice::WriteOnly);
2789 
2790         KFilterBase *filter = flt.filterBase();
2791 
2792         static_cast<KXzFilter *>(filter)->init(QIODevice::WriteOnly, KXzFilter::LZMA2, info->properties);
2793 
2794         const int ret = flt.write(d->outData);
2795         if (ret != d->outData.size()) {
2796             setErrorString(tr("Write error"));
2797             return false;
2798         }
2799 
2800         flt.close();
2801         encodedData = inBuffer.data();
2802     }
2803 
2804     d->packSizes.append(encodedData.size());
2805 
2806     int numUnpackStream = 0;
2807     for (int i = 0; i < d->fileInfos.size(); ++i) {
2808         if (d->fileInfos.at(i)->hasStream) {
2809             numUnpackStream++;
2810         }
2811     }
2812     d->numUnpackStreamsInFolders.append(numUnpackStream);
2813 
2814     quint64 headerOffset;
2815     d->writeHeader(headerOffset);
2816 
2817     // Encode Header
2818     QByteArray encodedStream;
2819     {
2820         QVector<quint64> packSizes;
2821         QVector<Folder *> folders;
2822         encodedStream = d->encodeStream(packSizes, folders);
2823 
2824         if (folders.isEmpty()) {
2825             // FIXME Not sure why this is an error. Come up with a better message
2826             setErrorString(tr("Failed while encoding header"));
2827             return false;
2828         }
2829 
2830         d->header.clear();
2831 
2832         d->writeByte(kEncodedHeader);
2833         QVector<bool> emptyDefined;
2834         QVector<quint32> emptyCrcs;
2835         d->writePackInfo(headerOffset, packSizes, emptyDefined, emptyCrcs);
2836         d->writeUnpackInfo(folders);
2837         d->writeByte(kEnd);
2838         for (int i = 0; i < packSizes.size(); i++) {
2839             headerOffset += packSizes.at(i);
2840         }
2841         qDeleteAll(folders);
2842     }
2843     // end encode header
2844 
2845     quint64 nextHeaderSize = d->header.size();
2846     quint32 nextHeaderCRC = crc32(0, (Bytef *)(d->header.data()), d->header.size());
2847     quint64 nextHeaderOffset = headerOffset;
2848 
2849     device()->seek(0);
2850     d->writeSignature();
2851     d->writeStartHeader(nextHeaderSize, nextHeaderCRC, nextHeaderOffset);
2852     device()->write(encodedData.data(), encodedData.size());
2853     device()->write(encodedStream.data(), encodedStream.size());
2854     device()->write(d->header.data(), d->header.size());
2855 
2856     return true;
2857 }
2858 
2859 bool K7Zip::doFinishWriting(qint64 size)
2860 {
2861     d->m_currentFile->setSize(size);
2862     d->m_currentFile = nullptr;
2863 
2864     return true;
2865 }
2866 
2867 bool K7Zip::writeData(const char *data, qint64 size)
2868 {
2869     if (!d->m_currentFile) {
2870         setErrorString(tr("No file currently selected"));
2871         return false;
2872     }
2873 
2874     if (d->m_currentFile->position() == d->outData.size()) {
2875         d->outData.append(data, size);
2876     } else {
2877         d->outData.remove(d->m_currentFile->position(), d->m_currentFile->size());
2878         d->outData.insert(d->m_currentFile->position(), data, size);
2879     }
2880 
2881     return true;
2882 }
2883 
2884 bool K7Zip::doPrepareWriting(const QString &name,
2885                              const QString &user,
2886                              const QString &group,
2887                              qint64 /*size*/,
2888                              mode_t perm,
2889                              const QDateTime & /*atime*/,
2890                              const QDateTime &mtime,
2891                              const QDateTime & /*ctime*/)
2892 {
2893     if (!isOpen()) {
2894         setErrorString(tr("Application error: 7-Zip file must be open before being written into"));
2895         qCWarning(KArchiveLog) << "doPrepareWriting failed: !isOpen()";
2896         return false;
2897     }
2898 
2899     if (!(mode() & QIODevice::WriteOnly)) {
2900         setErrorString(tr("Application error: attempted to write into non-writable 7-Zip file"));
2901         qCWarning(KArchiveLog) << "doPrepareWriting failed: !(mode() & QIODevice::WriteOnly)";
2902         return false;
2903     }
2904 
2905     // Find or create parent dir
2906     KArchiveDirectory *parentDir = rootDir();
2907     // QString fileName( name );
2908     // In some files we can find dir/./file => call cleanPath
2909     QString fileName(QDir::cleanPath(name));
2910     int i = name.lastIndexOf(QLatin1Char('/'));
2911     if (i != -1) {
2912         QString dir = name.left(i);
2913         fileName = name.mid(i + 1);
2914         parentDir = findOrCreate(dir);
2915     }
2916 
2917     // test if the entry already exist
2918     const KArchiveEntry *entry = parentDir->entry(fileName);
2919     if (!entry) {
2920         K7ZipFileEntry *e =
2921             new K7ZipFileEntry(this, fileName, perm, mtime, user, group, QString() /*symlink*/, d->outData.size(), 0 /*unknown yet*/, d->outData);
2922         if (!parentDir->addEntryV2(e)) {
2923             return false;
2924         }
2925         d->m_entryList << e;
2926         d->m_currentFile = e;
2927     } else {
2928         // TODO : find and replace in m_entryList
2929         // d->m_currentFile = static_cast<K7ZipFileEntry*>(entry);
2930     }
2931 
2932     return true;
2933 }
2934 
2935 bool K7Zip::doWriteDir(const QString &name,
2936                        const QString &user,
2937                        const QString &group,
2938                        mode_t perm,
2939                        const QDateTime & /*atime*/,
2940                        const QDateTime &mtime,
2941                        const QDateTime & /*ctime*/)
2942 {
2943     if (!isOpen()) {
2944         setErrorString(tr("Application error: 7-Zip file must be open before being written into"));
2945         qCWarning(KArchiveLog) << "doWriteDir failed: !isOpen()";
2946         return false;
2947     }
2948 
2949     if (!(mode() & QIODevice::WriteOnly)) {
2950         // qCWarning(KArchiveLog) << "You must open the tar file for writing\n";
2951         return false;
2952     }
2953 
2954     // In some tar files we can find dir/./ => call cleanPath
2955     QString dirName(QDir::cleanPath(name));
2956 
2957     // Remove trailing '/'
2958     if (dirName.endsWith(QLatin1Char('/'))) {
2959         dirName.remove(dirName.size() - 1, 1);
2960     }
2961 
2962     KArchiveDirectory *parentDir = rootDir();
2963     int i = dirName.lastIndexOf(QLatin1Char('/'));
2964     if (i != -1) {
2965         QString dir = name.left(i);
2966         dirName = name.mid(i + 1);
2967         parentDir = findOrCreate(dir);
2968     }
2969 
2970     KArchiveDirectory *e = new KArchiveDirectory(this, dirName, perm, mtime, user, group, QString() /*symlink*/);
2971     parentDir->addEntry(e);
2972 
2973     return true;
2974 }
2975 
2976 bool K7Zip::doWriteSymLink(const QString &name,
2977                            const QString &target,
2978                            const QString &user,
2979                            const QString &group,
2980                            mode_t perm,
2981                            const QDateTime & /*atime*/,
2982                            const QDateTime &mtime,
2983                            const QDateTime & /*ctime*/)
2984 {
2985     if (!isOpen()) {
2986         setErrorString(tr("Application error: 7-Zip file must be open before being written into"));
2987         qCWarning(KArchiveLog) << "doWriteSymLink failed: !isOpen()";
2988         return false;
2989     }
2990 
2991     if (!(mode() & QIODevice::WriteOnly)) {
2992         setErrorString(tr("Application error: attempted to write into non-writable 7-Zip file"));
2993         qCWarning(KArchiveLog) << "doWriteSymLink failed: !(mode() & QIODevice::WriteOnly)";
2994         return false;
2995     }
2996 
2997     // Find or create parent dir
2998     KArchiveDirectory *parentDir = rootDir();
2999     // In some files we can find dir/./file => call cleanPath
3000     QString fileName(QDir::cleanPath(name));
3001     int i = name.lastIndexOf(QLatin1Char('/'));
3002     if (i != -1) {
3003         QString dir = name.left(i);
3004         fileName = name.mid(i + 1);
3005         parentDir = findOrCreate(dir);
3006     }
3007     QByteArray encodedTarget = QFile::encodeName(target);
3008 
3009     K7ZipFileEntry *e = new K7ZipFileEntry(this, fileName, perm, mtime, user, group, target, 0, 0, nullptr);
3010     d->outData.append(encodedTarget);
3011 
3012     if (!parentDir->addEntryV2(e)) {
3013         return false;
3014     }
3015 
3016     d->m_entryList << e;
3017 
3018     return true;
3019 }
3020 
3021 void K7Zip::virtual_hook(int id, void *data)
3022 {
3023     KArchive::virtual_hook(id, data);
3024 }