Statistics
| Branch: | Revision:

root / trunk / hammock / src / net35 / ICSharpCode.SharpZipLib.Silverlight / Zip / ZipHelperStream.cs @ 0eea575a

History | View | Annotate | Download (16.9 kB)

1
// ZipHelperStream.cs
2
//
3
// Copyright 2006, 2007 John Reilly
4
//
5
// This program is free software; you can redistribute it and/or
6
// modify it under the terms of the GNU General Public License
7
// as published by the Free Software Foundation; either version 2
8
// of the License, or (at your option) any later version.
9
//
10
// This program is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
// GNU General Public License for more details.
14
//
15
// You should have received a copy of the GNU General Public License
16
// along with this program; if not, write to the Free Software
17
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
//
19
// Linking this library statically or dynamically with other modules is
20
// making a combined work based on this library.  Thus, the terms and
21
// conditions of the GNU General Public License cover the whole
22
// combination.
23
// 
24
// As a special exception, the copyright holders of this library give you
25
// permission to link this library with independent modules to produce an
26
// executable, regardless of the license terms of these independent
27
// modules, and to copy and distribute the resulting executable under
28
// terms of your choice, provided that you also meet, for each linked
29
// independent module, the terms and conditions of the license of that
30
// module.  An independent module is a module which is not derived from
31
// or based on this library.  If you modify this library, you may extend
32
// this exception to your version of the library, but you are not
33
// obligated to do so.  If you do not wish to do so, delete this
34
// exception statement from your version.
35

    
36
using System;
37
using System.IO;
38
using System.Text;
39
using ICSharpCode.SharpZipLib.Silverlight.Zip;
40

    
41
namespace ICSharpCode.SharpZipLib.Zip
42
{
43

    
44
	/// <summary>
45
	/// Holds data pertinent to a data descriptor.
46
	/// </summary>
47
	public class DescriptorData
48
	{
49
		/// <summary>
50
		/// Get /set the compressed size of data.
51
		/// </summary>
52
		public long CompressedSize
53
		{
54
			get { return compressedSize; }
55
			set { compressedSize = value; }
56
		}
57

    
58
		/// <summary>
59
		/// Get / set the uncompressed size of data
60
		/// </summary>
61
		public long Size
62
		{
63
			get { return size; }
64
			set { size = value; }
65
		}
66

    
67
		/// <summary>
68
		/// Get /set the crc value.
69
		/// </summary>
70
		public long Crc
71
		{
72
			get { return crc; }
73
			set { crc = (value & 0xffffffff); }
74
		}
75

    
76
		#region Instance Fields
77
		long size;
78
		long compressedSize;
79
		long crc;
80
		#endregion
81
	}
82

    
83
	class EntryPatchData
84
	{
85
		public long SizePatchOffset
86
		{
87
			get { return sizePatchOffset_; }
88
			set { sizePatchOffset_ = value; }
89
		}
90

    
91
		public long CrcPatchOffset
92
		{
93
			get { return crcPatchOffset_; }
94
			set { crcPatchOffset_ = value; }
95
		}
96
		
97
		#region Instance Fields
98
		long sizePatchOffset_;
99
		long crcPatchOffset_;
100
		#endregion
101
	}
102
	
103
	/// <summary>
104
	/// This class assists with writing/reading from Zip files.
105
	/// </summary>
106
	internal class ZipHelperStream : Stream
107
	{
108
		#region Constructors
109
		/// <summary>
110
		/// Initialise an instance of this class.
111
		/// </summary>
112
		/// <param name="name">The name of the file to open.</param>
113
		public ZipHelperStream(string name)
114
		{
115
			stream_ = new FileStream(name, FileMode.Open, FileAccess.ReadWrite);
116
			isOwner_ = true;
117
		}
118

    
119
		/// <summary>
120
		/// Initialise a new instance of <see cref="ZipHelperStream"/>.
121
		/// </summary>
122
		/// <param name="stream">The stream to use.</param>
123
		public ZipHelperStream(Stream stream)
124
		{
125
			stream_ = stream;
126
		}
127
		#endregion
128

    
129
		/// <summary>
130
		/// Get / set a value indicating wether the the underlying stream is owned or not.
131
		/// </summary>
132
		/// <remarks>If the stream is owned it is closed when this instance is closed.</remarks>
133
		public bool IsStreamOwner
134
		{
135
			get { return isOwner_; }
136
			set { isOwner_ = value; }
137
		}
138

    
139
		#region Base Stream Methods
140
		public override bool CanRead
141
		{
142
			get { return stream_.CanRead; }
143
		}
144

    
145
		public override bool CanSeek
146
		{
147
			get { return stream_.CanSeek; }
148
		}
149

    
150
		public override bool CanTimeout
151
		{
152
			get { return stream_.CanTimeout; }
153
		}
154

    
155
		public override long Length
156
		{
157
			get { return stream_.Length; }
158
		}
159

    
160
		public override long Position
161
		{
162
			get { return stream_.Position; }
163
			set { stream_.Position = value;	}
164
		}
165

    
166
		public override bool CanWrite
167
		{
168
			get { return stream_.CanWrite; }
169
		}
170

    
171
		public override void Flush()
172
		{
173
			stream_.Flush();
174
		}
175

    
176
		public override long Seek(long offset, SeekOrigin origin)
177
		{
178
			return stream_.Seek(offset, origin);
179
		}
180

    
181
		public override void SetLength(long value)
182
		{
183
			stream_.SetLength(value);
184
		}
185

    
186
		public override int Read(byte[] buffer, int offset, int count)
187
		{
188
			return stream_.Read(buffer, offset, count);
189
		}
190

    
191
		public override void Write(byte[] buffer, int offset, int count)
192
		{
193
			stream_.Write(buffer, offset, count);
194
		}
195

    
196
		/// <summary>
197
		/// Close the stream.
198
		/// </summary>
199
		/// <remarks>
200
		/// The underlying stream is closed only if <see cref="IsStreamOwner"/> is true.
201
		/// </remarks>
202
		override public void Close()
203
		{
204
			Stream toClose = stream_;
205
			stream_ = null;
206
			if (isOwner_ && (toClose != null))
207
			{
208
				isOwner_ = false;
209
				toClose.Close();
210
			}
211
		}
212
		#endregion
213
		
214
		// Write the local file header
215
		// TODO: ZipHelperStream.WriteLocalHeader is not yet used and needs checking for ZipFile and ZipOuptutStream usage
216
		void WriteLocalHeader(ZipEntry entry, EntryPatchData patchData) 
217
		{
218
			CompressionMethod method = entry.CompressionMethod;
219
			bool headerInfoAvailable = true; // How to get this?
220
			bool patchEntryHeader = false;
221

    
222
			WriteLEInt(ZipConstants.LocalHeaderSignature);
223
			
224
			WriteLEShort(entry.Version);
225
			WriteLEShort(entry.Flags);
226
			WriteLEShort((byte)method);
227
			WriteLEInt((int)entry.DosTime);
228

    
229
			if (headerInfoAvailable == true) {
230
				WriteLEInt((int)entry.Crc);
231
				if ( entry.LocalHeaderRequiresZip64 ) {
232
					WriteLEInt(-1);
233
					WriteLEInt(-1);
234
				}
235
				else {
236
					WriteLEInt(entry.IsCrypted ? (int)entry.CompressedSize + ZipConstants.CryptoHeaderSize : (int)entry.CompressedSize);
237
					WriteLEInt((int)entry.Size);
238
				}
239
			} else {
240
				if (patchData != null) {
241
					patchData.CrcPatchOffset = stream_.Position;
242
				}
243
				WriteLEInt(0);	// Crc
244
				
245
				if ( patchData != null ) {
246
					patchData.SizePatchOffset = stream_.Position;
247
				}
248

    
249
				// For local header both sizes appear in Zip64 Extended Information
250
				if ( entry.LocalHeaderRequiresZip64 && patchEntryHeader ) {
251
					WriteLEInt(-1);
252
					WriteLEInt(-1);
253
				}
254
				else {
255
					WriteLEInt(0);	// Compressed size
256
					WriteLEInt(0);	// Uncompressed size
257
				}
258
			}
259

    
260
			byte[] name = ZipConstants.ConvertToArray(entry.Flags, entry.Name);
261
			
262
			if (name.Length > 0xFFFF) {
263
				throw new ZipException("Entry name too long.");
264
			}
265

    
266
			ZipExtraData ed = new ZipExtraData(entry.ExtraData);
267

    
268
			if (entry.LocalHeaderRequiresZip64 && (headerInfoAvailable || patchEntryHeader)) {
269
				ed.StartNewEntry();
270
				if (headerInfoAvailable) {
271
					ed.AddLeLong(entry.Size);
272
					ed.AddLeLong(entry.CompressedSize);
273
				}
274
				else {
275
					ed.AddLeLong(-1);
276
					ed.AddLeLong(-1);
277
				}
278
				ed.AddNewEntry(1);
279

    
280
				if ( !ed.Find(1) ) {
281
					throw new ZipException("Internal error cant find extra data");
282
				}
283
				
284
				if ( patchData != null ) {
285
					patchData.SizePatchOffset = ed.CurrentReadIndex;
286
				}
287
			}
288
			else {
289
				ed.Delete(1);
290
			}
291
			
292
			byte[] extra = ed.GetEntryData();
293

    
294
			WriteLEShort(name.Length);
295
			WriteLEShort(extra.Length);
296

    
297
			if ( name.Length > 0 ) {
298
				stream_.Write(name, 0, name.Length);
299
			}
300
			
301
			if ( entry.LocalHeaderRequiresZip64 && patchEntryHeader ) {
302
				patchData.SizePatchOffset += stream_.Position;
303
			}
304

    
305
			if ( extra.Length > 0 ) {
306
				stream_.Write(extra, 0, extra.Length);
307
			}
308
		}
309
	
310
		/// <summary>
311
		/// Locates a block with the desired <paramref name="signature"/>.
312
		/// </summary>
313
		/// <param name="signature">The signature to find.</param>
314
		/// <param name="endLocation">Location, marking the end of block.</param>
315
		/// <param name="minimumBlockSize">Minimum size of the block.</param>
316
		/// <param name="maximumVariableData">The maximum variable data.</param>
317
		/// <returns>Eeturns the offset of the first byte after the signature; -1 if not found</returns>
318
		public long LocateBlockWithSignature(int signature, long endLocation, int minimumBlockSize, int maximumVariableData)
319
		{
320
			long pos = endLocation - minimumBlockSize;
321
			if ( pos < 0 ) {
322
				return -1;
323
			}
324

    
325
			long giveUpMarker = Math.Max(pos - maximumVariableData, 0);
326

    
327
			// TODO: This loop could be optimised for speed.
328
			do {
329
				if ( pos < giveUpMarker ) {
330
					return -1;
331
				}
332
				Seek(pos--, SeekOrigin.Begin);
333
			} while ( ReadLEInt() != signature );
334

    
335
			return Position;
336
		}
337

    
338
		/// <summary>
339
		/// Write Zip64 end of central directory records (File header and locator).
340
		/// </summary>
341
		/// <param name="noOfEntries">The number of entries in the central directory.</param>
342
		/// <param name="sizeEntries">The size of entries in the central directory.</param>
343
		/// <param name="centralDirOffset">The offset of the dentral directory.</param>
344
		public void WriteZip64EndOfCentralDirectory(long noOfEntries, long sizeEntries, long centralDirOffset)
345
		{
346
			long centralSignatureOffset = stream_.Position;
347
			WriteLEInt(ZipConstants.Zip64CentralFileHeaderSignature);
348
			WriteLELong(44);    // Size of this record (total size of remaining fields in header or full size - 12)
349
			WriteLEShort(ZipConstants.VersionMadeBy);   // Version made by
350
			WriteLEShort(ZipConstants.VersionZip64);   // Version to extract
351
			WriteLEInt(0);      // Number of this disk
352
			WriteLEInt(0);      // number of the disk with the start of the central directory
353
			WriteLELong(noOfEntries);       // No of entries on this disk
354
			WriteLELong(noOfEntries);       // Total No of entries in central directory
355
			WriteLELong(sizeEntries);       // Size of the central directory
356
			WriteLELong(centralDirOffset);  // offset of start of central directory
357
			// zip64 extensible data sector not catered for here (variable size)
358

    
359
			// Write the Zip64 end of central directory locator
360
			WriteLEInt(ZipConstants.Zip64CentralDirLocatorSignature);
361

    
362
			// no of the disk with the start of the zip64 end of central directory
363
			WriteLEInt(0);
364

    
365
			// relative offset of the zip64 end of central directory record
366
			WriteLELong(centralSignatureOffset);
367

    
368
			// total number of disks
369
			WriteLEInt(1);
370
		}
371

    
372
		/// <summary>
373
		/// Write the required records to end the central directory.
374
		/// </summary>
375
		/// <param name="noOfEntries">The number of entries in the directory.</param>
376
		/// <param name="sizeEntries">The size of the entries in the directory.</param>
377
		/// <param name="startOfCentralDirectory">The start of the central directory.</param>
378
		/// <param name="comment">The archive comment.  (This can be null).</param>
379
		public void WriteEndOfCentralDirectory(long noOfEntries, long sizeEntries,
380
			long startOfCentralDirectory, byte[] comment)
381
		{
382

    
383
			if ( (noOfEntries >= 0xffff) ||
384
				(startOfCentralDirectory >= 0xffffffff) ||
385
				(sizeEntries >= 0xffffffff) ) {
386
				WriteZip64EndOfCentralDirectory(noOfEntries, sizeEntries, startOfCentralDirectory);
387
			}
388

    
389
			WriteLEInt(ZipConstants.EndOfCentralDirectorySignature);
390

    
391
			// TODO: ZipFile Multi disk handling not done
392
			WriteLEShort(0);                    // number of this disk
393
			WriteLEShort(0);                    // no of disk with start of central dir
394

    
395
			
396
			// Number of entries
397
			if ( noOfEntries >= 0xffff ) {
398
				WriteLEUshort(0xffff);  // Zip64 marker
399
				WriteLEUshort(0xffff);
400
			}
401
			else {
402
				WriteLEShort(( short )noOfEntries);          // entries in central dir for this disk
403
				WriteLEShort(( short )noOfEntries);          // total entries in central directory
404
			}
405

    
406
			// Size of the central directory
407
			if ( sizeEntries >= 0xffffffff ) {
408
				WriteLEUint(0xffffffff);    // Zip64 marker
409
			}
410
			else {
411
				WriteLEInt(( int )sizeEntries);            
412
			}
413

    
414

    
415
			// offset of start of central directory
416
			if ( startOfCentralDirectory >= 0xffffffff ) {
417
				WriteLEUint(0xffffffff);    // Zip64 marker
418
			}
419
			else {
420
				WriteLEInt(( int )startOfCentralDirectory);
421
			}
422

    
423
			int commentLength = (comment != null) ? comment.Length : 0;
424

    
425
			if ( commentLength > 0xffff ) {
426
				throw new ZipException(string.Format("Comment length({0}) is too long can only be 64K", commentLength));
427
			}
428

    
429
			WriteLEShort(commentLength);
430

    
431
			if ( commentLength > 0 ) {
432
				Write(comment, 0, comment.Length);
433
			}
434
		}
435

    
436
		#region LE value reading/writing
437
		/// <summary>
438
		/// Read an unsigned short in little endian byte order.
439
		/// </summary>
440
		/// <returns>Returns the value read.</returns>
441
		/// <exception cref="IOException">
442
		/// An i/o error occurs.
443
		/// </exception>
444
		/// <exception cref="EndOfStreamException">
445
		/// The file ends prematurely
446
		/// </exception>
447
		public int ReadLEShort()
448
		{
449
			int byteValue1 = stream_.ReadByte();
450

    
451
			if (byteValue1 < 0) {
452
				throw new EndOfStreamException();
453
			}
454

    
455
			int byteValue2 = stream_.ReadByte();
456
			if (byteValue2 < 0) {
457
				throw new EndOfStreamException();
458
			}
459

    
460
			return byteValue1 | (byteValue2 << 8);
461
		}
462

    
463
		/// <summary>
464
		/// Read an int in little endian byte order.
465
		/// </summary>
466
		/// <returns>Returns the value read.</returns>
467
		/// <exception cref="IOException">
468
		/// An i/o error occurs.
469
		/// </exception>
470
		/// <exception cref="System.IO.EndOfStreamException">
471
		/// The file ends prematurely
472
		/// </exception>
473
		public int ReadLEInt()
474
		{
475
			return ReadLEShort() | (ReadLEShort() << 16);
476
		}
477

    
478
		/// <summary>
479
		/// Read a long in little endian byte order.
480
		/// </summary>
481
		/// <returns>The value read.</returns>
482
		public long ReadLELong()
483
		{
484
			return (uint)ReadLEInt() | ((long)ReadLEInt() << 32);
485
		}
486

    
487
		/// <summary>
488
		/// Write an unsigned short in little endian byte order.
489
		/// </summary>
490
		/// <param name="value">The value to write.</param>
491
		public void WriteLEShort(int value)
492
		{
493
			stream_.WriteByte(( byte )(value & 0xff));
494
			stream_.WriteByte(( byte )((value >> 8) & 0xff));
495
		}
496

    
497
		/// <summary>
498
		/// Write a ushort in little endian byte order.
499
		/// </summary>
500
		/// <param name="value">The value to write.</param>
501
		public void WriteLEUshort(ushort value)
502
		{
503
			stream_.WriteByte(( byte )(value & 0xff));
504
			stream_.WriteByte(( byte )(value >> 8));
505
		}
506

    
507
		/// <summary>
508
		/// Write an int in little endian byte order.
509
		/// </summary>
510
		/// <param name="value">The value to write.</param>
511
		public void WriteLEInt(int value)
512
		{
513
			WriteLEShort(value);
514
			WriteLEShort(value >> 16);
515
		}
516

    
517
		/// <summary>
518
		/// Write a uint in little endian byte order.
519
		/// </summary>
520
		/// <param name="value">The value to write.</param>
521
		public void WriteLEUint(uint value)
522
		{
523
			WriteLEUshort(( ushort )(value & 0xffff));
524
			WriteLEUshort(( ushort )(value >> 16));
525
		}
526

    
527
		/// <summary>
528
		/// Write a long in little endian byte order.
529
		/// </summary>
530
		/// <param name="value">The value to write.</param>
531
		public void WriteLELong(long value)
532
		{
533
			WriteLEInt(( int )value);
534
			WriteLEInt(( int )(value >> 32));
535
		}
536

    
537
		/// <summary>
538
		/// Write a ulong in little endian byte order.
539
		/// </summary>
540
		/// <param name="value">The value to write.</param>
541
		public void WriteLEUlong(ulong value)
542
		{
543
			WriteLEUint(( uint )(value & 0xffffffff));
544
			WriteLEUint(( uint )(value >> 32));
545
		}
546

    
547
		#endregion
548

    
549
		/// <summary>
550
		/// Write a data descriptor.
551
		/// </summary>
552
		/// <param name="entry">The entry to write a descriptor for.</param>
553
		/// <returns>Returns the number of descriptor bytes written.</returns>
554
		public int WriteDataDescriptor(ZipEntry entry)
555
		{
556
			if (entry == null) {
557
				throw new ArgumentNullException("entry");
558
			}
559

    
560
			int result=0;
561

    
562
			// Add data descriptor if flagged as required
563
			if ((entry.Flags & (int)GeneralBitFlags.Descriptor) != 0)
564
			{
565
				// The signature is not PKZIP originally but is now described as optional
566
				// in the PKZIP Appnote documenting trhe format.
567
				WriteLEInt(ZipConstants.DataDescriptorSignature);
568
				WriteLEInt(unchecked((int)(entry.Crc)));
569

    
570
				result+=8;
571

    
572
				if (entry.LocalHeaderRequiresZip64)
573
				{
574
					WriteLELong(entry.CompressedSize);
575
					WriteLELong(entry.Size);
576
					result+=16;
577
				}
578
				else
579
				{
580
					WriteLEInt((int)entry.CompressedSize);
581
					WriteLEInt((int)entry.Size);
582
					result+=8;
583
				}
584
			}
585

    
586
			return result;
587
		}
588

    
589
		/// <summary>
590
		/// Read data descriptor at the end of compressed data.
591
		/// </summary>
592
		/// <param name="zip64">if set to <c>true</c> [zip64].</param>
593
		/// <param name="data">The data to fill in.</param>
594
		/// <returns>Returns the number of bytes read in the descriptor.</returns>
595
		public void ReadDataDescriptor(bool zip64, DescriptorData data)
596
		{
597
			int intValue = ReadLEInt();
598

    
599
			// In theory this may not be a descriptor according to PKZIP appnote.
600
			// In practise its always there.
601
			if (intValue != ZipConstants.DataDescriptorSignature) {
602
				throw new ZipException("Data descriptor signature not found");
603
			}
604

    
605
			data.Crc = ReadLEInt();
606
			
607
			if (zip64) {
608
				data.CompressedSize = ReadLELong();
609
				data.Size = ReadLELong();
610
			}
611
			else {
612
				data.CompressedSize = ReadLEInt();
613
				data.Size = ReadLEInt();
614
			}
615
		}
616

    
617
		#region Instance Fields
618
		bool isOwner_;
619
		Stream stream_;
620
		#endregion
621
	}
622
}