File indexing completed on 2024-04-21 03:52:30

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