Example #1
0
		int WriteCentralDirectoryHeader( BlubbZipEntry entry ) {
			if( entry.CompressedSize < 0 ) {
				throw new BlubbZipException( "Attempt to write central directory entry with unknown csize" );
			}

			if( entry.Size < 0 ) {
				throw new BlubbZipException( "Attempt to write central directory entry with unknown size" );
			}

			if( entry.Crc < 0 ) {
				throw new BlubbZipException( "Attempt to write central directory entry with unknown crc" );
			}

			// Write the central file header
			WriteLEInt( BlubbZipConstants.CentralHeaderSignature );

			// Version made by
			WriteLEShort( BlubbZipConstants.VersionMadeBy );

			// Version required to extract
			WriteLEShort( entry.Version );

			WriteLEShort( entry.Flags );

			unchecked {
				WriteLEShort( (byte)entry.CompressionMethod );
				WriteLEInt( (int)entry.DosTime );
				WriteLEInt( (int)entry.Crc );
			}

			if( ( entry.IsBlubb64Forced() ) || ( entry.CompressedSize >= 0xffffffff ) ) {
				WriteLEInt( -1 );
			} else {
				WriteLEInt( (int)( entry.CompressedSize & 0xffffffff ) );
			}

			if( ( entry.IsBlubb64Forced() ) || ( entry.Size >= 0xffffffff ) ) {
				WriteLEInt( -1 );
			} else {
				WriteLEInt( (int)entry.Size );
			}

			byte[] name = BlubbZipConstants.ConvertToArray( entry.Flags, entry.Name );

			if( name.Length > 0xFFFF ) {
				throw new BlubbZipException( "Entry name is too long." );
			}

			WriteLEShort( name.Length );

			// Central header extra data is different to local header version so regenerate.
			BlubbZipExtraData ed = new BlubbZipExtraData( entry.ExtraData );

			if( entry.CentralHeaderRequiresBlubb64 ) {
				ed.StartNewEntry();

				if( ( entry.Size >= 0xffffffff ) || ( useBlubb64_ == UseBlubb64.On ) ) {
					ed.AddLeLong( entry.Size );
				}

				if( ( entry.CompressedSize >= 0xffffffff ) || ( useBlubb64_ == UseBlubb64.On ) ) {
					ed.AddLeLong( entry.CompressedSize );
				}

				if( entry.Offset >= 0xffffffff ) {
					ed.AddLeLong( entry.Offset );
				}

				// Number of disk on which this file starts isnt supported and is never written here.
				ed.AddNewEntry( 1 );
			} else {
				// Should have already be done when local header was added.
				ed.Delete( 1 );
			}

			byte[] centralExtraData = ed.GetEntryData();

			WriteLEShort( centralExtraData.Length );
			WriteLEShort( entry.Comment != null ? entry.Comment.Length : 0 );

			WriteLEShort( 0 );	// disk number
			WriteLEShort( 0 );	// internal file attributes

			// External file attributes...
			if( entry.ExternalFileAttributes != -1 ) {
				WriteLEInt( entry.ExternalFileAttributes );
			} else {
				if( entry.IsDirectory ) {
					WriteLEUint( 16 );
				} else {
					WriteLEUint( 0 );
				}
			}

			if( entry.Offset >= 0xffffffff ) {
				WriteLEUint( 0xffffffff );
			} else {
				WriteLEUint( (uint)(int)entry.Offset );
			}

			if( name.Length > 0 ) {
				baseStream_.Write( name, 0, name.Length );
			}

			if( centralExtraData.Length > 0 ) {
				baseStream_.Write( centralExtraData, 0, centralExtraData.Length );
			}

			byte[] rawComment = ( entry.Comment != null ) ? Encoding.ASCII.GetBytes( entry.Comment ) : new byte[ 0 ];

			if( rawComment.Length > 0 ) {
				baseStream_.Write( rawComment, 0, rawComment.Length );
			}

			return BlubbZipConstants.CentralHeaderBaseSize + name.Length + centralExtraData.Length + rawComment.Length;
		}
Example #2
0
		/// <summary>
		/// Test a local header against that provided from the central directory
		/// </summary>
		/// <param name="entry">
		/// The entry to test against
		/// </param>
		/// <param name="tests">The type of <see cref="HeaderTest">tests</see> to carry out.</param>
		/// <returns>The offset of the entries data in the file</returns>
		long TestLocalHeader( BlubbZipEntry entry, HeaderTest tests ) {
			lock( baseStream_ ) {
				bool testHeader = ( tests & HeaderTest.Header ) != 0;
				bool testData = ( tests & HeaderTest.Extract ) != 0;

				baseStream_.Seek( offsetOfFirstEntry + entry.Offset, SeekOrigin.Begin );
				if( (int)ReadLEUint() != BlubbZipConstants.LocalHeaderSignature ) {
					throw new BlubbZipException( string.Format( "Wrong local header signature @{0:X}", offsetOfFirstEntry + entry.Offset ) );
				}

				short extractVersion = (short)ReadLEUshort();
				short localFlags = (short)ReadLEUshort();
				short compressionMethod = (short)ReadLEUshort();
				short fileTime = (short)ReadLEUshort();
				short fileDate = (short)ReadLEUshort();
				uint crcValue = ReadLEUint();
				long compressedSize = ReadLEUint();
				long size = ReadLEUint();
				int storedNameLength = ReadLEUshort();
				int extraDataLength = ReadLEUshort();

				byte[] nameData = new byte[ storedNameLength ];
				StreamUtils.ReadFully( baseStream_, nameData );

				byte[] extraData = new byte[ extraDataLength ];
				StreamUtils.ReadFully( baseStream_, extraData );

				BlubbZipExtraData localExtraData = new BlubbZipExtraData( extraData );

				// Extra data / blubb64 checks
				if( localExtraData.Find( 1 ) ) {
					// TODO Check for tag values being distinct..  Multiple blubb64 tags means what?

					// Blubb64 extra data but 'extract version' is too low
					if( extractVersion < BlubbZipConstants.VersionBlubb64 ) {
						throw new BlubbZipException(
							string.Format( "Extra data contains Blubb64 information but version {0}.{1} is not high enough",
							extractVersion / 10, extractVersion % 10 ) );
					}

					// Blubb64 extra data but size fields dont indicate its required.
					if( ( (uint)size != uint.MaxValue ) && ( (uint)compressedSize != uint.MaxValue ) ) {
						throw new BlubbZipException( "Entry sizes not correct for Blubb64" );
					}

					size = localExtraData.ReadLong();
					compressedSize = localExtraData.ReadLong();

					if( ( localFlags & (int)GeneralBitFlags.Descriptor ) != 0 ) {
						// These may be valid if patched later
						if( ( size != -1 ) && ( size != entry.Size ) ) {
							throw new BlubbZipException( "Size invalid for descriptor" );
						}

						if( ( compressedSize != -1 ) && ( compressedSize != entry.CompressedSize ) ) {
							throw new BlubbZipException( "Compressed size invalid for descriptor" );
						}
					}
				} else {
					// No blubb64 extra data but entry requires it.
					if( ( extractVersion >= BlubbZipConstants.VersionBlubb64 ) &&
						( ( (uint)size == uint.MaxValue ) || ( (uint)compressedSize == uint.MaxValue ) ) ) {
						throw new BlubbZipException( "Required Blubb64 extended information missing" );
					}
				}

				if( testData ) {
					if( entry.IsFile ) {
						if( !entry.IsCompressionMethodSupported() ) {
							throw new BlubbZipException( "Compression method not supported" );
						}

						if( ( extractVersion > BlubbZipConstants.VersionMadeBy )
							|| ( ( extractVersion > 20 ) && ( extractVersion < BlubbZipConstants.VersionBlubb64 ) ) ) {
							throw new BlubbZipException( string.Format( "Version required to extract this entry not supported ({0})", extractVersion ) );
						}

						if( ( localFlags & (int)( GeneralBitFlags.Patched | GeneralBitFlags.StrongEncryption | GeneralBitFlags.EnhancedCompress | GeneralBitFlags.HeaderMasked ) ) != 0 ) {
							throw new BlubbZipException( "The library does not support the blubb version required to extract this entry" );
						}
					}
				}

				if( testHeader ) {
					if( ( extractVersion <= 63 ) &&
						( extractVersion != 10 ) &&
						( extractVersion != 11 ) &&
						( extractVersion != 20 ) &&
						( extractVersion != 21 ) &&
						( extractVersion != 25 ) &&
						( extractVersion != 27 ) &&
						( extractVersion != 45 ) &&
						( extractVersion != 46 ) &&
						( extractVersion != 50 ) &&
						( extractVersion != 51 ) &&
						( extractVersion != 52 ) &&
						( extractVersion != 61 ) &&
						( extractVersion != 62 ) &&
						( extractVersion != 63 )
						) {
						throw new BlubbZipException( string.Format( "Version required to extract this entry is invalid ({0})", extractVersion ) );
					}

					// Local entry flags dont have reserved bit set on.
					if( ( localFlags & (int)( GeneralBitFlags.ReservedPKware4 | GeneralBitFlags.ReservedPkware14 | GeneralBitFlags.ReservedPkware15 ) ) != 0 ) {
						throw new BlubbZipException( "Reserved bit flags cannot be set." );
					}

					// Encryption requires extract version >= 20
					if( ( ( localFlags & (int)GeneralBitFlags.Encrypted ) != 0 ) && ( extractVersion < 20 ) ) {
						throw new BlubbZipException( string.Format( "Version required to extract this entry is too low for encryption ({0})", extractVersion ) );
					}

					// Strong encryption requires encryption flag to be set and extract version >= 50.
					if( ( localFlags & (int)GeneralBitFlags.StrongEncryption ) != 0 ) {
						if( ( localFlags & (int)GeneralBitFlags.Encrypted ) == 0 ) {
							throw new BlubbZipException( "Strong encryption flag set but encryption flag is not set" );
						}

						if( extractVersion < 50 ) {
							throw new BlubbZipException( string.Format( "Version required to extract this entry is too low for encryption ({0})", extractVersion ) );
						}
					}

					// Patched entries require extract version >= 27
					if( ( ( localFlags & (int)GeneralBitFlags.Patched ) != 0 ) && ( extractVersion < 27 ) ) {
						throw new BlubbZipException( string.Format( "Patched data requires higher version than ({0})", extractVersion ) );
					}

					// Central header flags match local entry flags.
					if( localFlags != entry.Flags ) {
						throw new BlubbZipException( "Central header/local header flags mismatch" );
					}

					// Central header compression method matches local entry
					if( entry.CompressionMethod != (CompressionMethod)compressionMethod ) {
						throw new BlubbZipException( "Central header/local header compression method mismatch" );
					}

					if( entry.Version != extractVersion ) {
						throw new BlubbZipException( "Extract version mismatch" );
					}

					// Strong encryption and extract version match
					if( ( localFlags & (int)GeneralBitFlags.StrongEncryption ) != 0 ) {
						if( extractVersion < 62 ) {
							throw new BlubbZipException( "Strong encryption flag set but version not high enough" );
						}
					}

					if( ( localFlags & (int)GeneralBitFlags.HeaderMasked ) != 0 ) {
						if( ( fileTime != 0 ) || ( fileDate != 0 ) ) {
							throw new BlubbZipException( "Header masked set but date/time values non-zero" );
						}
					}

					if( ( localFlags & (int)GeneralBitFlags.Descriptor ) == 0 ) {
						if( crcValue != (uint)entry.Crc ) {
							throw new BlubbZipException( "Central header/local header crc mismatch" );
						}
					}

					// Crc valid for empty entry.
					// This will also apply to streamed entries where size isnt known and the header cant be patched
					if( ( size == 0 ) && ( compressedSize == 0 ) ) {
						if( crcValue != 0 ) {
							throw new BlubbZipException( "Invalid CRC for empty entry" );
						}
					}

					// TODO: make test more correct...  can't compare lengths as was done originally as this can fail for MBCS strings
					// Assuming a code page at this point is not valid?  Best is to store the name length in the BlubbEntry probably
					if( entry.Name.Length > storedNameLength ) {
						throw new BlubbZipException( "File name length mismatch" );
					}

					// Name data has already been read convert it and compare.
					string localName = BlubbZipConstants.ConvertToStringExt( localFlags, nameData );

					// Central directory and local entry name match
					if( localName != entry.Name ) {
						throw new BlubbZipException( "Central header and local header file name mismatch" );
					}

					// Directories have zero actual size but can have compressed size
					if( entry.IsDirectory ) {
						if( size > 0 ) {
							throw new BlubbZipException( "Directory cannot have size" );
						}

						// There may be other cases where the compressed size can be greater than this?
						// If so until details are known we will be strict.
						if( entry.IsCrypted ) {
							if( compressedSize > BlubbZipConstants.CryptoHeaderSize + 2 ) {
								throw new BlubbZipException( "Directory compressed size invalid" );
							}
						} else if( compressedSize > 2 ) {
							// When not compressed the directory size can validly be 2 bytes
							// if the true size wasnt known when data was originally being written.
							// NOTE: Versions of the library 0.85.4 and earlier always added 2 bytes
							throw new BlubbZipException( "Directory compressed size invalid" );
						}
					}

					if( !BlubbZipNameTransform.IsValidName( localName, true ) ) {
						throw new BlubbZipException( "Name is invalid" );
					}
				}

				// Tests that apply to both data and header.

				// Size can be verified only if it is known in the local header.
				// it will always be known in the central header.
				if( ( ( localFlags & (int)GeneralBitFlags.Descriptor ) == 0 ) ||
					( ( size > 0 ) || ( compressedSize > 0 ) ) ) {

					if( size != entry.Size ) {
						throw new BlubbZipException(
							string.Format( "Size mismatch between central header({0}) and local header({1})",
								entry.Size, size ) );
					}

					if( compressedSize != entry.CompressedSize ) {
						throw new BlubbZipException(
							string.Format( "Compressed size mismatch between central header({0}) and local header({1})",
							entry.CompressedSize, compressedSize ) );
					}
				}

				int extraLength = storedNameLength + extraDataLength;
				return offsetOfFirstEntry + entry.Offset + BlubbZipConstants.LocalHeaderBaseSize + extraLength;
			}
		}
        /// <summary>
        /// Finishes the stream.  This will write the central directory at the
        /// end of the blubb file and flush the stream.
        /// </summary>
        /// <remarks>
        /// This is automatically called when the stream is closed.
        /// </remarks>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurs.
        /// </exception>
        /// <exception cref="BlubbException">
        /// Comment exceeds the maximum length<br/>
        /// Entry name exceeds the maximum length
        /// </exception>
        public override void Finish()
        {
            if (entries == null)
            {
                return;
            }

            if (curEntry != null)
            {
                CloseEntry();
            }

            long numEntries  = entries.Count;
            long sizeEntries = 0;

            foreach (BlubbZipEntry entry in entries)
            {
                WriteLeInt(BlubbZipConstants.CentralHeaderSignature);
                WriteLeShort(BlubbZipConstants.VersionMadeBy);
                WriteLeShort(entry.Version);
                WriteLeShort(entry.Flags);
                WriteLeShort((short)entry.CompressionMethod);
                WriteLeInt((int)entry.DosTime);
                WriteLeInt((int)entry.Crc);

                if (entry.IsBlubb64Forced() ||
                    (entry.CompressedSize >= uint.MaxValue))
                {
                    WriteLeInt(-1);
                }
                else
                {
                    WriteLeInt((int)entry.CompressedSize);
                }

                if (entry.IsBlubb64Forced() ||
                    (entry.Size >= uint.MaxValue))
                {
                    WriteLeInt(-1);
                }
                else
                {
                    WriteLeInt((int)entry.Size);
                }

                byte[] name = BlubbZipConstants.ConvertToArray(entry.Flags, entry.Name);

                if (name.Length > 0xffff)
                {
                    throw new BlubbZipException("Name too long.");
                }

                BlubbZipExtraData ed = new BlubbZipExtraData(entry.ExtraData);

                if (entry.CentralHeaderRequiresBlubb64)
                {
                    ed.StartNewEntry();
                    if (entry.IsBlubb64Forced() ||
                        (entry.Size >= 0xffffffff))
                    {
                        ed.AddLeLong(entry.Size);
                    }

                    if (entry.IsBlubb64Forced() ||
                        (entry.CompressedSize >= 0xffffffff))
                    {
                        ed.AddLeLong(entry.CompressedSize);
                    }

                    if (entry.Offset >= 0xffffffff)
                    {
                        ed.AddLeLong(entry.Offset);
                    }

                    ed.AddNewEntry(1);
                }
                else
                {
                    ed.Delete(1);
                }

                byte[] extra = ed.GetEntryData();

                byte[] entryComment =
                    (entry.Comment != null) ?
                    BlubbZipConstants.ConvertToArray(entry.Flags, entry.Comment) :
                    new byte[0];

                if (entryComment.Length > 0xffff)
                {
                    throw new BlubbZipException("Comment too long.");
                }

                WriteLeShort(name.Length);
                WriteLeShort(extra.Length);
                WriteLeShort(entryComment.Length);
                WriteLeShort(0);                        // disk number
                WriteLeShort(0);                        // internal file attributes
                // external file attributes

                if (entry.ExternalFileAttributes != -1)
                {
                    WriteLeInt(entry.ExternalFileAttributes);
                }
                else
                {
                    if (entry.IsDirectory)
                    {
                        WriteLeInt(16);
                    }
                    else
                    {
                        WriteLeInt(0);
                    }
                }

                if (entry.Offset >= uint.MaxValue)
                {
                    WriteLeInt(-1);
                }
                else
                {
                    WriteLeInt((int)entry.Offset);
                }

                if (name.Length > 0)
                {
                    baseOutputStream_.Write(name, 0, name.Length);
                }

                if (extra.Length > 0)
                {
                    baseOutputStream_.Write(extra, 0, extra.Length);
                }

                if (entryComment.Length > 0)
                {
                    baseOutputStream_.Write(entryComment, 0, entryComment.Length);
                }

                sizeEntries += BlubbZipConstants.CentralHeaderBaseSize + name.Length + extra.Length + entryComment.Length;
            }

            using (BlubbZipHelperStream zhs = new BlubbZipHelperStream(baseOutputStream_)) {
                zhs.WriteEndOfCentralDirectory(numEntries, sizeEntries, offset, blubbComment);
            }

            entries = null;
        }
Example #4
0
		void WriteLocalEntryHeader( BlubbUpdate update ) {
			BlubbZipEntry entry = update.OutEntry;

			// TODO: Local offset will require adjusting for multi-disk blubb files.
			entry.Offset = baseStream_.Position;

			// TODO: Need to clear any entry flags that dont make sense or throw an exception here.
			if( update.Command != UpdateCommand.Copy ) {
				if( entry.CompressionMethod == CompressionMethod.Deflated ) {
					if( entry.Size == 0 ) {
						// No need to compress - no data.
						entry.CompressedSize = entry.Size;
						entry.Crc = 0;
						entry.CompressionMethod = CompressionMethod.Stored;
					}
				} else if( entry.CompressionMethod == CompressionMethod.Stored ) {
					entry.Flags &= ~(int)GeneralBitFlags.Descriptor;
				}

				if( HaveKeys ) {
					entry.IsCrypted = true;
					if( entry.Crc < 0 ) {
						entry.Flags |= (int)GeneralBitFlags.Descriptor;
					}
				} else {
					entry.IsCrypted = false;
				}

				switch( useBlubb64_ ) {
					case UseBlubb64.Dynamic:
						if( entry.Size < 0 ) {
							entry.ForceBlubb64();
						}
						break;

					case UseBlubb64.On:
						entry.ForceBlubb64();
						break;

					case UseBlubb64.Off:
						// Do nothing.  The entry itself may be using Blubb64 independantly.
						break;
				}
			}

			// Write the local file header
			WriteLEInt( BlubbZipConstants.LocalHeaderSignature );

			WriteLEShort( entry.Version );
			WriteLEShort( entry.Flags );

			WriteLEShort( (byte)entry.CompressionMethod );
			WriteLEInt( (int)entry.DosTime );

			if( !entry.HasCrc ) {
				// Note patch address for updating CRC later.
				update.CrcPatchOffset = baseStream_.Position;
				WriteLEInt( (int)0 );
			} else {
				WriteLEInt( unchecked( (int)entry.Crc ) );
			}

			if( entry.LocalHeaderRequiresBlubb64 ) {
				WriteLEInt( -1 );
				WriteLEInt( -1 );
			} else {
				if( ( entry.CompressedSize < 0 ) || ( entry.Size < 0 ) ) {
					update.SizePatchOffset = baseStream_.Position;
				}

				WriteLEInt( (int)entry.CompressedSize );
				WriteLEInt( (int)entry.Size );
			}

			byte[] name = BlubbZipConstants.ConvertToArray( entry.Flags, entry.Name );

			if( name.Length > 0xFFFF ) {
				throw new BlubbZipException( "Entry name too long." );
			}

			BlubbZipExtraData ed = new BlubbZipExtraData( entry.ExtraData );

			if( entry.LocalHeaderRequiresBlubb64 ) {
				ed.StartNewEntry();

				// Local entry header always includes size and compressed size.
				// NOTE the order of these fields is reversed when compared to the normal headers!
				ed.AddLeLong( entry.Size );
				ed.AddLeLong( entry.CompressedSize );
				ed.AddNewEntry( 1 );
			} else {
				ed.Delete( 1 );
			}

			entry.ExtraData = ed.GetEntryData();

			WriteLEShort( name.Length );
			WriteLEShort( entry.ExtraData.Length );

			if( name.Length > 0 ) {
				baseStream_.Write( name, 0, name.Length );
			}

			if( entry.LocalHeaderRequiresBlubb64 ) {
				if( !ed.Find( 1 ) ) {
					throw new BlubbZipException( "Internal error cannot find extra data" );
				}

				update.SizePatchOffset = baseStream_.Position + ed.CurrentReadIndex;
			}

			if( entry.ExtraData.Length > 0 ) {
				baseStream_.Write( entry.ExtraData, 0, entry.ExtraData.Length );
			}
		}
Example #5
0
        /// <summary>
        /// Process extra data fields updating the entry based on the contents.
        /// </summary>
        /// <param name="localHeader">True if the extra data fields should be handled
        /// for a local header, rather than for a central header.
        /// </param>
        internal void ProcessExtraData(bool localHeader)
        {
            BlubbZipExtraData extraData = new BlubbZipExtraData(this.extra);

            if (extraData.Find(0x0001))
            {
                if ((versionToExtract & 0xff) < BlubbZipConstants.VersionBlubb64)
                {
                    throw new BlubbZipException("Blubb64 Extended information found but version is not valid");
                }

                // The recorded size will change but remember that this is blubb64.
                forceBlubb64_ = true;

                if (extraData.ValueLength < 4)
                {
                    throw new BlubbZipException("Extra data extended Blubb64 information length is invalid");
                }

                if (localHeader || size == uint.MaxValue)
                {
                    size = (ulong)extraData.ReadLong();
                }

                if (localHeader || compressedSize == uint.MaxValue)
                {
                    compressedSize = (ulong)extraData.ReadLong();
                }

                if (!localHeader && offset == uint.MaxValue)
                {
                    offset = extraData.ReadLong();
                }
            }
            else
            {
                if (
                    ((versionToExtract & 0xff) >= BlubbZipConstants.VersionBlubb64) &&
                    ((size == uint.MaxValue) || (compressedSize == uint.MaxValue))
                    )
                {
                    throw new BlubbZipException("Blubb64 Extended information required but is missing.");
                }
            }

            if (extraData.Find(10))
            {
                // No room for any tags.
                if (extraData.ValueLength < 8)
                {
                    throw new BlubbZipException("NTFS Extra data invalid");
                }

                extraData.ReadInt();                 // Reserved

                while (extraData.UnreadCount >= 4)
                {
                    int ntfsTag    = extraData.ReadShort();
                    int ntfsLength = extraData.ReadShort();
                    if (ntfsTag == 1)
                    {
                        if (ntfsLength >= 24)
                        {
                            long lastModification = extraData.ReadLong();
                            long lastAccess       = extraData.ReadLong();
                            long createTime       = extraData.ReadLong();

                            DateTime = System.DateTime.FromFileTime(lastModification);
                        }
                        break;
                    }
                    else
                    {
                        // An unknown NTFS tag so simply skip it.
                        extraData.Skip(ntfsLength);
                    }
                }
            }
            else if (extraData.Find(0x5455))
            {
                int length = extraData.ValueLength;
                int flags  = extraData.ReadByte();

                // Can include other times but these are ignored.  Length of data should
                // actually be 1 + 4 * no of bits in flags.
                if (((flags & 1) != 0) && (length >= 5))
                {
                    int iTime = extraData.ReadInt();

                    DateTime = (new System.DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime() +
                                new TimeSpan(0, 0, 0, iTime, 0)).ToLocalTime();
                }
            }
        }
        /// <summary>
        /// Starts a new Blubb entry. It automatically closes the previous
        /// entry if present.
        /// All entry elements bar name are optional, but must be correct if present.
        /// If the compression method is stored and the output is not patchable
        /// the compression for that entry is automatically changed to deflate level 0
        /// </summary>
        /// <param name="entry">
        /// the entry.
        /// </param>
        /// <exception cref="System.ArgumentNullException">
        /// if entry passed is null.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// if an I/O error occured.
        /// </exception>
        /// <exception cref="System.InvalidOperationException">
        /// if stream was finished
        /// </exception>
        /// <exception cref="BlubbException">
        /// Too many entries in the Blubb file<br/>
        /// Entry name is too long<br/>
        /// Finish has already been called<br/>
        /// </exception>
        public void PutNextEntry(BlubbZipEntry entry)
        {
            if (entry == null)
            {
                throw new ArgumentNullException("entry");
            }

            if (entries == null)
            {
                throw new InvalidOperationException("BlubbOutputStream was finished");
            }

            if (curEntry != null)
            {
                CloseEntry();
            }

            if (entries.Count == int.MaxValue)
            {
                throw new BlubbZipException("Too many entries for Blubb file");
            }

            CompressionMethod method = entry.CompressionMethod;
            int compressionLevel     = defaultCompressionLevel;

            // Clear flags that the library manages internally
            entry.Flags     &= (int)GeneralBitFlags.UnicodeText;
            patchEntryHeader = false;

            bool headerInfoAvailable;

            // No need to compress - definitely no data.
            if (entry.Size == 0)
            {
                entry.CompressedSize = entry.Size;
                entry.Crc            = 0;
                method = CompressionMethod.Stored;
                headerInfoAvailable = true;
            }
            else
            {
                headerInfoAvailable = (entry.Size >= 0) && entry.HasCrc;

                // Switch to deflation if storing isnt possible.
                if (method == CompressionMethod.Stored)
                {
                    if (!headerInfoAvailable)
                    {
                        if (!CanPatchEntries)
                        {
                            // Can't patch entries so storing is not possible.
                            method           = CompressionMethod.Deflated;
                            compressionLevel = 0;
                        }
                    }
                    else
                    {
                        // entry.size must be > 0
                        entry.CompressedSize = entry.Size;
                        headerInfoAvailable  = entry.HasCrc;
                    }
                }
            }

            if (headerInfoAvailable == false)
            {
                if (CanPatchEntries == false)
                {
                    // Only way to record size and compressed size is to append a data descriptor
                    // after compressed data.

                    // Stored entries of this form have already been converted to deflating.
                    entry.Flags |= 8;
                }
                else
                {
                    patchEntryHeader = true;
                }
            }

            if (Password != null)
            {
                entry.IsCrypted = true;
                if (entry.Crc < 0)
                {
                    // Need to append a data descriptor as the crc isnt available for use
                    // with encryption, the date is used instead.  Setting the flag
                    // indicates this to the decompressor.
                    entry.Flags |= 8;
                }
            }

            entry.Offset            = offset;
            entry.CompressionMethod = (CompressionMethod)method;

            curMethod    = method;
            sizePatchPos = -1;

            if (useBlubb64_ == UseBlubb64.On || (entry.Size < 0 && useBlubb64_ == UseBlubb64.Dynamic))
            {
                entry.ForceBlubb64();
            }

            // Write the local file header
            WriteLeInt(BlubbZipConstants.LocalHeaderSignature);

            WriteLeShort(entry.Version);
            WriteLeShort(entry.Flags);
            WriteLeShort((byte)method);
            WriteLeInt((int)entry.DosTime);

            // TODO: Refactor header writing.  Its done in several places.
            if (headerInfoAvailable == true)
            {
                WriteLeInt((int)entry.Crc);
                if (entry.LocalHeaderRequiresBlubb64)
                {
                    WriteLeInt(-1);
                    WriteLeInt(-1);
                }
                else
                {
                    WriteLeInt(entry.IsCrypted ? (int)entry.CompressedSize + BlubbZipConstants.CryptoHeaderSize : (int)entry.CompressedSize);
                    WriteLeInt((int)entry.Size);
                }
            }
            else
            {
                if (patchEntryHeader == true)
                {
                    crcPatchPos = baseOutputStream_.Position;
                }
                WriteLeInt(0);                          // Crc

                if (patchEntryHeader)
                {
                    sizePatchPos = baseOutputStream_.Position;
                }

                // For local header both sizes appear in Blubb64 Extended Information
                if (entry.LocalHeaderRequiresBlubb64 || patchEntryHeader)
                {
                    WriteLeInt(-1);
                    WriteLeInt(-1);
                }
                else
                {
                    WriteLeInt(0);                              // Compressed size
                    WriteLeInt(0);                              // Uncompressed size
                }
            }

            byte[] name = BlubbZipConstants.ConvertToArray(entry.Flags, entry.Name);

            if (name.Length > 0xFFFF)
            {
                throw new BlubbZipException("Entry name too long.");
            }

            BlubbZipExtraData ed = new BlubbZipExtraData(entry.ExtraData);

            if (entry.LocalHeaderRequiresBlubb64)
            {
                ed.StartNewEntry();
                if (headerInfoAvailable)
                {
                    ed.AddLeLong(entry.Size);
                    ed.AddLeLong(entry.CompressedSize);
                }
                else
                {
                    ed.AddLeLong(-1);
                    ed.AddLeLong(-1);
                }
                ed.AddNewEntry(1);

                if (!ed.Find(1))
                {
                    throw new BlubbZipException("Internal error cant find extra data");
                }

                if (patchEntryHeader)
                {
                    sizePatchPos = ed.CurrentReadIndex;
                }
            }
            else
            {
                ed.Delete(1);
            }

            byte[] extra = ed.GetEntryData();

            WriteLeShort(name.Length);
            WriteLeShort(extra.Length);

            if (name.Length > 0)
            {
                baseOutputStream_.Write(name, 0, name.Length);
            }

            if (entry.LocalHeaderRequiresBlubb64 && patchEntryHeader)
            {
                sizePatchPos += baseOutputStream_.Position;
            }

            if (extra.Length > 0)
            {
                baseOutputStream_.Write(extra, 0, extra.Length);
            }

            offset += BlubbZipConstants.LocalHeaderBaseSize + name.Length + extra.Length;

            // Activate the entry.
            curEntry = entry;
            crc.Reset();
            if (method == CompressionMethod.Deflated)
            {
                deflater_.Reset();
                deflater_.SetLevel(compressionLevel);
            }
            size = 0;

            if (entry.IsCrypted == true)
            {
                if (entry.Crc < 0)                    // so testing Blubb will says its ok
                {
                    WriteEncryptionHeader(entry.DosTime << 16);
                }
                else
                {
                    WriteEncryptionHeader(entry.Crc);
                }
            }
        }
Example #7
0
		// Write the local file header
		// TODO: BlubbHelperStream.WriteLocalHeader is not yet used and needs checking for BlubbFile and BlubbOuptutStream usage
		void WriteLocalHeader( BlubbZipEntry entry, EntryPatchData patchData ) {
			CompressionMethod method = entry.CompressionMethod;
			bool headerInfoAvailable = true; // How to get this?
			bool patchEntryHeader = false;

			WriteLEInt( BlubbZipConstants.LocalHeaderSignature );

			WriteLEShort( entry.Version );
			WriteLEShort( entry.Flags );
			WriteLEShort( (byte)method );
			WriteLEInt( (int)entry.DosTime );

			if( headerInfoAvailable == true ) {
				WriteLEInt( (int)entry.Crc );
				if( entry.LocalHeaderRequiresBlubb64 ) {
					WriteLEInt( -1 );
					WriteLEInt( -1 );
				} else {
					WriteLEInt( entry.IsCrypted ? (int)entry.CompressedSize + BlubbZipConstants.CryptoHeaderSize : (int)entry.CompressedSize );
					WriteLEInt( (int)entry.Size );
				}
			} else {
				if( patchData != null ) {
					patchData.CrcPatchOffset = stream_.Position;
				}
				WriteLEInt( 0 );	// Crc

				if( patchData != null ) {
					patchData.SizePatchOffset = stream_.Position;
				}

				// For local header both sizes appear in Blubb64 Extended Information
				if( entry.LocalHeaderRequiresBlubb64 && patchEntryHeader ) {
					WriteLEInt( -1 );
					WriteLEInt( -1 );
				} else {
					WriteLEInt( 0 );	// Compressed size
					WriteLEInt( 0 );	// Uncompressed size
				}
			}

			byte[] name = BlubbZipConstants.ConvertToArray( entry.Flags, entry.Name );

			if( name.Length > 0xFFFF ) {
				throw new BlubbZipException( "Entry name too long." );
			}

			BlubbZipExtraData ed = new BlubbZipExtraData( entry.ExtraData );

			if( entry.LocalHeaderRequiresBlubb64 && ( headerInfoAvailable || patchEntryHeader ) ) {
				ed.StartNewEntry();
				if( headerInfoAvailable ) {
					ed.AddLeLong( entry.Size );
					ed.AddLeLong( entry.CompressedSize );
				} else {
					ed.AddLeLong( -1 );
					ed.AddLeLong( -1 );
				}
				ed.AddNewEntry( 1 );

				if( !ed.Find( 1 ) ) {
					throw new BlubbZipException( "Internal error cant find extra data" );
				}

				if( patchData != null ) {
					patchData.SizePatchOffset = ed.CurrentReadIndex;
				}
			} else {
				ed.Delete( 1 );
			}

			byte[] extra = ed.GetEntryData();

			WriteLEShort( name.Length );
			WriteLEShort( extra.Length );

			if( name.Length > 0 ) {
				stream_.Write( name, 0, name.Length );
			}

			if( entry.LocalHeaderRequiresBlubb64 && patchEntryHeader ) {
				patchData.SizePatchOffset += stream_.Position;
			}

			if( extra.Length > 0 ) {
				stream_.Write( extra, 0, extra.Length );
			}
		}
Example #8
0
		/// <summary>
		/// Process extra data fields updating the entry based on the contents.
		/// </summary>
		/// <param name="localHeader">True if the extra data fields should be handled
		/// for a local header, rather than for a central header.
		/// </param>
		internal void ProcessExtraData( bool localHeader ) {
			BlubbZipExtraData extraData = new BlubbZipExtraData( this.extra );

			if( extraData.Find( 0x0001 ) ) {
				if( ( versionToExtract & 0xff ) < BlubbZipConstants.VersionBlubb64 )
					throw new BlubbZipException( "Blubb64 Extended information found but version is not valid" );

				// The recorded size will change but remember that this is blubb64.
				forceBlubb64_ = true;

				if( extraData.ValueLength < 4 )
					throw new BlubbZipException( "Extra data extended Blubb64 information length is invalid" );

				if( localHeader || size == uint.MaxValue )
					size = (ulong)extraData.ReadLong();

				if( localHeader || compressedSize == uint.MaxValue )
					compressedSize = (ulong)extraData.ReadLong();

				if( !localHeader && offset == uint.MaxValue )
					offset = extraData.ReadLong();

			} else {
				if(
					( ( versionToExtract & 0xff ) >= BlubbZipConstants.VersionBlubb64 ) &&
					( ( size == uint.MaxValue ) || ( compressedSize == uint.MaxValue ) )
				) {
					throw new BlubbZipException( "Blubb64 Extended information required but is missing." );
				}
			}

			if( extraData.Find( 10 ) ) {
				// No room for any tags.
				if( extraData.ValueLength < 8 ) {
					throw new BlubbZipException( "NTFS Extra data invalid" );
				}

				extraData.ReadInt(); // Reserved

				while( extraData.UnreadCount >= 4 ) {
					int ntfsTag = extraData.ReadShort();
					int ntfsLength = extraData.ReadShort();
					if( ntfsTag == 1 ) {
						if( ntfsLength >= 24 ) {
							long lastModification = extraData.ReadLong();
							long lastAccess = extraData.ReadLong();
							long createTime = extraData.ReadLong();

							DateTime = System.DateTime.FromFileTime( lastModification );
						}
						break;
					} else {
						// An unknown NTFS tag so simply skip it.
						extraData.Skip( ntfsLength );
					}
				}
			} else if( extraData.Find( 0x5455 ) ) {
				int length = extraData.ValueLength;
				int flags = extraData.ReadByte();

				// Can include other times but these are ignored.  Length of data should
				// actually be 1 + 4 * no of bits in flags.
				if( ( ( flags & 1 ) != 0 ) && ( length >= 5 ) ) {
					int iTime = extraData.ReadInt();

					DateTime = ( new System.DateTime( 1970, 1, 1, 0, 0, 0 ).ToUniversalTime() +
						new TimeSpan( 0, 0, 0, iTime, 0 ) ).ToLocalTime();
				}
			}
		}
Example #9
0
		/// <summary>
		/// Finishes the stream.  This will write the central directory at the
		/// end of the blubb file and flush the stream.
		/// </summary>
		/// <remarks>
		/// This is automatically called when the stream is closed.
		/// </remarks>
		/// <exception cref="System.IO.IOException">
		/// An I/O error occurs.
		/// </exception>
		/// <exception cref="BlubbException">
		/// Comment exceeds the maximum length<br/>
		/// Entry name exceeds the maximum length
		/// </exception>
		public override void Finish() {
			if( entries == null ) {
				return;
			}

			if( curEntry != null ) {
				CloseEntry();
			}

			long numEntries = entries.Count;
			long sizeEntries = 0;

			foreach( BlubbZipEntry entry in entries ) {
				WriteLeInt( BlubbZipConstants.CentralHeaderSignature );
				WriteLeShort( BlubbZipConstants.VersionMadeBy );
				WriteLeShort( entry.Version );
				WriteLeShort( entry.Flags );
				WriteLeShort( (short)entry.CompressionMethod );
				WriteLeInt( (int)entry.DosTime );
				WriteLeInt( (int)entry.Crc );

				if( entry.IsBlubb64Forced() ||
					( entry.CompressedSize >= uint.MaxValue ) ) {
					WriteLeInt( -1 );
				} else {
					WriteLeInt( (int)entry.CompressedSize );
				}

				if( entry.IsBlubb64Forced() ||
					( entry.Size >= uint.MaxValue ) ) {
					WriteLeInt( -1 );
				} else {
					WriteLeInt( (int)entry.Size );
				}

				byte[] name = BlubbZipConstants.ConvertToArray( entry.Flags, entry.Name );

				if( name.Length > 0xffff ) {
					throw new BlubbZipException( "Name too long." );
				}

				BlubbZipExtraData ed = new BlubbZipExtraData( entry.ExtraData );

				if( entry.CentralHeaderRequiresBlubb64 ) {
					ed.StartNewEntry();
					if( entry.IsBlubb64Forced() ||
						( entry.Size >= 0xffffffff ) ) {
						ed.AddLeLong( entry.Size );
					}

					if( entry.IsBlubb64Forced() ||
						( entry.CompressedSize >= 0xffffffff ) ) {
						ed.AddLeLong( entry.CompressedSize );
					}

					if( entry.Offset >= 0xffffffff ) {
						ed.AddLeLong( entry.Offset );
					}

					ed.AddNewEntry( 1 );
				} else {
					ed.Delete( 1 );
				}

				byte[] extra = ed.GetEntryData();

				byte[] entryComment =
					( entry.Comment != null ) ?
					BlubbZipConstants.ConvertToArray( entry.Flags, entry.Comment ) :
					new byte[ 0 ];

				if( entryComment.Length > 0xffff ) {
					throw new BlubbZipException( "Comment too long." );
				}

				WriteLeShort( name.Length );
				WriteLeShort( extra.Length );
				WriteLeShort( entryComment.Length );
				WriteLeShort( 0 );	// disk number
				WriteLeShort( 0 );	// internal file attributes
				// external file attributes

				if( entry.ExternalFileAttributes != -1 ) {
					WriteLeInt( entry.ExternalFileAttributes );
				} else {
					if( entry.IsDirectory ) {
						WriteLeInt( 16 );
					} else {
						WriteLeInt( 0 );
					}
				}

				if( entry.Offset >= uint.MaxValue ) {
					WriteLeInt( -1 );
				} else {
					WriteLeInt( (int)entry.Offset );
				}

				if( name.Length > 0 ) {
					baseOutputStream_.Write( name, 0, name.Length );
				}

				if( extra.Length > 0 ) {
					baseOutputStream_.Write( extra, 0, extra.Length );
				}

				if( entryComment.Length > 0 ) {
					baseOutputStream_.Write( entryComment, 0, entryComment.Length );
				}

				sizeEntries += BlubbZipConstants.CentralHeaderBaseSize + name.Length + extra.Length + entryComment.Length;
			}

			using( BlubbZipHelperStream zhs = new BlubbZipHelperStream( baseOutputStream_ ) ) {
				zhs.WriteEndOfCentralDirectory( numEntries, sizeEntries, offset, blubbComment );
			}

			entries = null;
		}
Example #10
0
		/// <summary>
		/// Starts a new Blubb entry. It automatically closes the previous
		/// entry if present.
		/// All entry elements bar name are optional, but must be correct if present.
		/// If the compression method is stored and the output is not patchable
		/// the compression for that entry is automatically changed to deflate level 0
		/// </summary>
		/// <param name="entry">
		/// the entry.
		/// </param>
		/// <exception cref="System.ArgumentNullException">
		/// if entry passed is null.
		/// </exception>
		/// <exception cref="System.IO.IOException">
		/// if an I/O error occured.
		/// </exception>
		/// <exception cref="System.InvalidOperationException">
		/// if stream was finished
		/// </exception>
		/// <exception cref="BlubbException">
		/// Too many entries in the Blubb file<br/>
		/// Entry name is too long<br/>
		/// Finish has already been called<br/>
		/// </exception>
		public void PutNextEntry( BlubbZipEntry entry ) {
			if( entry == null ) 
				throw new ArgumentNullException( "entry" );

			if( entries == null ) 
				throw new InvalidOperationException( "BlubbOutputStream was finished" );

			if( curEntry != null ) 
				CloseEntry();

			if( entries.Count == int.MaxValue ) 
				throw new BlubbZipException( "Too many entries for Blubb file" );

			CompressionMethod method = entry.CompressionMethod;
			int compressionLevel = defaultCompressionLevel;

			// Clear flags that the library manages internally
			entry.Flags &= (int)GeneralBitFlags.UnicodeText;
			patchEntryHeader = false;

			bool headerInfoAvailable;

			// No need to compress - definitely no data.
			if( entry.Size == 0 ) {
				entry.CompressedSize = entry.Size;
				entry.Crc = 0;
				method = CompressionMethod.Stored;
				headerInfoAvailable = true;
			} else {
				headerInfoAvailable = ( entry.Size >= 0 ) && entry.HasCrc;

				// Switch to deflation if storing isnt possible.
				if( method == CompressionMethod.Stored ) {
					if( !headerInfoAvailable ) {
						if( !CanPatchEntries ) {
							// Can't patch entries so storing is not possible.
							method = CompressionMethod.Deflated;
							compressionLevel = 0;
						}
					} else {
						// entry.size must be > 0
						entry.CompressedSize = entry.Size;
						headerInfoAvailable = entry.HasCrc;
					}
				}
			}

			if( headerInfoAvailable == false ) {
				if( CanPatchEntries == false ) {
					// Only way to record size and compressed size is to append a data descriptor
					// after compressed data.

					// Stored entries of this form have already been converted to deflating.
					entry.Flags |= 8;
				} else {
					patchEntryHeader = true;
				}
			}

			if( Password != null ) {
				entry.IsCrypted = true;
				if( entry.Crc < 0 ) {
					// Need to append a data descriptor as the crc isnt available for use
					// with encryption, the date is used instead.  Setting the flag
					// indicates this to the decompressor.
					entry.Flags |= 8;
				}
			}

			entry.Offset = offset;
			entry.CompressionMethod = (CompressionMethod)method;

			curMethod = method;
			sizePatchPos = -1;

			if( useBlubb64_ == UseBlubb64.On || ( entry.Size < 0 && useBlubb64_ == UseBlubb64.Dynamic ) )
				entry.ForceBlubb64();

			// Write the local file header
			WriteLeInt( BlubbZipConstants.LocalHeaderSignature );

			WriteLeShort( entry.Version );
			WriteLeShort( entry.Flags );
			WriteLeShort( (byte)method );
			WriteLeInt( (int)entry.DosTime );

			// TODO: Refactor header writing.  Its done in several places.
			if( headerInfoAvailable == true ) {
				WriteLeInt( (int)entry.Crc );
				if( entry.LocalHeaderRequiresBlubb64 ) {
					WriteLeInt( -1 );
					WriteLeInt( -1 );
				} else {
					WriteLeInt( entry.IsCrypted ? (int)entry.CompressedSize + BlubbZipConstants.CryptoHeaderSize : (int)entry.CompressedSize );
					WriteLeInt( (int)entry.Size );
				}
			} else {
				if( patchEntryHeader == true ) {
					crcPatchPos = baseOutputStream_.Position;
				}
				WriteLeInt( 0 );	// Crc

				if( patchEntryHeader ) {
					sizePatchPos = baseOutputStream_.Position;
				}

				// For local header both sizes appear in Blubb64 Extended Information
				if( entry.LocalHeaderRequiresBlubb64 || patchEntryHeader ) {
					WriteLeInt( -1 );
					WriteLeInt( -1 );
				} else {
					WriteLeInt( 0 );	// Compressed size
					WriteLeInt( 0 );	// Uncompressed size
				}
			}

			byte[] name = BlubbZipConstants.ConvertToArray( entry.Flags, entry.Name );

			if( name.Length > 0xFFFF )
				throw new BlubbZipException( "Entry name too long." );

			BlubbZipExtraData ed = new BlubbZipExtraData( entry.ExtraData );

			if( entry.LocalHeaderRequiresBlubb64 ) {
				ed.StartNewEntry();
				if( headerInfoAvailable ) {
					ed.AddLeLong( entry.Size );
					ed.AddLeLong( entry.CompressedSize );
				} else {
					ed.AddLeLong( -1 );
					ed.AddLeLong( -1 );
				}
				ed.AddNewEntry( 1 );

				if( !ed.Find( 1 ) ) {
					throw new BlubbZipException( "Internal error cant find extra data" );
				}

				if( patchEntryHeader ) {
					sizePatchPos = ed.CurrentReadIndex;
				}
			} else {
				ed.Delete( 1 );
			}

			byte[] extra = ed.GetEntryData();

			WriteLeShort( name.Length );
			WriteLeShort( extra.Length );

			if( name.Length > 0 ) {
				baseOutputStream_.Write( name, 0, name.Length );
			}

			if( entry.LocalHeaderRequiresBlubb64 && patchEntryHeader ) {
				sizePatchPos += baseOutputStream_.Position;
			}

			if( extra.Length > 0 ) {
				baseOutputStream_.Write( extra, 0, extra.Length );
			}

			offset += BlubbZipConstants.LocalHeaderBaseSize + name.Length + extra.Length;

			// Activate the entry.
			curEntry = entry;
			crc.Reset();
			if( method == CompressionMethod.Deflated ) {
				deflater_.Reset();
				deflater_.SetLevel( compressionLevel );
			}
			size = 0;

			if( entry.IsCrypted == true ) {
				if( entry.Crc < 0 ) { // so testing Blubb will says its ok
					WriteEncryptionHeader( entry.DosTime << 16 );
				} else {
					WriteEncryptionHeader( entry.Crc );
				}
			}
		}
        // Write the local file header
        // TODO: BlubbHelperStream.WriteLocalHeader is not yet used and needs checking for BlubbFile and BlubbOuptutStream usage
        void WriteLocalHeader(BlubbZipEntry entry, EntryPatchData patchData)
        {
            CompressionMethod method = entry.CompressionMethod;
            bool headerInfoAvailable = true;             // How to get this?
            bool patchEntryHeader    = false;

            WriteLEInt(BlubbZipConstants.LocalHeaderSignature);

            WriteLEShort(entry.Version);
            WriteLEShort(entry.Flags);
            WriteLEShort((byte)method);
            WriteLEInt((int)entry.DosTime);

            if (headerInfoAvailable == true)
            {
                WriteLEInt((int)entry.Crc);
                if (entry.LocalHeaderRequiresBlubb64)
                {
                    WriteLEInt(-1);
                    WriteLEInt(-1);
                }
                else
                {
                    WriteLEInt(entry.IsCrypted ? (int)entry.CompressedSize + BlubbZipConstants.CryptoHeaderSize : (int)entry.CompressedSize);
                    WriteLEInt((int)entry.Size);
                }
            }
            else
            {
                if (patchData != null)
                {
                    patchData.CrcPatchOffset = stream_.Position;
                }
                WriteLEInt(0);                          // Crc

                if (patchData != null)
                {
                    patchData.SizePatchOffset = stream_.Position;
                }

                // For local header both sizes appear in Blubb64 Extended Information
                if (entry.LocalHeaderRequiresBlubb64 && patchEntryHeader)
                {
                    WriteLEInt(-1);
                    WriteLEInt(-1);
                }
                else
                {
                    WriteLEInt(0);                              // Compressed size
                    WriteLEInt(0);                              // Uncompressed size
                }
            }

            byte[] name = BlubbZipConstants.ConvertToArray(entry.Flags, entry.Name);

            if (name.Length > 0xFFFF)
            {
                throw new BlubbZipException("Entry name too long.");
            }

            BlubbZipExtraData ed = new BlubbZipExtraData(entry.ExtraData);

            if (entry.LocalHeaderRequiresBlubb64 && (headerInfoAvailable || patchEntryHeader))
            {
                ed.StartNewEntry();
                if (headerInfoAvailable)
                {
                    ed.AddLeLong(entry.Size);
                    ed.AddLeLong(entry.CompressedSize);
                }
                else
                {
                    ed.AddLeLong(-1);
                    ed.AddLeLong(-1);
                }
                ed.AddNewEntry(1);

                if (!ed.Find(1))
                {
                    throw new BlubbZipException("Internal error cant find extra data");
                }

                if (patchData != null)
                {
                    patchData.SizePatchOffset = ed.CurrentReadIndex;
                }
            }
            else
            {
                ed.Delete(1);
            }

            byte[] extra = ed.GetEntryData();

            WriteLEShort(name.Length);
            WriteLEShort(extra.Length);

            if (name.Length > 0)
            {
                stream_.Write(name, 0, name.Length);
            }

            if (entry.LocalHeaderRequiresBlubb64 && patchEntryHeader)
            {
                patchData.SizePatchOffset += stream_.Position;
            }

            if (extra.Length > 0)
            {
                stream_.Write(extra, 0, extra.Length);
            }
        }