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 |
} |