[runtime] Fixed get_process_module module name.
[mono.git] / mcs / class / System.IO.Compression / SharpCompress / Writer / Zip / ZipWriter.cs
1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using System.Text;
5 using SharpCompress.Common;
6 using SharpCompress.Common.Zip;
7 using SharpCompress.Common.Zip.Headers;
8 using SharpCompress.Compressor;
9 #if BZIP2
10 using SharpCompress.Compressor.BZip2;
11 #endif
12 using SharpCompress.Compressor.Deflate;
13 #if LZMA
14 using SharpCompress.Compressor.LZMA;
15 #endif
16 #if PPMd
17 using SharpCompress.Compressor.PPMd;
18 #endif
19 using SharpCompress.IO;
20 #if DEFLATE
21 using DeflateStream = SharpCompress.Compressor.Deflate.DeflateStream;
22 #endif
23
24 namespace SharpCompress.Writer.Zip
25 {
26     internal class ZipWriter : AbstractWriter
27     {
28         private readonly ZipCompressionMethod compression;
29         private readonly CompressionLevel deflateCompressionLevel;
30
31         private readonly List<ZipCentralDirectoryEntry> entries = new List<ZipCentralDirectoryEntry>();
32         private readonly string zipComment;
33         private long streamPosition;
34
35 #if PPMd
36         private readonly PpmdProperties ppmdProperties; // Caching properties to speed up PPMd.
37 #endif
38
39         public ZipWriter(Stream destination, CompressionInfo compressionInfo, string zipComment)
40             : base(ArchiveType.Zip)
41         {
42             this.zipComment = zipComment ?? string.Empty;
43
44             switch (compressionInfo.Type)
45             {
46                 case CompressionType.None:
47                     {
48                         compression = ZipCompressionMethod.None;
49                     }
50                     break;
51                 case CompressionType.Deflate:
52                     {
53                         compression = ZipCompressionMethod.Deflate;
54                         deflateCompressionLevel = compressionInfo.DeflateCompressionLevel;
55                     }
56                     break;
57                 case CompressionType.BZip2:
58                     {
59                         compression = ZipCompressionMethod.BZip2;
60                     }
61                     break;
62 #if LZMA
63                 case CompressionType.LZMA:
64                     {
65                         compression = ZipCompressionMethod.LZMA;
66                     }
67                     break;
68 #endif
69 #if PPMd
70                 case CompressionType.PPMd:
71                     {
72                         ppmdProperties = new PpmdProperties();
73                         compression = ZipCompressionMethod.PPMd;
74                     }
75                     break;
76 #endif
77                 default:
78                     throw new InvalidFormatException("Invalid compression method: " + compressionInfo.Type);
79             }
80             InitalizeStream(destination, false);
81         }
82
83         protected override void Dispose(bool isDisposing)
84         {
85             if (isDisposing)
86             {
87                 uint size = 0;
88                 foreach (ZipCentralDirectoryEntry entry in entries)
89                 {
90                     size += entry.Write(OutputStream, compression);
91                 }
92                 WriteEndRecord(size);
93             }
94             base.Dispose(isDisposing);
95         }
96
97         public override void Write(string entryPath, Stream source, DateTime? modificationTime)
98         {
99             Write(entryPath, source, modificationTime, null);
100         }
101
102         public void Write(string entryPath, Stream source, DateTime? modificationTime, string comment)
103         {
104             using (Stream output = WriteToStream(entryPath, modificationTime, comment))
105             {
106                 source.TransferTo(output);
107             }
108         }
109
110         public Stream WriteToStream(string entryPath, DateTime? modificationTime, string comment)
111         {
112             entryPath = NormalizeFilename(entryPath);
113             modificationTime = modificationTime ?? DateTime.Now;
114             comment = comment ?? "";
115             var entry = new ZipCentralDirectoryEntry
116                             {
117                                 Comment = comment,
118                                 FileName = entryPath,
119                                 ModificationTime = modificationTime,
120                                 HeaderOffset = (uint) streamPosition,
121                             };
122             var headersize = (uint) WriteHeader(entryPath, modificationTime);
123             streamPosition += headersize;
124             return new ZipWritingStream(this, OutputStream, entry);
125         }
126
127         private string NormalizeFilename(string filename)
128         {
129             filename = filename.Replace('\\', '/');
130
131             int pos = filename.IndexOf(':');
132             if (pos >= 0)
133                 filename = filename.Remove(0, pos + 1);
134
135             return filename;
136         }
137
138         private int WriteHeader(string filename, DateTime? modificationTime)
139         {
140             byte[] encodedFilename = Encoding.UTF8.GetBytes(filename);
141
142             OutputStream.Write(BitConverter.GetBytes(ZipHeaderFactory.ENTRY_HEADER_BYTES), 0, 4);
143             OutputStream.Write(new byte[] {63, 0}, 0, 2); //version
144             HeaderFlags flags = HeaderFlags.UTF8;
145             if (!OutputStream.CanSeek)
146             {
147                 flags |= HeaderFlags.UsePostDataDescriptor;
148                 if (compression == ZipCompressionMethod.LZMA)
149                 {
150                     flags |= HeaderFlags.Bit1; // eos marker
151                 }
152             }
153             OutputStream.Write(BitConverter.GetBytes((ushort) flags), 0, 2);
154             OutputStream.Write(BitConverter.GetBytes((ushort) compression), 0, 2); // zipping method
155             OutputStream.Write(BitConverter.GetBytes(modificationTime.DateTimeToDosTime()), 0, 4);
156             // zipping date and time
157             OutputStream.Write(new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, 12);
158             // unused CRC, un/compressed size, updated later
159             OutputStream.Write(BitConverter.GetBytes((ushort) encodedFilename.Length), 0, 2); // filename length
160             OutputStream.Write(BitConverter.GetBytes((ushort) 0), 0, 2); // extra length
161             OutputStream.Write(encodedFilename, 0, encodedFilename.Length);
162
163             return 6 + 2 + 2 + 4 + 12 + 2 + 2 + encodedFilename.Length;
164         }
165
166         private void WriteFooter(uint crc, uint compressed, uint uncompressed)
167         {
168             OutputStream.Write(BitConverter.GetBytes(crc), 0, 4);
169             OutputStream.Write(BitConverter.GetBytes(compressed), 0, 4);
170             OutputStream.Write(BitConverter.GetBytes(uncompressed), 0, 4);
171         }
172
173         private void WriteEndRecord(uint size)
174         {
175             byte[] encodedComment = Encoding.UTF8.GetBytes(zipComment);
176
177             OutputStream.Write(new byte[] {80, 75, 5, 6, 0, 0, 0, 0}, 0, 8);
178             OutputStream.Write(BitConverter.GetBytes((ushort) entries.Count), 0, 2);
179             OutputStream.Write(BitConverter.GetBytes((ushort) entries.Count), 0, 2);
180             OutputStream.Write(BitConverter.GetBytes(size), 0, 4);
181             OutputStream.Write(BitConverter.GetBytes((uint) streamPosition), 0, 4);
182             OutputStream.Write(BitConverter.GetBytes((ushort) encodedComment.Length), 0, 2);
183             OutputStream.Write(encodedComment, 0, encodedComment.Length);
184         }
185
186         #region Nested type: ZipWritingStream
187
188         internal class ZipWritingStream : Stream
189         {
190             private readonly CRC32 crc = new CRC32();
191             private readonly ZipCentralDirectoryEntry entry;
192             private readonly Stream originalStream;
193             private readonly Stream writeStream;
194             private readonly ZipWriter writer;
195             private CountingWritableSubStream counting;
196             private uint decompressed;
197
198             internal ZipWritingStream(ZipWriter writer, Stream originalStream, ZipCentralDirectoryEntry entry)
199             {
200                 this.writer = writer;
201                 this.originalStream = originalStream;
202                 writeStream = GetWriteStream(originalStream);
203                 this.writer = writer;
204                 this.entry = entry;
205             }
206
207             public override bool CanRead
208             {
209                 get { return false; }
210             }
211
212             public override bool CanSeek
213             {
214                 get { return false; }
215             }
216
217             public override bool CanWrite
218             {
219                 get { return true; }
220             }
221
222             public override long Length
223             {
224                 get { throw new NotSupportedException(); }
225             }
226
227             public override long Position
228             {
229                 get { throw new NotSupportedException(); }
230                 set { throw new NotSupportedException(); }
231             }
232
233             private Stream GetWriteStream(Stream writeStream)
234             {
235                 counting = new CountingWritableSubStream(writeStream);
236                 Stream output = counting;
237                 switch (writer.compression)
238                 {
239                     case ZipCompressionMethod.None:
240                         {
241                             return output;
242                         }
243                     case ZipCompressionMethod.Deflate:
244                         {
245                             return new System.IO.Compression.DeflateStream(counting,
246                                 System.IO.Compression.CompressionMode.Compress, true);
247                         }
248 #if BZIP2
249                     case ZipCompressionMethod.BZip2:
250                         {
251                             return new BZip2Stream(counting, CompressionMode.Compress, true);
252                         }
253 #endif
254 #if LZMA
255                     case ZipCompressionMethod.LZMA:
256                         {
257                             counting.WriteByte(9);
258                             counting.WriteByte(20);
259                             counting.WriteByte(5);
260                             counting.WriteByte(0);
261
262                             LzmaStream lzmaStream = new LzmaStream(new LzmaEncoderProperties(!originalStream.CanSeek),
263                                                                    false, counting);
264                             counting.Write(lzmaStream.Properties, 0, lzmaStream.Properties.Length);
265                             return lzmaStream;
266                         }
267 #endif
268 #if PPMd
269                     case ZipCompressionMethod.PPMd:
270                         {
271                             counting.Write(writer.ppmdProperties.Properties, 0, 2);
272                             return new PpmdStream(writer.ppmdProperties, counting, true);
273                         }
274 #endif
275                     default:
276                         {
277                             throw new NotSupportedException("CompressionMethod: " + writer.compression);
278                         }
279                 }
280             }
281
282             protected override void Dispose(bool disposing)
283             {
284                 base.Dispose(disposing);
285                 if (disposing)
286                 {
287                     writeStream.Dispose();
288                     entry.Crc = (uint) crc.Crc32Result;
289                     entry.Compressed = counting.Count;
290                     entry.Decompressed = decompressed;
291                     if (originalStream.CanSeek)
292                     {
293                         originalStream.Position = entry.HeaderOffset + 6;
294                         originalStream.WriteByte(0);
295                         originalStream.Position = entry.HeaderOffset + 14;
296                         writer.WriteFooter(entry.Crc, counting.Count, decompressed);
297                         originalStream.Position = writer.streamPosition + entry.Compressed;
298                         writer.streamPosition += entry.Compressed;
299                     }
300                     else
301                     {
302                         originalStream.Write(BitConverter.GetBytes(ZipHeaderFactory.POST_DATA_DESCRIPTOR), 0, 4);
303                         writer.WriteFooter(entry.Crc, counting.Count, decompressed);
304                         writer.streamPosition += entry.Compressed + 16;
305                     }
306                     writer.entries.Add(entry);
307                 }
308             }
309
310             public override void Flush()
311             {
312                 writeStream.Flush();
313             }
314
315             public override int Read(byte[] buffer, int offset, int count)
316             {
317                 throw new NotSupportedException();
318             }
319
320             public override long Seek(long offset, SeekOrigin origin)
321             {
322                 throw new NotSupportedException();
323             }
324
325             public override void SetLength(long value)
326             {
327                 throw new NotSupportedException();
328             }
329
330             public override void Write(byte[] buffer, int offset, int count)
331             {
332                 decompressed += (uint) count;
333                 crc.SlurpBlock(buffer, offset, count);
334                 writeStream.Write(buffer, offset, count);
335             }
336         }
337
338         #endregion
339     }
340 }