root/trunk/Updater/ICSharpCode.SharpZipLib/Zip/ZipExtraData.cs @ 597

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

re #165

Line 
1//
2// ZipExtraData.cs
3//
4// Copyright 2004-2007 John Reilly
5//
6// This program is free software; you can redistribute it and/or
7// modify it under the terms of the GNU General Public License
8// as published by the Free Software Foundation; either version 2
9// of the License, or (at your option) any later version.
10//
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with this program; if not, write to the Free Software
18// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19//
20// Linking this library statically or dynamically with other modules is
21// making a combined work based on this library.  Thus, the terms and
22// conditions of the GNU General Public License cover the whole
23// combination.
24//
25// As a special exception, the copyright holders of this library give you
26// permission to link this library with independent modules to produce an
27// executable, regardless of the license terms of these independent
28// modules, and to copy and distribute the resulting executable under
29// terms of your choice, provided that you also meet, for each linked
30// independent module, the terms and conditions of the license of that
31// module.  An independent module is a module which is not derived from
32// or based on this library.  If you modify this library, you may extend
33// this exception to your version of the library, but you are not
34// obligated to do so.  If you do not wish to do so, delete this
35// exception statement from your version.
36
37using System;
38using System.Collections;
39using System.IO;
40
41namespace ICSharpCode.SharpZipLib.Zip
42{
43        // TODO: Sort out wether tagged data is useful and what a good implementation might look like.
44        // Its just a sketch of an idea at the moment.
45       
46        /// <summary>
47        /// ExtraData tagged value interface.
48        /// </summary>
49        public interface ITaggedData
50        {
51                /// <summary>
52                /// Get the ID for this tagged data value.
53                /// </summary>
54                short TagID { get; }
55
56                /// <summary>
57                /// Set the contents of this instance from the data passed.
58                /// </summary>
59                /// <param name="data">The data to extract contents from.</param>
60                /// <param name="offset">The offset to begin extracting data from.</param>
61                /// <param name="count">The number of bytes to extract.</param>
62                void SetData(byte[] data, int offset, int count);
63
64                /// <summary>
65                /// Get the data representing this instance.
66                /// </summary>
67                /// <returns>Returns the data for this instance.</returns>
68                byte[] GetData();
69        }
70       
71        /// <summary>
72        /// A raw binary tagged value
73        /// </summary>
74        public class RawTaggedData : ITaggedData
75        {
76                /// <summary>
77                /// Initialise a new instance.
78                /// </summary>
79                /// <param name="tag">The tag ID.</param>
80                public RawTaggedData(short tag)
81                {
82                        tag_ = tag;
83                }
84
85                #region ITaggedData Members
86
87                /// <summary>
88                /// Get the ID for this tagged data value.
89                /// </summary>
90                public short TagID
91                {
92                        get { return tag_; }
93                        set { tag_ = value; }
94                }
95
96                /// <summary>
97                /// Set the data from the raw values provided.
98                /// </summary>
99                /// <param name="data">The raw data to extract values from.</param>
100                /// <param name="offset">The index to start extracting values from.</param>
101                /// <param name="count">The number of bytes available.</param>
102                public void SetData(byte[] data, int offset, int count)
103                {
104                        if( data==null )
105                        {
106                                throw new ArgumentNullException("data");
107                        }
108
109                        data_=new byte[count];
110                        Array.Copy(data, offset, data_, 0, count);
111                }
112
113                /// <summary>
114                /// Get the binary data representing this instance.
115                /// </summary>
116                /// <returns>The raw binary data representing this instance.</returns>
117                public byte[] GetData()
118                {
119                        return data_;
120                }
121
122                #endregion
123
124                /// <summary>
125                /// Get /set the binary data representing this instance.
126                /// </summary>
127                /// <returns>The raw binary data representing this instance.</returns>
128                public byte[] Data
129                {
130                        get { return data_; }
131                        set { data_=value; }
132                }
133
134                #region Instance Fields
135                /// <summary>
136                /// The tag ID for this instance.
137                /// </summary>
138                protected short tag_;
139
140                byte[] data_;
141                #endregion
142        }
143
144        /// <summary>
145        /// Class representing extended unix date time values.
146        /// </summary>
147        public class ExtendedUnixData : ITaggedData
148        {
149                /// <summary>
150                /// Flags indicate which values are included in this instance.
151                /// </summary>
152                [Flags]
153                public enum Flags : byte
154                {
155                        /// <summary>
156                        /// The modification time is included
157                        /// </summary>
158                        ModificationTime = 0x01,
159                       
160                        /// <summary>
161                        /// The access time is included
162                        /// </summary>
163                        AccessTime = 0x02,
164                       
165                        /// <summary>
166                        /// The create time is included.
167                        /// </summary>
168                        CreateTime = 0x04,
169                }
170               
171                #region ITaggedData Members
172
173                /// <summary>
174                /// Get the ID
175                /// </summary>
176                public short TagID
177                {
178                        get { return 0x5455; }
179                }
180               
181                /// <summary>
182                /// Set the data from the raw values provided.
183                /// </summary>
184                /// <param name="data">The raw data to extract values from.</param>
185                /// <param name="index">The index to start extracting values from.</param>
186                /// <param name="count">The number of bytes available.</param>
187                public void SetData(byte[] data, int index, int count)
188                {
189                        using (MemoryStream ms = new MemoryStream(data, index, count, false))
190                        using (ZipHelperStream helperStream = new ZipHelperStream(ms))
191                        {
192                                // bit 0           if set, modification time is present
193                                // bit 1           if set, access time is present
194                                // bit 2           if set, creation time is present
195                               
196                                flags_ = (Flags)helperStream.ReadByte();
197                                if (((flags_ & Flags.ModificationTime) != 0) && (count >= 5))
198                                {
199                                        int iTime = helperStream.ReadLEInt();
200
201                                        modificationTime_ = (new System.DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime() +
202                                                new TimeSpan(0, 0, 0, iTime, 0)).ToLocalTime();
203                                }
204
205                                if ((flags_ & Flags.AccessTime) != 0)
206                                {
207                                        int iTime = helperStream.ReadLEInt();
208
209                                        lastAccessTime_ = (new System.DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime() +
210                                                new TimeSpan(0, 0, 0, iTime, 0)).ToLocalTime();
211                                }
212                               
213                                if ((flags_ & Flags.CreateTime) != 0)
214                                {
215                                        int iTime = helperStream.ReadLEInt();
216
217                                        createTime_ = (new System.DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime() +
218                                                new TimeSpan(0, 0, 0, iTime, 0)).ToLocalTime();
219                                }
220                        }
221                }
222
223                /// <summary>
224                /// Get the binary data representing this instance.
225                /// </summary>
226                /// <returns>The raw binary data representing this instance.</returns>
227                public byte[] GetData()
228                {
229                        using (MemoryStream ms = new MemoryStream())
230                        using (ZipHelperStream helperStream = new ZipHelperStream(ms))
231                        {
232                                helperStream.IsStreamOwner = false;
233                                helperStream.WriteByte((byte)flags_);     // Flags
234                                if ( (flags_ & Flags.ModificationTime) != 0) {
235                                        TimeSpan span = modificationTime_.ToUniversalTime() - new System.DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime();
236                                        int seconds = (int)span.TotalSeconds;
237                                        helperStream.WriteLEInt(seconds);
238                                }
239                                if ( (flags_ & Flags.AccessTime) != 0) {
240                                        TimeSpan span = lastAccessTime_.ToUniversalTime() - new System.DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime();
241                                        int seconds = (int)span.TotalSeconds;
242                                        helperStream.WriteLEInt(seconds);
243                                }
244                                if ( (flags_ & Flags.CreateTime) != 0) {
245                                        TimeSpan span = createTime_.ToUniversalTime() - new System.DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime();
246                                        int seconds = (int)span.TotalSeconds;
247                                        helperStream.WriteLEInt(seconds);
248                                }
249                                return ms.ToArray();
250                        }
251                }
252
253                #endregion
254
255                /// <summary>
256                /// Test a <see cref="DateTime"> value to see if is valid and can be represented here.</see>
257                /// </summary>
258                /// <param name="value">The <see cref="DateTime">value</see> to test.</param>
259                /// <returns>Returns true if the value is valid and can be represented; false if not.</returns>
260                /// <remarks>The standard Unix time is a signed integer data type, directly encoding the Unix time number,
261                /// which is the number of seconds since 1970-01-01.
262                /// Being 32 bits means the values here cover a range of about 136 years.
263                /// The minimum representable time is 1901-12-13 20:45:52,
264                /// and the maximum representable time is 2038-01-19 03:14:07.
265                /// </remarks>
266                public static bool IsValidValue(DateTime value)
267                {
268                        return (( value >= new DateTime(1901, 12, 13, 20, 45, 52)) ||
269                                        ( value <= new DateTime(2038, 1, 19, 03, 14, 07) ));
270                }
271
272                /// <summary>
273                /// Get /set the Modification Time
274                /// </summary>
275                /// <exception cref="ArgumentOutOfRangeException"></exception>
276                /// <seealso cref="IsValidValue"></seealso>
277                public DateTime ModificationTime
278                {
279                        get { return modificationTime_; }
280                        set
281                        {
282                                if ( !IsValidValue(value) ) {
283                                        throw new ArgumentOutOfRangeException("value");
284                                }
285                               
286                                flags_ |= Flags.ModificationTime;
287                                modificationTime_=value;
288                        }
289                }
290
291                /// <summary>
292                /// Get / set the Access Time
293                /// </summary>
294                /// <exception cref="ArgumentOutOfRangeException"></exception>
295                /// <seealso cref="IsValidValue"></seealso>
296                public DateTime AccessTime
297                {
298                        get { return lastAccessTime_; }
299                        set {
300                                if ( !IsValidValue(value) ) {
301                                        throw new ArgumentOutOfRangeException("value");
302                                }
303                       
304                                flags_ |= Flags.AccessTime;
305                                lastAccessTime_=value;
306                        }
307                }
308
309                /// <summary>
310                /// Get / Set the Create Time
311                /// </summary>
312                /// <exception cref="ArgumentOutOfRangeException"></exception>
313                /// <seealso cref="IsValidValue"></seealso>
314                public DateTime CreateTime
315                {
316                        get { return createTime_; }
317                        set {
318                                if ( !IsValidValue(value) ) {
319                                        throw new ArgumentOutOfRangeException("value");
320                                }
321                       
322                                flags_ |= Flags.CreateTime;
323                                createTime_=value;
324                        }
325                }
326
327                /// <summary>
328                /// Get/set the <see cref="Flags">values</see> to include.
329                /// </summary>
330                Flags Include
331                {
332                        get { return flags_; }
333                        set { flags_ = value; }
334                }
335
336                #region Instance Fields
337                Flags flags_;
338                DateTime modificationTime_ = new DateTime(1970,1,1);
339                DateTime lastAccessTime_ = new DateTime(1970, 1, 1);
340                DateTime createTime_ = new DateTime(1970, 1, 1);
341                #endregion
342        }
343
344        /// <summary>
345        /// Class handling NT date time values.
346        /// </summary>
347        public class NTTaggedData : ITaggedData
348        {
349                /// <summary>
350                /// Get the ID for this tagged data value.
351                /// </summary>
352                public short TagID
353                {
354                        get { return 10; }
355                }
356
357                /// <summary>
358                /// Set the data from the raw values provided.
359                /// </summary>
360                /// <param name="data">The raw data to extract values from.</param>
361                /// <param name="index">The index to start extracting values from.</param>
362                /// <param name="count">The number of bytes available.</param>
363                public void SetData(byte[] data, int index, int count)
364                {
365                        using (MemoryStream ms = new MemoryStream(data, index, count, false))
366                        using (ZipHelperStream helperStream = new ZipHelperStream(ms))
367                        {
368                                helperStream.ReadLEInt(); // Reserved
369                                while (helperStream.Position < helperStream.Length)
370                                {
371                                        int ntfsTag = helperStream.ReadLEShort();
372                                        int ntfsLength = helperStream.ReadLEShort();
373                                        if (ntfsTag == 1)
374                                        {
375                                                if (ntfsLength >= 24)
376                                                {
377                                                        long lastModificationTicks = helperStream.ReadLELong();
378                                                        lastModificationTime_ = DateTime.FromFileTime(lastModificationTicks);
379
380                                                        long lastAccessTicks = helperStream.ReadLELong();
381                                                        lastAccessTime_ = DateTime.FromFileTime(lastAccessTicks);
382
383                                                        long createTimeTicks = helperStream.ReadLELong();
384                                                        createTime_ = DateTime.FromFileTime(createTimeTicks);
385                                                }
386                                                break;
387                                        }
388                                        else
389                                        {
390                                                // An unknown NTFS tag so simply skip it.
391                                                helperStream.Seek(ntfsLength, SeekOrigin.Current);
392                                        }
393                                }
394                        }
395                }
396
397                /// <summary>
398                /// Get the binary data representing this instance.
399                /// </summary>
400                /// <returns>The raw binary data representing this instance.</returns>
401                public byte[] GetData()
402                {
403                        using (MemoryStream ms = new MemoryStream())
404                        using (ZipHelperStream helperStream = new ZipHelperStream(ms))
405                        {
406                                helperStream.IsStreamOwner = false;
407                                helperStream.WriteLEInt(0);       // Reserved
408                                helperStream.WriteLEShort(1);     // Tag
409                                helperStream.WriteLEShort(24);    // Length = 3 x 8.
410                                helperStream.WriteLELong(lastModificationTime_.ToFileTime());
411                                helperStream.WriteLELong(lastAccessTime_.ToFileTime());
412                                helperStream.WriteLELong(createTime_.ToFileTime());
413                                return ms.ToArray();
414                        }
415                }
416
417                /// <summary>
418                /// Test a <see cref="DateTime"> valuie to see if is valid and can be represented here.</see>
419                /// </summary>
420                /// <param name="value">The <see cref="DateTime">value</see> to test.</param>
421                /// <returns>Returns true if the value is valid and can be represented; false if not.</returns>
422                /// <remarks>
423                /// NTFS filetimes are 64-bit unsigned integers, stored in Intel
424                /// (least significant byte first) byte order. They determine the
425                /// number of 1.0E-07 seconds (1/10th microseconds!) past WinNT "epoch",
426                /// which is "01-Jan-1601 00:00:00 UTC". 28 May 60056 is the upper limit
427                /// </remarks>
428                public static bool IsValidValue(DateTime value)
429                {
430                        bool result = true;
431                        try
432                        {
433                                value.ToFileTimeUtc();
434                        }
435                        catch
436                        {
437                                result = false;
438                        }
439                        return result;
440                }
441               
442                /// <summary>
443                /// Get/set the <see cref="DateTime">last modification time</see>.
444                /// </summary>
445                public DateTime LastModificationTime
446                {
447                        get { return lastModificationTime_; }
448                        set {
449                                if (! IsValidValue(value))
450                                {
451                                        throw new ArgumentOutOfRangeException("value");
452                                }
453                                lastModificationTime_ = value;
454                        }
455                }
456
457                /// <summary>
458                /// Get /set the <see cref="DateTime">create time</see>
459                /// </summary>
460                public DateTime CreateTime
461                {
462                        get { return createTime_; }
463                        set {
464                                if ( !IsValidValue(value)) {
465                                        throw new ArgumentOutOfRangeException("value");
466                                }
467                                createTime_ = value;
468                        }
469                }
470
471                /// <summary>
472                /// Get /set the <see cref="DateTime">last access time</see>.
473                /// </summary>
474                public DateTime LastAccessTime
475                {
476                        get { return lastAccessTime_; }
477                        set {
478                                if (!IsValidValue(value)) {
479                                        throw new ArgumentOutOfRangeException("value");
480                                }
481                                lastAccessTime_ = value;
482                        }
483                }
484
485                #region Instance Fields
486                DateTime lastAccessTime_ = DateTime.FromFileTime(0);
487                DateTime lastModificationTime_ = DateTime.FromFileTime(0);
488                DateTime createTime_ = DateTime.FromFileTime(0);
489                #endregion
490        }
491
492        /// <summary>
493        /// A factory that creates <see cref="ITaggedData">tagged data</see> instances.
494        /// </summary>
495        interface ITaggedDataFactory
496        {
497                /// <summary>
498                /// Get data for a specific tag value.
499                /// </summary>
500                /// <param name="tag">The tag ID to find.</param>
501                /// <param name="data">The data to search.</param>
502                /// <param name="offset">The offset to begin extracting data from.</param>
503                /// <param name="count">The number of bytes to extract.</param>
504                /// <returns>The located <see cref="ITaggedData">value found</see>, or null if not found.</returns>
505                ITaggedData Create(short tag, byte[] data, int offset, int count);
506        }
507
508        ///
509        /// <summary>
510        /// A class to handle the extra data field for Zip entries
511        /// </summary>
512        /// <remarks>
513        /// Extra data contains 0 or more values each prefixed by a header tag and length.
514        /// They contain zero or more bytes of actual data.
515        /// The data is held internally using a copy on write strategy.  This is more efficient but
516        /// means that for extra data created by passing in data can have the values modified by the caller
517        /// in some circumstances.
518        /// </remarks>
519        sealed public class ZipExtraData : IDisposable
520        {
521                #region Constructors
522                /// <summary>
523                /// Initialise a default instance.
524                /// </summary>
525                public ZipExtraData()
526                {
527                        Clear();
528                }
529
530                /// <summary>
531                /// Initialise with known extra data.
532                /// </summary>
533                /// <param name="data">The extra data.</param>
534                public ZipExtraData(byte[] data)
535                {
536                        if ( data == null )
537                        {
538                                data_ = new byte[0];
539                        }
540                        else
541                        {
542                                data_ = data;
543                        }
544                }
545                #endregion
546
547                /// <summary>
548                /// Get the raw extra data value
549                /// </summary>
550                /// <returns>Returns the raw byte[] extra data this instance represents.</returns>
551                public byte[] GetEntryData()
552                {
553                        if ( Length > ushort.MaxValue ) {
554                                throw new ZipException("Data exceeds maximum length");
555                        }
556
557                        return (byte[])data_.Clone();
558                }
559
560                /// <summary>
561                /// Clear the stored data.
562                /// </summary>
563                public void Clear()
564                {
565                        if ( (data_ == null) || (data_.Length != 0) ) {
566                                data_ = new byte[0];
567                        }
568                }
569
570                /// <summary>
571                /// Gets the current extra data length.
572                /// </summary>
573                public int Length
574                {
575                        get { return data_.Length; }
576                }
577
578                /// <summary>
579                /// Get a read-only <see cref="Stream"/> for the associated tag.
580                /// </summary>
581                /// <param name="tag">The tag to locate data for.</param>
582                /// <returns>Returns a <see cref="Stream"/> containing tag data or null if no tag was found.</returns>
583                public Stream GetStreamForTag(int tag)
584                {
585                        Stream result = null;
586                        if ( Find(tag) ) {
587                                result = new MemoryStream(data_, index_, readValueLength_, false);
588                        }
589                        return result;
590                }
591
592                /// <summary>
593                /// Get the <see cref="ITaggedData">tagged data</see> for a tag.
594                /// </summary>
595                /// <param name="tag">The tag to search for.</param>
596                /// <returns>Returns a <see cref="ITaggedData">tagged value</see> or null if none found.</returns>
597                private ITaggedData GetData(short tag)
598                {
599                        ITaggedData result = null;
600                        if (Find(tag))
601                        {
602                                result = Create(tag, data_, readValueStart_, readValueLength_);
603                        }
604                        return result;
605                }
606
607                static ITaggedData Create(short tag, byte[] data, int offset, int count)
608                {
609                        ITaggedData result = null;
610                        switch ( tag )
611                        {
612                                case 0x000A:
613                                        result = new NTTaggedData();
614                                        break;
615                                case 0x5455:
616                                        result = new ExtendedUnixData();
617                                        break;
618                                default:
619                                        result = new RawTaggedData(tag);
620                                        break;
621                        }
622                        result.SetData(data, offset, count);
623                        return result;
624                }
625               
626                /// <summary>
627                /// Get the length of the last value found by <see cref="Find"/>
628                /// </summary>
629                /// <remarks>This is only valid if <see cref="Find"/> has previously returned true.</remarks>
630                public int ValueLength
631                {
632                        get { return readValueLength_; }
633                }
634
635                /// <summary>
636                /// Get the index for the current read value.
637                /// </summary>
638                /// <remarks>This is only valid if <see cref="Find"/> has previously returned true.
639                /// Initially the result will be the index of the first byte of actual data.  The value is updated after calls to
640                /// <see cref="ReadInt"/>, <see cref="ReadShort"/> and <see cref="ReadLong"/>. </remarks>
641                public int CurrentReadIndex
642                {
643                        get { return index_; }
644                }
645
646                /// <summary>
647                /// Get the number of bytes remaining to be read for the current value;
648                /// </summary>
649                public int UnreadCount
650                {
651                        get
652                        {
653                                if ((readValueStart_ > data_.Length) ||
654                                        (readValueStart_ < 4) ) {
655                                        throw new ZipException("Find must be called before calling a Read method");
656                                }
657
658                                return readValueStart_ + readValueLength_ - index_;
659                        }
660                }
661
662                /// <summary>
663                /// Find an extra data value
664                /// </summary>
665                /// <param name="headerID">The identifier for the value to find.</param>
666                /// <returns>Returns true if the value was found; false otherwise.</returns>
667                public bool Find(int headerID)
668                {
669                        readValueStart_ = data_.Length;
670                        readValueLength_ = 0;
671                        index_ = 0;
672
673                        int localLength = readValueStart_;
674                        int localTag = headerID - 1;
675
676                        // Trailing bytes that cant make up an entry (as there arent enough
677                        // bytes for a tag and length) are ignored!
678                        while ( (localTag != headerID) && (index_ < data_.Length - 3) ) {
679                                localTag = ReadShortInternal();
680                                localLength = ReadShortInternal();
681                                if ( localTag != headerID ) {
682                                        index_ += localLength;
683                                }
684                        }
685
686                        bool result = (localTag == headerID) && ((index_ + localLength) <= data_.Length);
687
688                        if ( result ) {
689                                readValueStart_ = index_;
690                                readValueLength_ = localLength;
691                        }
692
693                        return result;
694                }
695
696                /// <summary>
697                /// Add a new entry to extra data.
698                /// </summary>
699                /// <param name="taggedData">The <see cref="ITaggedData"/> value to add.</param>
700                public void AddEntry(ITaggedData taggedData)
701                {
702                        if (taggedData == null)
703                        {
704                                throw new ArgumentNullException("taggedData");
705                        }
706                        AddEntry(taggedData.TagID, taggedData.GetData());
707                }
708
709                /// <summary>
710                /// Add a new entry to extra data
711                /// </summary>
712                /// <param name="headerID">The ID for this entry.</param>
713                /// <param name="fieldData">The data to add.</param>
714                /// <remarks>If the ID already exists its contents are replaced.</remarks>
715                public void AddEntry(int headerID, byte[] fieldData)
716                {
717                        if ( (headerID > ushort.MaxValue) || (headerID < 0)) {
718                                throw new ArgumentOutOfRangeException("headerID");
719                        }
720
721                        int addLength = (fieldData == null) ? 0 : fieldData.Length;
722
723                        if ( addLength > ushort.MaxValue ) {
724#if NETCF_1_0
725                                throw new ArgumentOutOfRangeException("fieldData");
726#else
727                                throw new ArgumentOutOfRangeException("fieldData", "exceeds maximum length");
728#endif
729                        }
730
731                        // Test for new length before adjusting data.
732                        int newLength = data_.Length + addLength + 4;
733
734                        if ( Find(headerID) )
735                        {
736                                newLength -= (ValueLength + 4);
737                        }
738
739                        if ( newLength > ushort.MaxValue ) {
740                                throw new ZipException("Data exceeds maximum length");
741                        }
742                       
743                        Delete(headerID);
744
745                        byte[] newData = new byte[newLength];
746                        data_.CopyTo(newData, 0);
747                        int index = data_.Length;
748                        data_ = newData;
749                        SetShort(ref index, headerID);
750                        SetShort(ref index, addLength);
751                        if ( fieldData != null ) {
752                                fieldData.CopyTo(newData, index);
753                        }
754                }
755
756                /// <summary>
757                /// Start adding a new entry.
758                /// </summary>
759                /// <remarks>Add data using <see cref="AddData(byte[])"/>, <see cref="AddLeShort"/>, <see cref="AddLeInt"/>, or <see cref="AddLeLong"/>.
760                /// The new entry is completed and actually added by calling <see cref="AddNewEntry"/></remarks>
761                /// <seealso cref="AddEntry(ITaggedData)"/>
762                public void StartNewEntry()
763                {
764                        newEntry_ = new MemoryStream();
765                }
766
767                /// <summary>
768                /// Add entry data added since <see cref="StartNewEntry"/> using the ID passed.
769                /// </summary>
770                /// <param name="headerID">The identifier to use for this entry.</param>
771                public void AddNewEntry(int headerID)
772                {
773                        byte[] newData = newEntry_.ToArray();
774                        newEntry_ = null;
775                        AddEntry(headerID, newData);
776                }
777
778                /// <summary>
779                /// Add a byte of data to the pending new entry.
780                /// </summary>
781                /// <param name="data">The byte to add.</param>
782                /// <seealso cref="StartNewEntry"/>
783                public void AddData(byte data)
784                {
785                        newEntry_.WriteByte(data);
786                }
787
788                /// <summary>
789                /// Add data to a pending new entry.
790                /// </summary>
791                /// <param name="data">The data to add.</param>
792                /// <seealso cref="StartNewEntry"/>
793                public void AddData(byte[] data)
794                {
795                        if ( data == null ) {
796                                throw new ArgumentNullException("data");
797                        }
798
799                        newEntry_.Write(data, 0, data.Length);
800                }
801
802                /// <summary>
803                /// Add a short value in little endian order to the pending new entry.
804                /// </summary>
805                /// <param name="toAdd">The data to add.</param>
806                /// <seealso cref="StartNewEntry"/>
807                public void AddLeShort(int toAdd)
808                {
809                        unchecked {
810                                newEntry_.WriteByte(( byte )toAdd);
811                                newEntry_.WriteByte(( byte )(toAdd >> 8));
812                        }
813                }
814
815                /// <summary>
816                /// Add an integer value in little endian order to the pending new entry.
817                /// </summary>
818                /// <param name="toAdd">The data to add.</param>
819                /// <seealso cref="StartNewEntry"/>
820                public void AddLeInt(int toAdd)
821                {
822                        unchecked {
823                                AddLeShort(( short )toAdd);
824                                AddLeShort(( short )(toAdd >> 16));
825                        }
826                }
827
828                /// <summary>
829                /// Add a long value in little endian order to the pending new entry.
830                /// </summary>
831                /// <param name="toAdd">The data to add.</param>
832                /// <seealso cref="StartNewEntry"/>
833                public void AddLeLong(long toAdd)
834                {
835                        unchecked {
836                                AddLeInt(( int )(toAdd & 0xffffffff));
837                                AddLeInt(( int )(toAdd >> 32));
838                        }
839                }
840
841                /// <summary>
842                /// Delete an extra data field.
843                /// </summary>
844                /// <param name="headerID">The identifier of the field to delete.</param>
845                /// <returns>Returns true if the field was found and deleted.</returns>
846                public bool Delete(int headerID)
847                {
848                        bool result = false;
849
850                        if ( Find(headerID) ) {
851                                result = true;
852                                int trueStart = readValueStart_ - 4;
853
854                                byte[] newData = new byte[data_.Length - (ValueLength + 4)];
855                                Array.Copy(data_, 0, newData, 0, trueStart);
856
857                                int trueEnd = trueStart + ValueLength + 4;
858                                Array.Copy(data_, trueEnd, newData, trueStart, data_.Length - trueEnd);
859                                data_ = newData;
860                        }
861                        return result;
862                }
863
864                #region Reading Support
865                /// <summary>
866                /// Read a long in little endian form from the last <see cref="Find">found</see> data value
867                /// </summary>
868                /// <returns>Returns the long value read.</returns>
869                public long ReadLong()
870                {
871                        ReadCheck(8);
872                        return (ReadInt() & 0xffffffff) | ((( long )ReadInt()) << 32);
873                }
874
875                /// <summary>
876                /// Read an integer in little endian form from the last <see cref="Find">found</see> data value.
877                /// </summary>
878                /// <returns>Returns the integer read.</returns>
879                public int ReadInt()
880                {
881                        ReadCheck(4);
882
883                        int result = data_[index_] + (data_[index_ + 1] << 8) +
884                                (data_[index_ + 2] << 16) + (data_[index_ + 3] << 24);
885                        index_ += 4;
886                        return result;
887                }
888
889                /// <summary>
890                /// Read a short value in little endian form from the last <see cref="Find">found</see> data value.
891                /// </summary>
892                /// <returns>Returns the short value read.</returns>
893                public int ReadShort()
894                {
895                        ReadCheck(2);
896                        int result = data_[index_] + (data_[index_ + 1] << 8);
897                        index_ += 2;
898                        return result;
899                }
900
901                /// <summary>
902                /// Read a byte from an extra data
903                /// </summary>
904                /// <returns>The byte value read or -1 if the end of data has been reached.</returns>
905                public int ReadByte()
906                {
907                        int result = -1;
908                        if ( (index_ < data_.Length) && (readValueStart_ + readValueLength_ > index_) ) {
909                                result = data_[index_];
910                                index_ += 1;
911                        }
912                        return result;
913                }
914
915                /// <summary>
916                /// Skip data during reading.
917                /// </summary>
918                /// <param name="amount">The number of bytes to skip.</param>
919                public void Skip(int amount)
920                {
921                        ReadCheck(amount);
922                        index_ += amount;
923                }
924
925                void ReadCheck(int length)
926                {
927                        if ((readValueStart_ > data_.Length) ||
928                                (readValueStart_ < 4) ) {
929                                throw new ZipException("Find must be called before calling a Read method");
930                        }
931
932                        if (index_ > readValueStart_ + readValueLength_ - length ) {
933                                throw new ZipException("End of extra data");
934                        }
935                }
936
937                /// <summary>
938                /// Internal form of <see cref="ReadShort"/> that reads data at any location.
939                /// </summary>
940                /// <returns>Returns the short value read.</returns>
941                int ReadShortInternal()
942                {
943                        if ( index_ > data_.Length - 2) {
944                                throw new ZipException("End of extra data");
945                        }
946
947                        int result = data_[index_] + (data_[index_ + 1] << 8);
948                        index_ += 2;
949                        return result;
950                }
951
952                void SetShort(ref int index, int source)
953                {
954                        data_[index] = (byte)source;
955                        data_[index + 1] = (byte)(source >> 8);
956                        index += 2;
957                }
958
959                #endregion
960
961                #region IDisposable Members
962
963                /// <summary>
964                /// Dispose of this instance.
965                /// </summary>
966                public void Dispose()
967                {
968                        if ( newEntry_ != null ) {
969                                newEntry_.Close();
970                        }
971                }
972
973                #endregion
974
975                #region Instance Fields
976                int index_;
977                int readValueStart_;
978                int readValueLength_;
979
980                MemoryStream newEntry_;
981                byte[] data_;
982                #endregion
983        }
984}
Notatka: Zobacz TracBrowser aby uzyskać więcej informacji.