| // ZipOut.cpp |
| |
| #include "StdAfx.h" |
| |
| #include "../../../../C/7zCrc.h" |
| |
| #include "../../../Windows/TimeUtils.h" |
| #include "../../Common/OffsetStream.h" |
| |
| #include "ZipOut.h" |
| |
| namespace NArchive { |
| namespace NZip { |
| |
| HRESULT COutArchive::ClearRestriction() |
| { |
| if (SetRestriction) |
| return SetRestriction->SetRestriction(0, 0); |
| return S_OK; |
| } |
| |
| HRESULT COutArchive::SetRestrictionFromCurrent() |
| { |
| if (SetRestriction) |
| return SetRestriction->SetRestriction(m_Base + m_CurPos, (UInt64)(Int64)-1); |
| return S_OK; |
| } |
| |
| HRESULT COutArchive::Create(IOutStream *outStream) |
| { |
| m_CurPos = 0; |
| if (!m_OutBuffer.Create(1 << 16)) |
| return E_OUTOFMEMORY; |
| m_Stream = outStream; |
| m_OutBuffer.SetStream(outStream); |
| m_OutBuffer.Init(); |
| |
| return m_Stream->Seek(0, STREAM_SEEK_CUR, &m_Base); |
| } |
| |
| void COutArchive::SeekToCurPos() |
| { |
| HRESULT res = m_Stream->Seek((Int64)(m_Base + m_CurPos), STREAM_SEEK_SET, NULL); |
| if (res != S_OK) |
| throw CSystemException(res); |
| } |
| |
| #define DOES_NEED_ZIP64(v) (v >= (UInt32)0xFFFFFFFF) |
| // #define DOES_NEED_ZIP64(v) (v >= 0) |
| |
| |
| void COutArchive::WriteBytes(const void *data, size_t size) |
| { |
| m_OutBuffer.WriteBytes(data, size); |
| m_CurPos += size; |
| } |
| |
| void COutArchive::Write8(Byte b) |
| { |
| m_OutBuffer.WriteByte(b); |
| m_CurPos++; |
| } |
| |
| void COutArchive::Write16(UInt16 val) |
| { |
| Write8((Byte)val); |
| Write8((Byte)(val >> 8)); |
| } |
| |
| void COutArchive::Write32(UInt32 val) |
| { |
| for (int i = 0; i < 4; i++) |
| { |
| Write8((Byte)val); |
| val >>= 8; |
| } |
| } |
| |
| void COutArchive::Write64(UInt64 val) |
| { |
| for (int i = 0; i < 8; i++) |
| { |
| Write8((Byte)val); |
| val >>= 8; |
| } |
| } |
| |
| void COutArchive::WriteExtra(const CExtraBlock &extra) |
| { |
| FOR_VECTOR (i, extra.SubBlocks) |
| { |
| const CExtraSubBlock &subBlock = extra.SubBlocks[i]; |
| Write16((UInt16)subBlock.ID); |
| Write16((UInt16)subBlock.Data.Size()); |
| WriteBytes(subBlock.Data, (UInt16)subBlock.Data.Size()); |
| } |
| } |
| |
| void COutArchive::WriteCommonItemInfo(const CLocalItem &item, bool isZip64) |
| { |
| { |
| Byte ver = item.ExtractVersion.Version; |
| if (isZip64 && ver < NFileHeader::NCompressionMethod::kExtractVersion_Zip64) |
| ver = NFileHeader::NCompressionMethod::kExtractVersion_Zip64; |
| Write8(ver); |
| } |
| Write8(item.ExtractVersion.HostOS); |
| Write16(item.Flags); |
| Write16(item.Method); |
| Write32(item.Time); |
| } |
| |
| |
| #define WRITE_32_VAL_SPEC(_v_, _isZip64_) Write32((_isZip64_) ? 0xFFFFFFFF : (UInt32)(_v_)); |
| |
| |
| void COutArchive::WriteUtfName(const CItemOut &item) |
| { |
| if (item.Name_Utf.Size() == 0) |
| return; |
| Write16(NFileHeader::NExtraID::kIzUnicodeName); |
| Write16((UInt16)(5 + item.Name_Utf.Size())); |
| Write8(1); // (1 = version) of that extra field |
| Write32(CrcCalc(item.Name.Ptr(), item.Name.Len())); |
| WriteBytes(item.Name_Utf, (UInt16)item.Name_Utf.Size()); |
| } |
| |
| |
| static const unsigned k_Ntfs_ExtraSize = 4 + 2 + 2 + (3 * 8); |
| static const unsigned k_UnixTime_ExtraSize = 1 + (1 * 4); |
| |
| void COutArchive::WriteTimeExtra(const CItemOut &item, bool writeNtfs) |
| { |
| if (writeNtfs) |
| { |
| // windows explorer ignores that extra |
| Write16(NFileHeader::NExtraID::kNTFS); |
| Write16(k_Ntfs_ExtraSize); |
| Write32(0); // reserved |
| Write16(NFileHeader::NNtfsExtra::kTagTime); |
| Write16(8 * 3); |
| WriteNtfsTime(item.Ntfs_MTime); |
| WriteNtfsTime(item.Ntfs_ATime); |
| WriteNtfsTime(item.Ntfs_CTime); |
| } |
| |
| if (item.Write_UnixTime) |
| { |
| // windows explorer ignores that extra |
| // by specification : should we write to local header also? |
| Write16(NFileHeader::NExtraID::kUnixTime); |
| Write16(k_UnixTime_ExtraSize); |
| const Byte flags = (Byte)((unsigned)1 << NFileHeader::NUnixTime::kMTime); |
| Write8(flags); |
| UInt32 unixTime; |
| NWindows::NTime::FileTime_To_UnixTime(item.Ntfs_MTime, unixTime); |
| Write32(unixTime); |
| } |
| } |
| |
| |
| void COutArchive::WriteLocalHeader(CItemOut &item, bool needCheck) |
| { |
| m_LocalHeaderPos = m_CurPos; |
| item.LocalHeaderPos = m_CurPos; |
| |
| bool isZip64 = |
| DOES_NEED_ZIP64(item.PackSize) || |
| DOES_NEED_ZIP64(item.Size); |
| |
| if (needCheck && m_IsZip64) |
| isZip64 = true; |
| |
| // Why don't we write NTFS timestamps to local header? |
| // Probably we want to reduce size of archive? |
| const bool writeNtfs = false; // do not write NTFS timestamp to local header |
| // const bool writeNtfs = item.Write_NtfsTime; // write NTFS time to local header |
| const UInt32 localExtraSize = (UInt32)( |
| (isZip64 ? (4 + 8 + 8): 0) |
| + (writeNtfs ? 4 + k_Ntfs_ExtraSize : 0) |
| + (item.Write_UnixTime ? 4 + k_UnixTime_ExtraSize : 0) |
| + item.Get_UtfName_ExtraSize() |
| + item.LocalExtra.GetSize()); |
| if ((UInt16)localExtraSize != localExtraSize) |
| throw CSystemException(E_FAIL); |
| if (needCheck && m_ExtraSize != localExtraSize) |
| throw CSystemException(E_FAIL); |
| |
| m_IsZip64 = isZip64; |
| m_ExtraSize = localExtraSize; |
| |
| item.LocalExtra.IsZip64 = isZip64; |
| |
| Write32(NSignature::kLocalFileHeader); |
| |
| WriteCommonItemInfo(item, isZip64); |
| |
| Write32(item.HasDescriptor() ? 0 : item.Crc); |
| |
| UInt64 packSize = item.PackSize; |
| UInt64 size = item.Size; |
| |
| if (item.HasDescriptor()) |
| { |
| packSize = 0; |
| size = 0; |
| } |
| |
| WRITE_32_VAL_SPEC(packSize, isZip64) |
| WRITE_32_VAL_SPEC(size, isZip64) |
| |
| Write16((UInt16)item.Name.Len()); |
| |
| Write16((UInt16)localExtraSize); |
| |
| WriteBytes((const char *)item.Name, (UInt16)item.Name.Len()); |
| |
| if (isZip64) |
| { |
| Write16(NFileHeader::NExtraID::kZip64); |
| Write16(8 + 8); |
| Write64(size); |
| Write64(packSize); |
| } |
| |
| WriteTimeExtra(item, writeNtfs); |
| |
| WriteUtfName(item); |
| |
| WriteExtra(item.LocalExtra); |
| |
| const UInt32 localFileHeaderSize = (UInt32)(m_CurPos - m_LocalHeaderPos); |
| if (needCheck && m_LocalFileHeaderSize != localFileHeaderSize) |
| throw CSystemException(E_FAIL); |
| m_LocalFileHeaderSize = localFileHeaderSize; |
| |
| m_OutBuffer.FlushWithCheck(); |
| } |
| |
| |
| void COutArchive::WriteLocalHeader_Replace(CItemOut &item) |
| { |
| m_CurPos = m_LocalHeaderPos + m_LocalFileHeaderSize + item.PackSize; |
| |
| if (item.HasDescriptor()) |
| { |
| WriteDescriptor(item); |
| m_OutBuffer.FlushWithCheck(); |
| return; |
| // we don't replace local header, if we write Descriptor. |
| // so local header with Descriptor flag must be written to local header before. |
| } |
| |
| const UInt64 nextPos = m_CurPos; |
| m_CurPos = m_LocalHeaderPos; |
| SeekToCurPos(); |
| WriteLocalHeader(item, true); |
| m_CurPos = nextPos; |
| SeekToCurPos(); |
| } |
| |
| |
| void COutArchive::WriteDescriptor(const CItemOut &item) |
| { |
| Byte buf[kDataDescriptorSize64]; |
| SetUi32(buf, NSignature::kDataDescriptor) |
| SetUi32(buf + 4, item.Crc) |
| unsigned descriptorSize; |
| if (m_IsZip64) |
| { |
| SetUi64(buf + 8, item.PackSize) |
| SetUi64(buf + 16, item.Size) |
| descriptorSize = kDataDescriptorSize64; |
| } |
| else |
| { |
| SetUi32(buf + 8, (UInt32)item.PackSize) |
| SetUi32(buf + 12, (UInt32)item.Size) |
| descriptorSize = kDataDescriptorSize32; |
| } |
| WriteBytes(buf, descriptorSize); |
| } |
| |
| |
| |
| void COutArchive::WriteCentralHeader(const CItemOut &item) |
| { |
| const bool isUnPack64 = DOES_NEED_ZIP64(item.Size); |
| const bool isPack64 = DOES_NEED_ZIP64(item.PackSize); |
| const bool isPosition64 = DOES_NEED_ZIP64(item.LocalHeaderPos); |
| const bool isZip64 = isPack64 || isUnPack64 || isPosition64; |
| |
| Write32(NSignature::kCentralFileHeader); |
| Write8(item.MadeByVersion.Version); |
| Write8(item.MadeByVersion.HostOS); |
| |
| WriteCommonItemInfo(item, isZip64); |
| Write32(item.Crc); |
| |
| WRITE_32_VAL_SPEC(item.PackSize, isPack64) |
| WRITE_32_VAL_SPEC(item.Size, isUnPack64) |
| |
| Write16((UInt16)item.Name.Len()); |
| |
| const UInt16 zip64ExtraSize = (UInt16)((isUnPack64 ? 8: 0) + (isPack64 ? 8: 0) + (isPosition64 ? 8: 0)); |
| const bool writeNtfs = item.Write_NtfsTime; |
| const size_t centralExtraSize = |
| (isZip64 ? 4 + zip64ExtraSize : 0) |
| + (writeNtfs ? 4 + k_Ntfs_ExtraSize : 0) |
| + (item.Write_UnixTime ? 4 + k_UnixTime_ExtraSize : 0) |
| + item.Get_UtfName_ExtraSize() |
| + item.CentralExtra.GetSize(); |
| |
| const UInt16 centralExtraSize16 = (UInt16)centralExtraSize; |
| if (centralExtraSize16 != centralExtraSize) |
| throw CSystemException(E_FAIL); |
| |
| Write16(centralExtraSize16); |
| |
| const UInt16 commentSize = (UInt16)item.Comment.Size(); |
| |
| Write16(commentSize); |
| Write16(0); // DiskNumberStart |
| Write16(item.InternalAttrib); |
| Write32(item.ExternalAttrib); |
| WRITE_32_VAL_SPEC(item.LocalHeaderPos, isPosition64) |
| WriteBytes((const char *)item.Name, item.Name.Len()); |
| |
| if (isZip64) |
| { |
| Write16(NFileHeader::NExtraID::kZip64); |
| Write16(zip64ExtraSize); |
| if (isUnPack64) |
| Write64(item.Size); |
| if (isPack64) |
| Write64(item.PackSize); |
| if (isPosition64) |
| Write64(item.LocalHeaderPos); |
| } |
| |
| WriteTimeExtra(item, writeNtfs); |
| WriteUtfName(item); |
| |
| WriteExtra(item.CentralExtra); |
| if (commentSize != 0) |
| WriteBytes(item.Comment, commentSize); |
| } |
| |
| HRESULT COutArchive::WriteCentralDir(const CObjectVector<CItemOut> &items, const CByteBuffer *comment) |
| { |
| RINOK(ClearRestriction()) |
| |
| const UInt64 cdOffset = GetCurPos(); |
| FOR_VECTOR (i, items) |
| WriteCentralHeader(items[i]); |
| const UInt64 cd64EndOffset = GetCurPos(); |
| const UInt64 cdSize = cd64EndOffset - cdOffset; |
| const bool cdOffset64 = DOES_NEED_ZIP64(cdOffset); |
| const bool cdSize64 = DOES_NEED_ZIP64(cdSize); |
| const bool items64 = items.Size() >= 0xFFFF; |
| const bool isZip64 = (cdOffset64 || cdSize64 || items64); |
| |
| // isZip64 = true; // to test Zip64 |
| |
| if (isZip64) |
| { |
| Write32(NSignature::kEcd64); |
| Write64(kEcd64_MainSize); |
| |
| // to test extra block: |
| // const UInt32 extraSize = 1 << 26; |
| // Write64(kEcd64_MainSize + extraSize); |
| |
| Write16(45); // made by version |
| Write16(45); // extract version |
| Write32(0); // ThisDiskNumber |
| Write32(0); // StartCentralDirectoryDiskNumber |
| Write64((UInt64)items.Size()); |
| Write64((UInt64)items.Size()); |
| Write64((UInt64)cdSize); |
| Write64((UInt64)cdOffset); |
| |
| // for (UInt32 iii = 0; iii < extraSize; iii++) Write8(1); |
| |
| Write32(NSignature::kEcd64Locator); |
| Write32(0); // number of the disk with the start of the zip64 end of central directory |
| Write64(cd64EndOffset); |
| Write32(1); // total number of disks |
| } |
| |
| Write32(NSignature::kEcd); |
| Write16(0); // ThisDiskNumber |
| Write16(0); // StartCentralDirectoryDiskNumber |
| Write16((UInt16)(items64 ? 0xFFFF: items.Size())); |
| Write16((UInt16)(items64 ? 0xFFFF: items.Size())); |
| |
| WRITE_32_VAL_SPEC(cdSize, cdSize64) |
| WRITE_32_VAL_SPEC(cdOffset, cdOffset64) |
| |
| const UInt16 commentSize = (UInt16)(comment ? comment->Size() : 0); |
| Write16((UInt16)commentSize); |
| if (commentSize != 0) |
| WriteBytes((const Byte *)*comment, commentSize); |
| m_OutBuffer.FlushWithCheck(); |
| return S_OK; |
| } |
| |
| void COutArchive::CreateStreamForCompressing(CMyComPtr<IOutStream> &outStream) |
| { |
| COffsetOutStream *streamSpec = new COffsetOutStream; |
| outStream = streamSpec; |
| streamSpec->Init(m_Stream, m_Base + m_CurPos); |
| } |
| |
| void COutArchive::CreateStreamForCopying(CMyComPtr<ISequentialOutStream> &outStream) |
| { |
| outStream = m_Stream; |
| } |
| |
| }} |