root/branches/Abonament/BazaReklam.Updater/ICSharpCode.SharpZipLib/Zip/ZipHelperStream.cs @ 810

Wersja 597, 17.6 KB (wprowadzona przez marek, 17 years temu)

re #165

Line 
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
36using System;
37using System.IO;
38using System.Text;
39
40namespace ICSharpCode.SharpZipLib.Zip
41{
42
43        /// <summary>
44        /// Holds data pertinent to a data descriptor.
45        /// </summary>
46        public class DescriptorData
47        {
48                /// <summary>
49                /// Get /set the compressed size of data.
50                /// </summary>
51                public long CompressedSize
52                {
53                        get { return compressedSize; }
54                        set { compressedSize = value; }
55                }
56
57                /// <summary>
58                /// Get / set the uncompressed size of data
59                /// </summary>
60                public long Size
61                {
62                        get { return size; }
63                        set { size = value; }
64                }
65
66                /// <summary>
67                /// Get /set the crc value.
68                /// </summary>
69                public long Crc
70                {
71                        get { return crc; }
72                        set { crc = (value & 0xffffffff); }
73                }
74
75                #region Instance Fields
76                long size;
77                long compressedSize;
78                long crc;
79                #endregion
80        }
81
82        class EntryPatchData
83        {
84                public long SizePatchOffset
85                {
86                        get { return sizePatchOffset_; }
87                        set { sizePatchOffset_ = value; }
88                }
89
90                public long CrcPatchOffset
91                {
92                        get { return crcPatchOffset_; }
93                        set { crcPatchOffset_ = value; }
94                }
95               
96                #region Instance Fields
97                long sizePatchOffset_;
98                long crcPatchOffset_;
99                #endregion
100        }
101       
102        /// <summary>
103        /// This class assists with writing/reading from Zip files.
104        /// </summary>
105        internal class ZipHelperStream : Stream
106        {
107                #region Constructors
108                /// <summary>
109                /// Initialise an instance of this class.
110                /// </summary>
111                /// <param name="name">The name of the file to open.</param>
112                public ZipHelperStream(string name)
113                {
114                        stream_ = new FileStream(name, FileMode.Open, FileAccess.ReadWrite);
115                        isOwner_ = true;
116                }
117
118                /// <summary>
119                /// Initialise a new instance of <see cref="ZipHelperStream"/>.
120                /// </summary>
121                /// <param name="stream">The stream to use.</param>
122                public ZipHelperStream(Stream stream)
123                {
124                        stream_ = stream;
125                }
126                #endregion
127
128                /// <summary>
129                /// Get / set a value indicating wether the the underlying stream is owned or not.
130                /// </summary>
131                /// <remarks>If the stream is owned it is closed when this instance is closed.</remarks>
132                public bool IsStreamOwner
133                {
134                        get { return isOwner_; }
135                        set { isOwner_ = value; }
136                }
137
138                #region Base Stream Methods
139                public override bool CanRead
140                {
141                        get { return stream_.CanRead; }
142                }
143
144                public override bool CanSeek
145                {
146                        get { return stream_.CanSeek; }
147                }
148
149#if !NET_1_0 && !NET_1_1 && !NETCF_1_0
150                public override bool CanTimeout
151                {
152                        get { return stream_.CanTimeout; }
153                }
154#endif
155
156                public override long Length
157                {
158                        get { return stream_.Length; }
159                }
160
161                public override long Position
162                {
163                        get { return stream_.Position; }
164                        set { stream_.Position = value; }
165                }
166
167                public override bool CanWrite
168                {
169                        get { return stream_.CanWrite; }
170                }
171
172                public override void Flush()
173                {
174                        stream_.Flush();
175                }
176
177                public override long Seek(long offset, SeekOrigin origin)
178                {
179                        return stream_.Seek(offset, origin);
180                }
181
182                public override void SetLength(long value)
183                {
184                        stream_.SetLength(value);
185                }
186
187                public override int Read(byte[] buffer, int offset, int count)
188                {
189                        return stream_.Read(buffer, offset, count);
190                }
191
192                public override void Write(byte[] buffer, int offset, int count)
193                {
194                        stream_.Write(buffer, offset, count);
195                }
196
197                /// <summary>
198                /// Close the stream.
199                /// </summary>
200                /// <remarks>
201                /// The underlying stream is closed only if <see cref="IsStreamOwner"/> is true.
202                /// </remarks>
203                override public void Close()
204                {
205                        Stream toClose = stream_;
206                        stream_ = null;
207                        if (isOwner_ && (toClose != null))
208                        {
209                                isOwner_ = false;
210                                toClose.Close();
211                        }
212                }
213                #endregion
214               
215                // Write the local file header
216                // TODO: ZipHelperStream.WriteLocalHeader is not yet used and needs checking for ZipFile and ZipOuptutStream usage
217                void WriteLocalHeader(ZipEntry entry, EntryPatchData patchData)
218                {
219                        CompressionMethod method = entry.CompressionMethod;
220                        bool headerInfoAvailable = true; // How to get this?
221                        bool patchEntryHeader = false;
222
223                        WriteLEInt(ZipConstants.LocalHeaderSignature);
224                       
225                        WriteLEShort(entry.Version);
226                        WriteLEShort(entry.Flags);
227                        WriteLEShort((byte)method);
228                        WriteLEInt((int)entry.DosTime);
229
230                        if (headerInfoAvailable == true) {
231                                WriteLEInt((int)entry.Crc);
232                                if ( entry.LocalHeaderRequiresZip64 ) {
233                                        WriteLEInt(-1);
234                                        WriteLEInt(-1);
235                                }
236                                else {
237                                        WriteLEInt(entry.IsCrypted ? (int)entry.CompressedSize + ZipConstants.CryptoHeaderSize : (int)entry.CompressedSize);
238                                        WriteLEInt((int)entry.Size);
239                                }
240                        } else {
241                                if (patchData != null) {
242                                        patchData.CrcPatchOffset = stream_.Position;
243                                }
244                                WriteLEInt(0);  // Crc
245                               
246                                if ( patchData != null ) {
247                                        patchData.SizePatchOffset = stream_.Position;
248                                }
249
250                                // For local header both sizes appear in Zip64 Extended Information
251                                if ( entry.LocalHeaderRequiresZip64 && patchEntryHeader ) {
252                                        WriteLEInt(-1);
253                                        WriteLEInt(-1);
254                                }
255                                else {
256                                        WriteLEInt(0);  // Compressed size
257                                        WriteLEInt(0);  // Uncompressed size
258                                }
259                        }
260
261                        byte[] name = ZipConstants.ConvertToArray(entry.Flags, entry.Name);
262                       
263                        if (name.Length > 0xFFFF) {
264                                throw new ZipException("Entry name too long.");
265                        }
266
267                        ZipExtraData ed = new ZipExtraData(entry.ExtraData);
268
269                        if (entry.LocalHeaderRequiresZip64 && (headerInfoAvailable || patchEntryHeader)) {
270                                ed.StartNewEntry();
271                                if (headerInfoAvailable) {
272                                        ed.AddLeLong(entry.Size);
273                                        ed.AddLeLong(entry.CompressedSize);
274                                }
275                                else {
276                                        ed.AddLeLong(-1);
277                                        ed.AddLeLong(-1);
278                                }
279                                ed.AddNewEntry(1);
280
281                                if ( !ed.Find(1) ) {
282                                        throw new ZipException("Internal error cant find extra data");
283                                }
284                               
285                                if ( patchData != null ) {
286                                        patchData.SizePatchOffset = ed.CurrentReadIndex;
287                                }
288                        }
289                        else {
290                                ed.Delete(1);
291                        }
292                       
293                        byte[] extra = ed.GetEntryData();
294
295                        WriteLEShort(name.Length);
296                        WriteLEShort(extra.Length);
297
298                        if ( name.Length > 0 ) {
299                                stream_.Write(name, 0, name.Length);
300                        }
301                       
302                        if ( entry.LocalHeaderRequiresZip64 && patchEntryHeader ) {
303                                patchData.SizePatchOffset += stream_.Position;
304                        }
305
306                        if ( extra.Length > 0 ) {
307                                stream_.Write(extra, 0, extra.Length);
308                        }
309                }
310       
311                /// <summary>
312                /// Locates a block with the desired <paramref name="signature"/>.
313                /// </summary>
314                /// <param name="signature">The signature to find.</param>
315                /// <param name="endLocation">Location, marking the end of block.</param>
316                /// <param name="minimumBlockSize">Minimum size of the block.</param>
317                /// <param name="maximumVariableData">The maximum variable data.</param>
318                /// <returns>Eeturns the offset of the first byte after the signature; -1 if not found</returns>
319                public long LocateBlockWithSignature(int signature, long endLocation, int minimumBlockSize, int maximumVariableData)
320                {
321                        long pos = endLocation - minimumBlockSize;
322                        if ( pos < 0 ) {
323                                return -1;
324                        }
325
326                        long giveUpMarker = Math.Max(pos - maximumVariableData, 0);
327
328                        // TODO: This loop could be optimised for speed.
329                        do {
330                                if ( pos < giveUpMarker ) {
331                                        return -1;
332                                }
333                                Seek(pos--, SeekOrigin.Begin);
334                        } while ( ReadLEInt() != signature );
335
336                        return Position;
337                }
338
339                /// <summary>
340                /// Write Zip64 end of central directory records (File header and locator).
341                /// </summary>
342                /// <param name="noOfEntries">The number of entries in the central directory.</param>
343                /// <param name="sizeEntries">The size of entries in the central directory.</param>
344                /// <param name="centralDirOffset">The offset of the dentral directory.</param>
345                public void WriteZip64EndOfCentralDirectory(long noOfEntries, long sizeEntries, long centralDirOffset)
346                {
347                        long centralSignatureOffset = stream_.Position;
348                        WriteLEInt(ZipConstants.Zip64CentralFileHeaderSignature);
349                        WriteLELong(44);    // Size of this record (total size of remaining fields in header or full size - 12)
350                        WriteLEShort(ZipConstants.VersionMadeBy);   // Version made by
351                        WriteLEShort(ZipConstants.VersionZip64);   // Version to extract
352                        WriteLEInt(0);      // Number of this disk
353                        WriteLEInt(0);      // number of the disk with the start of the central directory
354                        WriteLELong(noOfEntries);       // No of entries on this disk
355                        WriteLELong(noOfEntries);       // Total No of entries in central directory
356                        WriteLELong(sizeEntries);       // Size of the central directory
357                        WriteLELong(centralDirOffset);  // offset of start of central directory
358                        // zip64 extensible data sector not catered for here (variable size)
359
360                        // Write the Zip64 end of central directory locator
361                        WriteLEInt(ZipConstants.Zip64CentralDirLocatorSignature);
362
363                        // no of the disk with the start of the zip64 end of central directory
364                        WriteLEInt(0);
365
366                        // relative offset of the zip64 end of central directory record
367                        WriteLELong(centralSignatureOffset);
368
369                        // total number of disks
370                        WriteLEInt(1);
371                }
372
373                /// <summary>
374                /// Write the required records to end the central directory.
375                /// </summary>
376                /// <param name="noOfEntries">The number of entries in the directory.</param>
377                /// <param name="sizeEntries">The size of the entries in the directory.</param>
378                /// <param name="startOfCentralDirectory">The start of the central directory.</param>
379                /// <param name="comment">The archive comment.  (This can be null).</param>
380                public void WriteEndOfCentralDirectory(long noOfEntries, long sizeEntries,
381                        long startOfCentralDirectory, byte[] comment)
382                {
383
384                        if ( (noOfEntries >= 0xffff) ||
385                                (startOfCentralDirectory >= 0xffffffff) ||
386                                (sizeEntries >= 0xffffffff) ) {
387                                WriteZip64EndOfCentralDirectory(noOfEntries, sizeEntries, startOfCentralDirectory);
388                        }
389
390                        WriteLEInt(ZipConstants.EndOfCentralDirectorySignature);
391
392                        // TODO: ZipFile Multi disk handling not done
393                        WriteLEShort(0);                    // number of this disk
394                        WriteLEShort(0);                    // no of disk with start of central dir
395
396                       
397                        // Number of entries
398                        if ( noOfEntries >= 0xffff ) {
399                                WriteLEUshort(0xffff);  // Zip64 marker
400                                WriteLEUshort(0xffff);
401                        }
402                        else {
403                                WriteLEShort(( short )noOfEntries);          // entries in central dir for this disk
404                                WriteLEShort(( short )noOfEntries);          // total entries in central directory
405                        }
406
407                        // Size of the central directory
408                        if ( sizeEntries >= 0xffffffff ) {
409                                WriteLEUint(0xffffffff);    // Zip64 marker
410                        }
411                        else {
412                                WriteLEInt(( int )sizeEntries);           
413                        }
414
415
416                        // offset of start of central directory
417                        if ( startOfCentralDirectory >= 0xffffffff ) {
418                                WriteLEUint(0xffffffff);    // Zip64 marker
419                        }
420                        else {
421                                WriteLEInt(( int )startOfCentralDirectory);
422                        }
423
424                        int commentLength = (comment != null) ? comment.Length : 0;
425
426                        if ( commentLength > 0xffff ) {
427                                throw new ZipException(string.Format("Comment length({0}) is too long can only be 64K", commentLength));
428                        }
429
430                        WriteLEShort(commentLength);
431
432                        if ( commentLength > 0 ) {
433                                Write(comment, 0, comment.Length);
434                        }
435                }
436
437                #region LE value reading/writing
438                /// <summary>
439                /// Read an unsigned short in little endian byte order.
440                /// </summary>
441                /// <returns>Returns the value read.</returns>
442                /// <exception cref="IOException">
443                /// An i/o error occurs.
444                /// </exception>
445                /// <exception cref="EndOfStreamException">
446                /// The file ends prematurely
447                /// </exception>
448                public int ReadLEShort()
449                {
450                        int byteValue1 = stream_.ReadByte();
451
452                        if (byteValue1 < 0) {
453                                throw new EndOfStreamException();
454                        }
455
456                        int byteValue2 = stream_.ReadByte();
457                        if (byteValue2 < 0) {
458                                throw new EndOfStreamException();
459                        }
460
461                        return byteValue1 | (byteValue2 << 8);
462                }
463
464                /// <summary>
465                /// Read an int in little endian byte order.
466                /// </summary>
467                /// <returns>Returns the value read.</returns>
468                /// <exception cref="IOException">
469                /// An i/o error occurs.
470                /// </exception>
471                /// <exception cref="System.IO.EndOfStreamException">
472                /// The file ends prematurely
473                /// </exception>
474                public int ReadLEInt()
475                {
476                        return ReadLEShort() | (ReadLEShort() << 16);
477                }
478
479                /// <summary>
480                /// Read a long in little endian byte order.
481                /// </summary>
482                /// <returns>The value read.</returns>
483                public long ReadLELong()
484                {
485                        return (uint)ReadLEInt() | ((long)ReadLEInt() << 32);
486                }
487
488                /// <summary>
489                /// Write an unsigned short in little endian byte order.
490                /// </summary>
491                /// <param name="value">The value to write.</param>
492                public void WriteLEShort(int value)
493                {
494                        stream_.WriteByte(( byte )(value & 0xff));
495                        stream_.WriteByte(( byte )((value >> 8) & 0xff));
496                }
497
498                /// <summary>
499                /// Write a ushort in little endian byte order.
500                /// </summary>
501                /// <param name="value">The value to write.</param>
502                public void WriteLEUshort(ushort value)
503                {
504                        stream_.WriteByte(( byte )(value & 0xff));
505                        stream_.WriteByte(( byte )(value >> 8));
506                }
507
508                /// <summary>
509                /// Write an int in little endian byte order.
510                /// </summary>
511                /// <param name="value">The value to write.</param>
512                public void WriteLEInt(int value)
513                {
514                        WriteLEShort(value);
515                        WriteLEShort(value >> 16);
516                }
517
518                /// <summary>
519                /// Write a uint in little endian byte order.
520                /// </summary>
521                /// <param name="value">The value to write.</param>
522                public void WriteLEUint(uint value)
523                {
524                        WriteLEUshort(( ushort )(value & 0xffff));
525                        WriteLEUshort(( ushort )(value >> 16));
526                }
527
528                /// <summary>
529                /// Write a long in little endian byte order.
530                /// </summary>
531                /// <param name="value">The value to write.</param>
532                public void WriteLELong(long value)
533                {
534                        WriteLEInt(( int )value);
535                        WriteLEInt(( int )(value >> 32));
536                }
537
538                /// <summary>
539                /// Write a ulong in little endian byte order.
540                /// </summary>
541                /// <param name="value">The value to write.</param>
542                public void WriteLEUlong(ulong value)
543                {
544                        WriteLEUint(( uint )(value & 0xffffffff));
545                        WriteLEUint(( uint )(value >> 32));
546                }
547
548                #endregion
549
550                /// <summary>
551                /// Write a data descriptor.
552                /// </summary>
553                /// <param name="entry">The entry to write a descriptor for.</param>
554                /// <returns>Returns the number of descriptor bytes written.</returns>
555                public int WriteDataDescriptor(ZipEntry entry)
556                {
557                        if (entry == null) {
558                                throw new ArgumentNullException("entry");
559                        }
560
561                        int result=0;
562
563                        // Add data descriptor if flagged as required
564                        if ((entry.Flags & (int)GeneralBitFlags.Descriptor) != 0)
565                        {
566                                // The signature is not PKZIP originally but is now described as optional
567                                // in the PKZIP Appnote documenting trhe format.
568                                WriteLEInt(ZipConstants.DataDescriptorSignature);
569                                WriteLEInt(unchecked((int)(entry.Crc)));
570
571                                result+=8;
572
573                                if (entry.LocalHeaderRequiresZip64)
574                                {
575                                        WriteLELong(entry.CompressedSize);
576                                        WriteLELong(entry.Size);
577                                        result+=16;
578                                }
579                                else
580                                {
581                                        WriteLEInt((int)entry.CompressedSize);
582                                        WriteLEInt((int)entry.Size);
583                                        result+=8;
584                                }
585                        }
586
587                        return result;
588                }
589
590                /// <summary>
591                /// Read data descriptor at the end of compressed data.
592                /// </summary>
593                /// <param name="zip64">if set to <c>true</c> [zip64].</param>
594                /// <param name="data">The data to fill in.</param>
595                /// <returns>Returns the number of bytes read in the descriptor.</returns>
596                public void ReadDataDescriptor(bool zip64, DescriptorData data)
597                {
598                        int intValue = ReadLEInt();
599
600                        // In theory this may not be a descriptor according to PKZIP appnote.
601                        // In practise its always there.
602                        if (intValue != ZipConstants.DataDescriptorSignature) {
603                                throw new ZipException("Data descriptor signature not found");
604                        }
605
606                        data.Crc = ReadLEInt();
607                       
608                        if (zip64) {
609                                data.CompressedSize = ReadLELong();
610                                data.Size = ReadLELong();
611                        }
612                        else {
613                                data.CompressedSize = ReadLEInt();
614                                data.Size = ReadLEInt();
615                        }
616                }
617
618                #region Instance Fields
619                bool isOwner_;
620                Stream stream_;
621                #endregion
622        }
623}
Notatka: Zobacz TracBrowser aby uzyskać więcej informacji.