Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / Archive / Zip / ZipOut.cpp
1 // ZipOut.cpp\r
2 \r
3 #include "StdAfx.h"\r
4 \r
5 #include "../../Common/OffsetStream.h"\r
6 \r
7 #include "ZipOut.h"\r
8 \r
9 namespace NArchive {\r
10 namespace NZip {\r
11 \r
12 void COutArchive::Create(IOutStream *outStream)\r
13 {\r
14   if (!m_OutBuffer.Create(1 << 16))\r
15     throw CSystemException(E_OUTOFMEMORY);\r
16   m_Stream = outStream;\r
17   m_OutBuffer.SetStream(outStream);\r
18   m_OutBuffer.Init();\r
19   m_BasePosition = 0;\r
20 }\r
21 \r
22 void COutArchive::MoveBasePosition(UInt64 distanceToMove)\r
23 {\r
24   m_BasePosition += distanceToMove; // test overflow\r
25 }\r
26 \r
27 void COutArchive::PrepareWriteCompressedDataZip64(UInt16 fileNameLength, bool isZip64, bool aesEncryption)\r
28 {\r
29   m_IsZip64 = isZip64;\r
30   m_ExtraSize = isZip64 ? (4 + 8 + 8) : 0;\r
31   if (aesEncryption)\r
32     m_ExtraSize += 4 + 7;\r
33   m_LocalFileHeaderSize = 4 + NFileHeader::kLocalBlockSize + fileNameLength + m_ExtraSize;\r
34 }\r
35 \r
36 void COutArchive::PrepareWriteCompressedData(UInt16 fileNameLength, UInt64 unPackSize, bool aesEncryption)\r
37 {\r
38   // We test it to 0xF8000000 to support case when compressed size\r
39   // can be larger than uncompressed size.\r
40   PrepareWriteCompressedDataZip64(fileNameLength, unPackSize >= 0xF8000000, aesEncryption);\r
41 }\r
42 \r
43 void COutArchive::PrepareWriteCompressedData2(UInt16 fileNameLength, UInt64 unPackSize, UInt64 packSize, bool aesEncryption)\r
44 {\r
45   bool isUnPack64 = unPackSize >= 0xFFFFFFFF;\r
46   bool isPack64 = packSize >= 0xFFFFFFFF;\r
47   bool isZip64 = isPack64 || isUnPack64;\r
48   PrepareWriteCompressedDataZip64(fileNameLength, isZip64, aesEncryption);\r
49 }\r
50 \r
51 void COutArchive::WriteBytes(const void *buffer, UInt32 size)\r
52 {\r
53   m_OutBuffer.WriteBytes(buffer, size);\r
54   m_BasePosition += size;\r
55 }\r
56 \r
57 void COutArchive::WriteByte(Byte b)\r
58 {\r
59   WriteBytes(&b, 1);\r
60 }\r
61 \r
62 void COutArchive::WriteUInt16(UInt16 value)\r
63 {\r
64   for (int i = 0; i < 2; i++)\r
65   {\r
66     WriteByte((Byte)value);\r
67     value >>= 8;\r
68   }\r
69 }\r
70 \r
71 void COutArchive::WriteUInt32(UInt32 value)\r
72 {\r
73   for (int i = 0; i < 4; i++)\r
74   {\r
75     WriteByte((Byte)value);\r
76     value >>= 8;\r
77   }\r
78 }\r
79 \r
80 void COutArchive::WriteUInt64(UInt64 value)\r
81 {\r
82   for (int i = 0; i < 8; i++)\r
83   {\r
84     WriteByte((Byte)value);\r
85     value >>= 8;\r
86   }\r
87 }\r
88 \r
89 void COutArchive::WriteExtra(const CExtraBlock &extra)\r
90 {\r
91   if (extra.SubBlocks.Size() != 0)\r
92   {\r
93     for (int i = 0; i < extra.SubBlocks.Size(); i++)\r
94     {\r
95       const CExtraSubBlock &subBlock = extra.SubBlocks[i];\r
96       WriteUInt16(subBlock.ID);\r
97       WriteUInt16((UInt16)subBlock.Data.GetCapacity());\r
98       WriteBytes(subBlock.Data, (UInt32)subBlock.Data.GetCapacity());\r
99     }\r
100   }\r
101 }\r
102 \r
103 void COutArchive::SeekTo(UInt64 offset)\r
104 {\r
105   HRESULT res = m_Stream->Seek(offset, STREAM_SEEK_SET, NULL);\r
106   if (res != S_OK)\r
107     throw CSystemException(res);\r
108 }\r
109 \r
110 void COutArchive::WriteLocalHeader(const CLocalItem &item)\r
111 {\r
112   SeekTo(m_BasePosition);\r
113   \r
114   bool isZip64 = m_IsZip64 || item.PackSize >= 0xFFFFFFFF || item.UnPackSize >= 0xFFFFFFFF;\r
115   \r
116   WriteUInt32(NSignature::kLocalFileHeader);\r
117   {\r
118     Byte ver = item.ExtractVersion.Version;\r
119     if (isZip64 && ver < NFileHeader::NCompressionMethod::kExtractVersion_Zip64)\r
120       ver = NFileHeader::NCompressionMethod::kExtractVersion_Zip64;\r
121     WriteByte(ver);\r
122   }\r
123   WriteByte(item.ExtractVersion.HostOS);\r
124   WriteUInt16(item.Flags);\r
125   WriteUInt16(item.CompressionMethod);\r
126   WriteUInt32(item.Time);\r
127   WriteUInt32(item.FileCRC);\r
128   WriteUInt32(isZip64 ? 0xFFFFFFFF: (UInt32)item.PackSize);\r
129   WriteUInt32(isZip64 ? 0xFFFFFFFF: (UInt32)item.UnPackSize);\r
130   WriteUInt16((UInt16)item.Name.Length());\r
131   {\r
132     UInt16 localExtraSize = (UInt16)((isZip64 ? (4 + 16): 0) + item.LocalExtra.GetSize());\r
133     if (localExtraSize > m_ExtraSize)\r
134       throw CSystemException(E_FAIL);\r
135   }\r
136   WriteUInt16((UInt16)m_ExtraSize); // test it;\r
137   WriteBytes((const char *)item.Name, item.Name.Length());\r
138 \r
139   UInt32 extraPos = 0;\r
140   if (isZip64)\r
141   {\r
142     extraPos += 4 + 16;\r
143     WriteUInt16(NFileHeader::NExtraID::kZip64);\r
144     WriteUInt16(16);\r
145     WriteUInt64(item.UnPackSize);\r
146     WriteUInt64(item.PackSize);\r
147   }\r
148 \r
149   WriteExtra(item.LocalExtra);\r
150   extraPos += (UInt32)item.LocalExtra.GetSize();\r
151   for (; extraPos < m_ExtraSize; extraPos++)\r
152     WriteByte(0);\r
153 \r
154   m_OutBuffer.FlushWithCheck();\r
155   MoveBasePosition(item.PackSize);\r
156   SeekTo(m_BasePosition);\r
157 }\r
158 \r
159 void COutArchive::WriteCentralHeader(const CItem &item)\r
160 {\r
161   bool isUnPack64 = item.UnPackSize >= 0xFFFFFFFF;\r
162   bool isPack64 = item.PackSize >= 0xFFFFFFFF;\r
163   bool isPosition64 = item.LocalHeaderPosition >= 0xFFFFFFFF;\r
164   bool isZip64  = isPack64 || isUnPack64 || isPosition64;\r
165   \r
166   WriteUInt32(NSignature::kCentralFileHeader);\r
167   WriteByte(item.MadeByVersion.Version);\r
168   WriteByte(item.MadeByVersion.HostOS);\r
169   {\r
170     Byte ver = item.ExtractVersion.Version;\r
171     if (isZip64 && ver < NFileHeader::NCompressionMethod::kExtractVersion_Zip64)\r
172       ver = NFileHeader::NCompressionMethod::kExtractVersion_Zip64;\r
173     WriteByte(ver);\r
174   }\r
175   WriteByte(item.ExtractVersion.HostOS);\r
176   WriteUInt16(item.Flags);\r
177   WriteUInt16(item.CompressionMethod);\r
178   WriteUInt32(item.Time);\r
179   WriteUInt32(item.FileCRC);\r
180   WriteUInt32(isPack64 ? 0xFFFFFFFF: (UInt32)item.PackSize);\r
181   WriteUInt32(isUnPack64 ? 0xFFFFFFFF: (UInt32)item.UnPackSize);\r
182   WriteUInt16((UInt16)item.Name.Length());\r
183   UInt16 zip64ExtraSize = (UInt16)((isUnPack64 ? 8: 0) +  (isPack64 ? 8: 0) + (isPosition64 ? 8: 0));\r
184   const UInt16 kNtfsExtraSize = 4 + 2 + 2 + (3 * 8);\r
185   UInt16 centralExtraSize = (UInt16)(isZip64 ? (4 + zip64ExtraSize) : 0) + (item.NtfsTimeIsDefined ? (4 + kNtfsExtraSize) : 0);\r
186   centralExtraSize = (UInt16)(centralExtraSize + item.CentralExtra.GetSize());\r
187   WriteUInt16(centralExtraSize); // test it;\r
188   WriteUInt16((UInt16)item.Comment.GetCapacity());\r
189   WriteUInt16(0); // DiskNumberStart;\r
190   WriteUInt16(item.InternalAttributes);\r
191   WriteUInt32(item.ExternalAttributes);\r
192   WriteUInt32(isPosition64 ? 0xFFFFFFFF: (UInt32)item.LocalHeaderPosition);\r
193   WriteBytes((const char *)item.Name, item.Name.Length());\r
194   if (isZip64)\r
195   {\r
196     WriteUInt16(NFileHeader::NExtraID::kZip64);\r
197     WriteUInt16(zip64ExtraSize);\r
198     if(isUnPack64)\r
199       WriteUInt64(item.UnPackSize);\r
200     if(isPack64)\r
201       WriteUInt64(item.PackSize);\r
202     if(isPosition64)\r
203       WriteUInt64(item.LocalHeaderPosition);\r
204   }\r
205   if (item.NtfsTimeIsDefined)\r
206   {\r
207     WriteUInt16(NFileHeader::NExtraID::kNTFS);\r
208     WriteUInt16(kNtfsExtraSize);\r
209     WriteUInt32(0); // reserved\r
210     WriteUInt16(NFileHeader::NNtfsExtra::kTagTime);\r
211     WriteUInt16(8 * 3);\r
212     WriteUInt32(item.NtfsMTime.dwLowDateTime);\r
213     WriteUInt32(item.NtfsMTime.dwHighDateTime);\r
214     WriteUInt32(item.NtfsATime.dwLowDateTime);\r
215     WriteUInt32(item.NtfsATime.dwHighDateTime);\r
216     WriteUInt32(item.NtfsCTime.dwLowDateTime);\r
217     WriteUInt32(item.NtfsCTime.dwHighDateTime);\r
218   }\r
219   WriteExtra(item.CentralExtra);\r
220   if (item.Comment.GetCapacity() > 0)\r
221     WriteBytes(item.Comment, (UInt32)item.Comment.GetCapacity());\r
222 }\r
223 \r
224 void COutArchive::WriteCentralDir(const CObjectVector<CItem> &items, const CByteBuffer *comment)\r
225 {\r
226   SeekTo(m_BasePosition);\r
227   \r
228   UInt64 cdOffset = GetCurrentPosition();\r
229   for(int i = 0; i < items.Size(); i++)\r
230     WriteCentralHeader(items[i]);\r
231   UInt64 cd64EndOffset = GetCurrentPosition();\r
232   UInt64 cdSize = cd64EndOffset - cdOffset;\r
233   bool cdOffset64 = cdOffset >= 0xFFFFFFFF;\r
234   bool cdSize64 = cdSize >= 0xFFFFFFFF;\r
235   bool items64 = items.Size() >= 0xFFFF;\r
236   bool isZip64 = (cdOffset64 || cdSize64 || items64);\r
237 \r
238   if (isZip64)\r
239   {\r
240     WriteUInt32(NSignature::kZip64EndOfCentralDir);\r
241     WriteUInt64(kZip64EcdSize); // ThisDiskNumber = 0;\r
242     WriteUInt16(45); // version\r
243     WriteUInt16(45); // version\r
244     WriteUInt32(0); // ThisDiskNumber = 0;\r
245     WriteUInt32(0); // StartCentralDirectoryDiskNumber;;\r
246     WriteUInt64((UInt64)items.Size());\r
247     WriteUInt64((UInt64)items.Size());\r
248     WriteUInt64((UInt64)cdSize);\r
249     WriteUInt64((UInt64)cdOffset);\r
250 \r
251     WriteUInt32(NSignature::kZip64EndOfCentralDirLocator);\r
252     WriteUInt32(0); // number of the disk with the start of the zip64 end of central directory\r
253     WriteUInt64(cd64EndOffset);\r
254     WriteUInt32(1); // total number of disks\r
255   }\r
256   WriteUInt32(NSignature::kEndOfCentralDir);\r
257   WriteUInt16(0); // ThisDiskNumber = 0;\r
258   WriteUInt16(0); // StartCentralDirectoryDiskNumber;\r
259   WriteUInt16((UInt16)(items64 ? 0xFFFF: items.Size()));\r
260   WriteUInt16((UInt16)(items64 ? 0xFFFF: items.Size()));\r
261   WriteUInt32(cdSize64 ? 0xFFFFFFFF: (UInt32)cdSize);\r
262   WriteUInt32(cdOffset64 ? 0xFFFFFFFF: (UInt32)cdOffset);\r
263   UInt32 commentSize = (UInt32)(comment ? comment->GetCapacity() : 0);\r
264   WriteUInt16((UInt16)commentSize);\r
265   if (commentSize > 0)\r
266     WriteBytes((const Byte *)*comment, commentSize);\r
267   m_OutBuffer.FlushWithCheck();\r
268 }\r
269 \r
270 void COutArchive::CreateStreamForCompressing(IOutStream **outStream)\r
271 {\r
272   COffsetOutStream *streamSpec = new COffsetOutStream;\r
273   CMyComPtr<IOutStream> tempStream(streamSpec);\r
274   streamSpec->Init(m_Stream, m_BasePosition + m_LocalFileHeaderSize);\r
275   *outStream = tempStream.Detach();\r
276 }\r
277 \r
278 void COutArchive::SeekToPackedDataPosition()\r
279 {\r
280   SeekTo(m_BasePosition + m_LocalFileHeaderSize);\r
281 }\r
282 \r
283 void COutArchive::CreateStreamForCopying(ISequentialOutStream **outStream)\r
284 {\r
285   CMyComPtr<ISequentialOutStream> tempStream(m_Stream);\r
286   *outStream = tempStream.Detach();\r
287 }\r
288 \r
289 }}\r