// 7zOut.cpp #include "StdAfx.h" #include "../../../../C/7zCrc.h" #include "../../../Common/AutoPtr.h" #include "../../Common/StreamObjects.h" #include "7zOut.h" namespace NArchive { namespace N7z { HRESULT COutArchive::WriteSignature() { Byte buf[8]; memcpy(buf, kSignature, kSignatureSize); buf[kSignatureSize] = kMajorVersion; buf[kSignatureSize + 1] = 4; return WriteDirect(buf, 8); } #ifdef _7Z_VOL HRESULT COutArchive::WriteFinishSignature() { RINOK(WriteDirect(kFinishSignature, kSignatureSize)); CArchiveVersion av; av.Major = kMajorVersion; av.Minor = 2; RINOK(WriteDirectByte(av.Major)); return WriteDirectByte(av.Minor); } #endif static void SetUInt32(Byte *p, UInt32 d) { for (int i = 0; i < 4; i++, d >>= 8) p[i] = (Byte)d; } static void SetUInt64(Byte *p, UInt64 d) { for (int i = 0; i < 8; i++, d >>= 8) p[i] = (Byte)d; } HRESULT COutArchive::WriteStartHeader(const CStartHeader &h) { Byte buf[24]; SetUInt64(buf + 4, h.NextHeaderOffset); SetUInt64(buf + 12, h.NextHeaderSize); SetUInt32(buf + 20, h.NextHeaderCRC); SetUInt32(buf, CrcCalc(buf + 4, 20)); return WriteDirect(buf, 24); } #ifdef _7Z_VOL HRESULT COutArchive::WriteFinishHeader(const CFinishHeader &h) { CCRC crc; crc.UpdateUInt64(h.NextHeaderOffset); crc.UpdateUInt64(h.NextHeaderSize); crc.UpdateUInt32(h.NextHeaderCRC); crc.UpdateUInt64(h.ArchiveStartOffset); crc.UpdateUInt64(h.AdditionalStartBlockSize); RINOK(WriteDirectUInt32(crc.GetDigest())); RINOK(WriteDirectUInt64(h.NextHeaderOffset)); RINOK(WriteDirectUInt64(h.NextHeaderSize)); RINOK(WriteDirectUInt32(h.NextHeaderCRC)); RINOK(WriteDirectUInt64(h.ArchiveStartOffset)); return WriteDirectUInt64(h.AdditionalStartBlockSize); } #endif HRESULT COutArchive::Create(ISequentialOutStream *stream, bool endMarker) { Close(); #ifdef _7Z_VOL // endMarker = false; _endMarker = endMarker; #endif SeqStream = stream; if (!endMarker) { SeqStream.QueryInterface(IID_IOutStream, &Stream); if (!Stream) { return E_NOTIMPL; // endMarker = true; } } #ifdef _7Z_VOL if (endMarker) { /* CStartHeader sh; sh.NextHeaderOffset = (UInt32)(Int32)-1; sh.NextHeaderSize = (UInt32)(Int32)-1; sh.NextHeaderCRC = 0; WriteStartHeader(sh); */ } else #endif { if (!Stream) return E_FAIL; RINOK(WriteSignature()); RINOK(Stream->Seek(0, STREAM_SEEK_CUR, &_prefixHeaderPos)); } return S_OK; } void COutArchive::Close() { SeqStream.Release(); Stream.Release(); } HRESULT COutArchive::SkipPrefixArchiveHeader() { #ifdef _7Z_VOL if (_endMarker) return S_OK; #endif Byte buf[24]; memset(buf, 0, 24); return WriteDirect(buf, 24); } UInt64 COutArchive::GetPos() const { if (_countMode) return _countSize; if (_writeToStream) return _outByte.GetProcessedSize(); return _outByte2.GetPos(); } void COutArchive::WriteBytes(const void *data, size_t size) { if (_countMode) _countSize += size; else if (_writeToStream) { _outByte.WriteBytes(data, size); _crc = CrcUpdate(_crc, data, size); } else _outByte2.WriteBytes(data, size); } void COutArchive::WriteByte(Byte b) { if (_countMode) _countSize++; else if (_writeToStream) { _outByte.WriteByte(b); _crc = CRC_UPDATE_BYTE(_crc, b); } else _outByte2.WriteByte(b); } void COutArchive::WriteUInt32(UInt32 value) { for (int i = 0; i < 4; i++) { WriteByte((Byte)value); value >>= 8; } } void COutArchive::WriteUInt64(UInt64 value) { for (int i = 0; i < 8; i++) { WriteByte((Byte)value); value >>= 8; } } void COutArchive::WriteNumber(UInt64 value) { Byte firstByte = 0; Byte mask = 0x80; int i; for (i = 0; i < 8; i++) { if (value < ((UInt64(1) << ( 7 * (i + 1))))) { firstByte |= Byte(value >> (8 * i)); break; } firstByte |= mask; mask >>= 1; } WriteByte(firstByte); for (; i > 0; i--) { WriteByte((Byte)value); value >>= 8; } } static UInt32 GetBigNumberSize(UInt64 value) { int i; for (i = 1; i < 9; i++) if (value < (((UInt64)1 << (i * 7)))) break; return i; } #ifdef _7Z_VOL UInt32 COutArchive::GetVolHeadersSize(UInt64 dataSize, int nameLength, bool props) { UInt32 result = GetBigNumberSize(dataSize) * 2 + 41; if (nameLength != 0) { nameLength = (nameLength + 1) * 2; result += nameLength + GetBigNumberSize(nameLength) + 2; } if (props) { result += 20; } if (result >= 128) result++; result += kSignatureSize + 2 + kFinishHeaderSize; return result; } UInt64 COutArchive::GetVolPureSize(UInt64 volSize, int nameLength, bool props) { UInt32 headersSizeBase = COutArchive::GetVolHeadersSize(1, nameLength, props); int testSize; if (volSize > headersSizeBase) testSize = volSize - headersSizeBase; else testSize = 1; UInt32 headersSize = COutArchive::GetVolHeadersSize(testSize, nameLength, props); UInt64 pureSize = 1; if (volSize > headersSize) pureSize = volSize - headersSize; return pureSize; } #endif void COutArchive::WriteFolder(const CFolder &folder) { WriteNumber(folder.Coders.Size()); unsigned i; for (i = 0; i < folder.Coders.Size(); i++) { const CCoderInfo &coder = folder.Coders[i]; { UInt64 id = coder.MethodID; unsigned idSize; for (idSize = 1; idSize < sizeof(id); idSize++) if ((id >> (8 * idSize)) == 0) break; idSize &= 0xF; Byte temp[16]; for (unsigned t = idSize; t != 0; t--, id >>= 8) temp[t] = (Byte)(id & 0xFF); Byte b = (Byte)(idSize); bool isComplex = !coder.IsSimpleCoder(); b |= (isComplex ? 0x10 : 0); size_t propsSize = coder.Props.Size(); b |= ((propsSize != 0) ? 0x20 : 0); temp[0] = b; WriteBytes(temp, idSize + 1); if (isComplex) { WriteNumber(coder.NumStreams); WriteNumber(1); // NumOutStreams; } if (propsSize == 0) continue; WriteNumber(propsSize); WriteBytes(coder.Props, propsSize); } } for (i = 0; i < folder.Bonds.Size(); i++) { const CBond &bond = folder.Bonds[i]; WriteNumber(bond.PackIndex); WriteNumber(bond.UnpackIndex); } if (folder.PackStreams.Size() > 1) for (i = 0; i < folder.PackStreams.Size(); i++) WriteNumber(folder.PackStreams[i]); } void COutArchive::WriteBoolVector(const CBoolVector &boolVector) { Byte b = 0; Byte mask = 0x80; FOR_VECTOR (i, boolVector) { if (boolVector[i]) b |= mask; mask >>= 1; if (mask == 0) { WriteByte(b); mask = 0x80; b = 0; } } if (mask != 0x80) WriteByte(b); } static inline unsigned Bv_GetSizeInBytes(const CBoolVector &v) { return ((unsigned)v.Size() + 7) / 8; } void COutArchive::WritePropBoolVector(Byte id, const CBoolVector &boolVector) { WriteByte(id); WriteNumber(Bv_GetSizeInBytes(boolVector)); WriteBoolVector(boolVector); } unsigned BoolVector_CountSum(const CBoolVector &v); void COutArchive::WriteHashDigests(const CUInt32DefVector &digests) { const unsigned numDefined = BoolVector_CountSum(digests.Defs); if (numDefined == 0) return; WriteByte(NID::kCRC); if (numDefined == digests.Defs.Size()) WriteByte(1); else { WriteByte(0); WriteBoolVector(digests.Defs); } for (unsigned i = 0; i < digests.Defs.Size(); i++) if (digests.Defs[i]) WriteUInt32(digests.Vals[i]); } void COutArchive::WritePackInfo( UInt64 dataOffset, const CRecordVector &packSizes, const CUInt32DefVector &packCRCs) { if (packSizes.IsEmpty()) return; WriteByte(NID::kPackInfo); WriteNumber(dataOffset); WriteNumber(packSizes.Size()); WriteByte(NID::kSize); FOR_VECTOR (i, packSizes) WriteNumber(packSizes[i]); WriteHashDigests(packCRCs); WriteByte(NID::kEnd); } void COutArchive::WriteUnpackInfo(const CObjectVector &folders, const COutFolders &outFolders) { if (folders.IsEmpty()) return; WriteByte(NID::kUnpackInfo); WriteByte(NID::kFolder); WriteNumber(folders.Size()); { WriteByte(0); FOR_VECTOR (i, folders) WriteFolder(folders[i]); } WriteByte(NID::kCodersUnpackSize); FOR_VECTOR (i, outFolders.CoderUnpackSizes) WriteNumber(outFolders.CoderUnpackSizes[i]); WriteHashDigests(outFolders.FolderUnpackCRCs); WriteByte(NID::kEnd); } void COutArchive::WriteSubStreamsInfo(const CObjectVector &folders, const COutFolders &outFolders, const CRecordVector &unpackSizes, const CUInt32DefVector &digests) { const CRecordVector &numUnpackStreamsInFolders = outFolders.NumUnpackStreamsVector; WriteByte(NID::kSubStreamsInfo); unsigned i; for (i = 0; i < numUnpackStreamsInFolders.Size(); i++) if (numUnpackStreamsInFolders[i] != 1) { WriteByte(NID::kNumUnpackStream); for (i = 0; i < numUnpackStreamsInFolders.Size(); i++) WriteNumber(numUnpackStreamsInFolders[i]); break; } for (i = 0; i < numUnpackStreamsInFolders.Size(); i++) if (numUnpackStreamsInFolders[i] > 1) { WriteByte(NID::kSize); CNum index = 0; for (i = 0; i < numUnpackStreamsInFolders.Size(); i++) { CNum num = numUnpackStreamsInFolders[i]; for (CNum j = 0; j < num; j++) { if (j + 1 != num) WriteNumber(unpackSizes[index]); index++; } } break; } CUInt32DefVector digests2; unsigned digestIndex = 0; for (i = 0; i < folders.Size(); i++) { unsigned numSubStreams = (unsigned)numUnpackStreamsInFolders[i]; if (numSubStreams == 1 && outFolders.FolderUnpackCRCs.ValidAndDefined(i)) digestIndex++; else for (unsigned j = 0; j < numSubStreams; j++, digestIndex++) { digests2.Defs.Add(digests.Defs[digestIndex]); digests2.Vals.Add(digests.Vals[digestIndex]); } } WriteHashDigests(digests2); WriteByte(NID::kEnd); } // 7-Zip 4.50 - 4.58 contain BUG, so they do not support .7z archives with Unknown field. void COutArchive::SkipToAligned(unsigned pos, unsigned alignShifts) { if (!_useAlign) return; const unsigned alignSize = (unsigned)1 << alignShifts; pos += (unsigned)GetPos(); pos &= (alignSize - 1); if (pos == 0) return; unsigned skip = alignSize - pos; if (skip < 2) skip += alignSize; skip -= 2; WriteByte(NID::kDummy); WriteByte((Byte)skip); for (unsigned i = 0; i < skip; i++) WriteByte(0); } void COutArchive::WriteAlignedBools(const CBoolVector &v, unsigned numDefined, Byte type, unsigned itemSizeShifts) { const unsigned bvSize = (numDefined == v.Size()) ? 0 : Bv_GetSizeInBytes(v); const UInt64 dataSize = ((UInt64)numDefined << itemSizeShifts) + bvSize + 2; SkipToAligned(3 + (unsigned)bvSize + (unsigned)GetBigNumberSize(dataSize), itemSizeShifts); WriteByte(type); WriteNumber(dataSize); if (numDefined == v.Size()) WriteByte(1); else { WriteByte(0); WriteBoolVector(v); } WriteByte(0); // 0 means no switching to external stream } void COutArchive::WriteUInt64DefVector(const CUInt64DefVector &v, Byte type) { const unsigned numDefined = BoolVector_CountSum(v.Defs); if (numDefined == 0) return; WriteAlignedBools(v.Defs, numDefined, type, 3); for (unsigned i = 0; i < v.Defs.Size(); i++) if (v.Defs[i]) WriteUInt64(v.Vals[i]); } HRESULT COutArchive::EncodeStream( DECL_EXTERNAL_CODECS_LOC_VARS CEncoder &encoder, const CByteBuffer &data, CRecordVector &packSizes, CObjectVector &folders, COutFolders &outFolders) { CBufInStream *streamSpec = new CBufInStream; CMyComPtr stream = streamSpec; streamSpec->Init(data, data.Size()); outFolders.FolderUnpackCRCs.Defs.Add(true); outFolders.FolderUnpackCRCs.Vals.Add(CrcCalc(data, data.Size())); // outFolders.NumUnpackStreamsVector.Add(1); UInt64 dataSize64 = data.Size(); UInt64 unpackSize = data.Size(); RINOK(encoder.Encode( EXTERNAL_CODECS_LOC_VARS stream, // NULL, &dataSize64, folders.AddNew(), outFolders.CoderUnpackSizes, unpackSize, SeqStream, packSizes, NULL)) return S_OK; } void COutArchive::WriteHeader( const CArchiveDatabaseOut &db, // const CHeaderOptions &headerOptions, UInt64 &headerOffset) { /* bool thereIsSecure = (db.SecureBuf.Size() != 0); */ _useAlign = true; { UInt64 packSize = 0; FOR_VECTOR (i, db.PackSizes) packSize += db.PackSizes[i]; headerOffset = packSize; } WriteByte(NID::kHeader); // Archive Properties if (db.Folders.Size() > 0) { WriteByte(NID::kMainStreamsInfo); WritePackInfo(0, db.PackSizes, db.PackCRCs); WriteUnpackInfo(db.Folders, (const COutFolders &)db); CRecordVector unpackSizes; CUInt32DefVector digests; FOR_VECTOR (i, db.Files) { const CFileItem &file = db.Files[i]; if (!file.HasStream) continue; unpackSizes.Add(file.Size); digests.Defs.Add(file.CrcDefined); digests.Vals.Add(file.Crc); } WriteSubStreamsInfo(db.Folders, (const COutFolders &)db, unpackSizes, digests); WriteByte(NID::kEnd); } if (db.Files.IsEmpty()) { WriteByte(NID::kEnd); return; } WriteByte(NID::kFilesInfo); WriteNumber(db.Files.Size()); { /* ---------- Empty Streams ---------- */ CBoolVector emptyStreamVector; emptyStreamVector.ClearAndSetSize(db.Files.Size()); unsigned numEmptyStreams = 0; { FOR_VECTOR (i, db.Files) if (db.Files[i].HasStream) emptyStreamVector[i] = false; else { emptyStreamVector[i] = true; numEmptyStreams++; } } if (numEmptyStreams != 0) { WritePropBoolVector(NID::kEmptyStream, emptyStreamVector); CBoolVector emptyFileVector, antiVector; emptyFileVector.ClearAndSetSize(numEmptyStreams); antiVector.ClearAndSetSize(numEmptyStreams); bool thereAreEmptyFiles = false, thereAreAntiItems = false; unsigned cur = 0; FOR_VECTOR (i, db.Files) { const CFileItem &file = db.Files[i]; if (file.HasStream) continue; emptyFileVector[cur] = !file.IsDir; if (!file.IsDir) thereAreEmptyFiles = true; bool isAnti = db.IsItemAnti(i); antiVector[cur] = isAnti; if (isAnti) thereAreAntiItems = true; cur++; } if (thereAreEmptyFiles) WritePropBoolVector(NID::kEmptyFile, emptyFileVector); if (thereAreAntiItems) WritePropBoolVector(NID::kAnti, antiVector); } } { /* ---------- Names ---------- */ unsigned numDefined = 0; size_t namesDataSize = 0; FOR_VECTOR (i, db.Files) { const UString &name = db.Names[i]; if (!name.IsEmpty()) numDefined++; namesDataSize += (name.Len() + 1) * 2; } if (numDefined > 0) { namesDataSize++; SkipToAligned(2 + GetBigNumberSize(namesDataSize), 4); WriteByte(NID::kName); WriteNumber(namesDataSize); WriteByte(0); FOR_VECTOR (i, db.Files) { const UString &name = db.Names[i]; for (unsigned t = 0; t <= name.Len(); t++) { wchar_t c = name[t]; WriteByte((Byte)c); WriteByte((Byte)(c >> 8)); } } } } /* if (headerOptions.WriteCTime) */ WriteUInt64DefVector(db.CTime, NID::kCTime); /* if (headerOptions.WriteATime) */ WriteUInt64DefVector(db.ATime, NID::kATime); /* if (headerOptions.WriteMTime) */ WriteUInt64DefVector(db.MTime, NID::kMTime); WriteUInt64DefVector(db.StartPos, NID::kStartPos); { /* ---------- Write Attrib ---------- */ const unsigned numDefined = BoolVector_CountSum(db.Attrib.Defs); if (numDefined != 0) { WriteAlignedBools(db.Attrib.Defs, numDefined, NID::kWinAttrib, 2); FOR_VECTOR (i, db.Attrib.Defs) { if (db.Attrib.Defs[i]) WriteUInt32(db.Attrib.Vals[i]); } } } /* { // ---------- Write IsAux ---------- if (BoolVector_CountSum(db.IsAux) != 0) WritePropBoolVector(NID::kIsAux, db.IsAux); } { // ---------- Write Parent ---------- CBoolVector boolVector; boolVector.Reserve(db.Files.Size()); unsigned numIsDir = 0; unsigned numParentLinks = 0; for (i = 0; i < db.Files.Size(); i++) { const CFileItem &file = db.Files[i]; bool defined = !file.IsAltStream; boolVector.Add(defined); if (defined) numIsDir++; if (file.Parent >= 0) numParentLinks++; } if (numParentLinks > 0) { // WriteAlignedBools(boolVector, numDefined, NID::kParent, 2); const unsigned bvSize = (numIsDir == boolVector.Size()) ? 0 : Bv_GetSizeInBytes(boolVector); const UInt64 dataSize = (UInt64)db.Files.Size() * 4 + bvSize + 1; SkipToAligned(2 + (unsigned)bvSize + (unsigned)GetBigNumberSize(dataSize), 2); WriteByte(NID::kParent); WriteNumber(dataSize); if (numIsDir == boolVector.Size()) WriteByte(1); else { WriteByte(0); WriteBoolVector(boolVector); } for (i = 0; i < db.Files.Size(); i++) { const CFileItem &file = db.Files[i]; // if (file.Parent >= 0) WriteUInt32(file.Parent); } } } if (thereIsSecure) { UInt64 secureDataSize = 1 + 4 + db.SecureBuf.Size() + db.SecureSizes.Size() * 4; // secureDataSize += db.SecureIDs.Size() * 4; for (i = 0; i < db.SecureIDs.Size(); i++) secureDataSize += GetBigNumberSize(db.SecureIDs[i]); SkipToAligned(2 + GetBigNumberSize(secureDataSize), 2); WriteByte(NID::kNtSecure); WriteNumber(secureDataSize); WriteByte(0); WriteUInt32(db.SecureSizes.Size()); for (i = 0; i < db.SecureSizes.Size(); i++) WriteUInt32(db.SecureSizes[i]); WriteBytes(db.SecureBuf, db.SecureBuf.Size()); for (i = 0; i < db.SecureIDs.Size(); i++) { WriteNumber(db.SecureIDs[i]); // WriteUInt32(db.SecureIDs[i]); } } */ WriteByte(NID::kEnd); // for files WriteByte(NID::kEnd); // for headers } HRESULT COutArchive::WriteDatabase( DECL_EXTERNAL_CODECS_LOC_VARS const CArchiveDatabaseOut &db, const CCompressionMethodMode *options, const CHeaderOptions &headerOptions) { if (!db.CheckNumFiles()) return E_FAIL; UInt64 headerOffset; UInt32 headerCRC; UInt64 headerSize; if (db.IsEmpty()) { headerSize = 0; headerOffset = 0; headerCRC = CrcCalc(0, 0); } else { bool encodeHeaders = false; if (options != 0) if (options->IsEmpty()) options = 0; if (options != 0) if (options->PasswordIsDefined || headerOptions.CompressMainHeader) encodeHeaders = true; _outByte.SetStream(SeqStream); _outByte.Init(); _crc = CRC_INIT_VAL; _countMode = encodeHeaders; _writeToStream = true; _countSize = 0; WriteHeader(db, /* headerOptions, */ headerOffset); if (encodeHeaders) { CByteBuffer buf(_countSize); _outByte2.Init((Byte *)buf, _countSize); _countMode = false; _writeToStream = false; WriteHeader(db, /* headerOptions, */ headerOffset); if (_countSize != _outByte2.GetPos()) return E_FAIL; CCompressionMethodMode encryptOptions; encryptOptions.PasswordIsDefined = options->PasswordIsDefined; encryptOptions.Password = options->Password; CEncoder encoder(headerOptions.CompressMainHeader ? *options : encryptOptions); CRecordVector packSizes; CObjectVector folders; COutFolders outFolders; RINOK(EncodeStream( EXTERNAL_CODECS_LOC_VARS encoder, buf, packSizes, folders, outFolders)); _writeToStream = true; if (folders.Size() == 0) throw 1; WriteID(NID::kEncodedHeader); WritePackInfo(headerOffset, packSizes, CUInt32DefVector()); WriteUnpackInfo(folders, outFolders); WriteByte(NID::kEnd); FOR_VECTOR (i, packSizes) headerOffset += packSizes[i]; } RINOK(_outByte.Flush()); headerCRC = CRC_GET_DIGEST(_crc); headerSize = _outByte.GetProcessedSize(); } #ifdef _7Z_VOL if (_endMarker) { CFinishHeader h; h.NextHeaderSize = headerSize; h.NextHeaderCRC = headerCRC; h.NextHeaderOffset = UInt64(0) - (headerSize + 4 + kFinishHeaderSize); h.ArchiveStartOffset = h.NextHeaderOffset - headerOffset; h.AdditionalStartBlockSize = 0; RINOK(WriteFinishHeader(h)); return WriteFinishSignature(); } else #endif { CStartHeader h; h.NextHeaderSize = headerSize; h.NextHeaderCRC = headerCRC; h.NextHeaderOffset = headerOffset; RINOK(Stream->Seek(_prefixHeaderPos, STREAM_SEEK_SET, NULL)); return WriteStartHeader(h); } } void CUInt32DefVector::SetItem(unsigned index, bool defined, UInt32 value) { while (index >= Defs.Size()) Defs.Add(false); Defs[index] = defined; if (!defined) return; while (index >= Vals.Size()) Vals.Add(0); Vals[index] = value; } void CUInt64DefVector::SetItem(unsigned index, bool defined, UInt64 value) { while (index >= Defs.Size()) Defs.Add(false); Defs[index] = defined; if (!defined) return; while (index >= Vals.Size()) Vals.Add(0); Vals[index] = value; } void CArchiveDatabaseOut::AddFile(const CFileItem &file, const CFileItem2 &file2, const UString &name) { unsigned index = Files.Size(); CTime.SetItem(index, file2.CTimeDefined, file2.CTime); ATime.SetItem(index, file2.ATimeDefined, file2.ATime); MTime.SetItem(index, file2.MTimeDefined, file2.MTime); StartPos.SetItem(index, file2.StartPosDefined, file2.StartPos); Attrib.SetItem(index, file2.AttribDefined, file2.Attrib); SetItem_Anti(index, file2.IsAnti); // SetItem_Aux(index, file2.IsAux); Names.Add(name); Files.Add(file); } }}