// 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]; { size_t propsSize = coder.Props.Size(); UInt64 id = coder.MethodID; int idSize; for (idSize = 1; idSize < sizeof(id); idSize++) if ((id >> (8 * idSize)) == 0) break; Byte longID[15]; for (int t = idSize - 1; t >= 0 ; t--, id >>= 8) longID[t] = (Byte)(id & 0xFF); Byte b; b = (Byte)(idSize & 0xF); bool isComplex = !coder.IsSimpleCoder(); b |= (isComplex ? 0x10 : 0); b |= ((propsSize != 0) ? 0x20 : 0 ); WriteByte(b); WriteBytes(longID, idSize); if (isComplex) { WriteNumber(coder.NumInStreams); WriteNumber(coder.NumOutStreams); } if (propsSize == 0) continue; WriteNumber(propsSize); WriteBytes(coder.Props, propsSize); } } for (i = 0; i < folder.BindPairs.Size(); i++) { const CBindPair &bindPair = folder.BindPairs[i]; WriteNumber(bindPair.InIndex); WriteNumber(bindPair.OutIndex); } 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); } void COutArchive::WriteHashDigests(const CUInt32DefVector &digests) { unsigned numDefined = 0; unsigned i; for (i = 0; i < digests.Defs.Size(); i++) if (digests.Defs[i]) numDefined++; if (numDefined == 0) return; WriteByte(NID::kCRC); if (numDefined == digests.Defs.Size()) WriteByte(1); else { WriteByte(0); WriteBoolVector(digests.Defs); } for (i = 0; i < digests.Defs.Size(); i++) if (digests.Defs[i]) WriteUInt32(digests.Vals[i]); } void COutArchive::WritePackInfo( UInt64 dataOffset, const CRecordVector<UInt64> &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<CFolder> &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<CFolder> &folders, const COutFolders &outFolders, const CRecordVector<UInt64> &unpackSizes, const CUInt32DefVector &digests) { const CRecordVector<CNum> &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::SkipAlign(unsigned pos, unsigned alignSize) { if (!_useAlign) return; 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::WriteAlignedBoolHeader(const CBoolVector &v, unsigned numDefined, Byte type, unsigned itemSize) { const unsigned bvSize = (numDefined == v.Size()) ? 0 : Bv_GetSizeInBytes(v); const UInt64 dataSize = (UInt64)numDefined * itemSize + bvSize + 2; SkipAlign(3 + (unsigned)bvSize + (unsigned)GetBigNumberSize(dataSize), itemSize); WriteByte(type); WriteNumber(dataSize); if (numDefined == v.Size()) WriteByte(1); else { WriteByte(0); WriteBoolVector(v); } WriteByte(0); } void COutArchive::WriteUInt64DefVector(const CUInt64DefVector &v, Byte type) { unsigned numDefined = 0; unsigned i; for (i = 0; i < v.Defs.Size(); i++) if (v.Defs[i]) numDefined++; if (numDefined == 0) return; WriteAlignedBoolHeader(v.Defs, numDefined, type, 8); for (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<UInt64> &packSizes, CObjectVector<CFolder> &folders, COutFolders &outFolders) { CBufInStream *streamSpec = new CBufInStream; CMyComPtr<ISequentialInStream> 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; 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; unsigned i; UInt64 packedSize = 0; for (i = 0; i < db.PackSizes.Size(); i++) packedSize += db.PackSizes[i]; headerOffset = packedSize; 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<UInt64> unpackSizes; CUInt32DefVector digests; for (i = 0; i < db.Files.Size(); i++) { 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 (i = 0; i < db.Files.Size(); i++) 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 (i = 0; i < db.Files.Size(); i++) { 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++; SkipAlign(2 + GetBigNumberSize(namesDataSize), 16); 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 ---------- */ CBoolVector boolVector; boolVector.ClearAndSetSize(db.Files.Size()); unsigned numDefined = 0; for (i = 0; i < db.Files.Size(); i++) { bool defined = db.Files[i].AttribDefined; boolVector[i] = defined; if (defined) numDefined++; } if (numDefined != 0) { WriteAlignedBoolHeader(boolVector, numDefined, NID::kWinAttrib, 4); for (i = 0; i < db.Files.Size(); i++) { const CFileItem &file = db.Files[i]; if (file.AttribDefined) WriteUInt32(file.Attrib); } } } /* { // ---------- Write IsAux ---------- unsigned numAux = 0; const CBoolVector &isAux = db.IsAux; for (i = 0; i < isAux.Size(); i++) if (isAux[i]) numAux++; if (numAux > 0) { const unsigned bvSize = Bv_GetSizeInBytes(isAux); WriteByte(NID::kIsAux); WriteNumber(bvSize); WriteBoolVector(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) { // WriteAlignedBoolHeader(boolVector, numDefined, NID::kParent, 4); const unsigned bvSize = (numIsDir == boolVector.Size()) ? 0 : Bv_GetSizeInBytes(boolVector); const UInt64 dataSize = (UInt64)db.Files.Size() * 4 + bvSize + 1; SkipAlign(2 + (unsigned)bvSize + (unsigned)GetBigNumberSize(dataSize), 4); 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]); SkipAlign(2 + GetBigNumberSize(secureDataSize), 4); 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<UInt64> packSizes; CObjectVector<CFolder> 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 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); SetItem_Anti(index, file2.IsAnti); // SetItem_Aux(index, file2.IsAux); Names.Add(name); Files.Add(file); } }}