Include Non-NuGet Resources
This commit is contained in:
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,530 @@
|
||||
//#define Trace
|
||||
|
||||
// BZip2OutputStream.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-August-02 16:44:11>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the BZip2OutputStream class, which is a
|
||||
// compressing stream that handles BZIP2. This code may have been
|
||||
// derived in part from Apache commons source code. The license below
|
||||
// applies to the original Apache code.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
// flymake: csc.exe /t:module BZip2InputStream.cs BZip2Compressor.cs Rand.cs BCRC32.cs @@FILE@@
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
|
||||
// Design Notes:
|
||||
//
|
||||
// This class follows the classic Decorator pattern: it is a Stream that
|
||||
// wraps itself around a Stream, and in doing so provides bzip2
|
||||
// compression as callers Write into it.
|
||||
//
|
||||
// BZip2 is a straightforward data format: there are 4 magic bytes at
|
||||
// the top of the file, followed by 1 or more compressed blocks. There
|
||||
// is a small "magic byte" trailer after all compressed blocks. This
|
||||
// class emits the magic bytes for the header and trailer, and relies on
|
||||
// a BZip2Compressor to generate each of the compressed data blocks.
|
||||
//
|
||||
// BZip2 does byte-shredding - it uses partial fractions of bytes to
|
||||
// represent independent pieces of information. This class relies on the
|
||||
// BitWriter to adapt the bit-oriented BZip2 output to the byte-oriented
|
||||
// model of the .NET Stream class.
|
||||
//
|
||||
// ----
|
||||
//
|
||||
// Regarding the Apache code base: Most of the code in this particular
|
||||
// class is related to stream operations, and is my own code. It largely
|
||||
// does not rely on any code obtained from Apache commons. If you
|
||||
// compare this code with the Apache commons BZip2OutputStream, you will
|
||||
// see very little code that is common, except for the
|
||||
// nearly-boilerplate structure that is common to all subtypes of
|
||||
// System.IO.Stream. There may be some small remnants of code in this
|
||||
// module derived from the Apache stuff, which is why I left the license
|
||||
// in here. Most of the Apache commons compressor magic has been ported
|
||||
// into the BZip2Compressor class.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
|
||||
namespace Ionic.BZip2
|
||||
{
|
||||
/// <summary>
|
||||
/// A write-only decorator stream that compresses data as it is
|
||||
/// written using the BZip2 algorithm.
|
||||
/// </summary>
|
||||
public class BZip2OutputStream : System.IO.Stream
|
||||
{
|
||||
int totalBytesWrittenIn;
|
||||
bool leaveOpen;
|
||||
BZip2Compressor compressor;
|
||||
uint combinedCRC;
|
||||
Stream output;
|
||||
BitWriter bw;
|
||||
int blockSize100k; // 0...9
|
||||
|
||||
private TraceBits desiredTrace = TraceBits.Crc | TraceBits.Write;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <c>BZip2OutputStream</c>, that sends its
|
||||
/// compressed output to the given output stream.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name='output'>
|
||||
/// The destination stream, to which compressed output will be sent.
|
||||
/// </param>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example reads a file, then compresses it with bzip2 file,
|
||||
/// and writes the compressed data into a newly created file.
|
||||
///
|
||||
/// <code>
|
||||
/// var fname = "logfile.log";
|
||||
/// using (var fs = File.OpenRead(fname))
|
||||
/// {
|
||||
/// var outFname = fname + ".bz2";
|
||||
/// using (var output = File.Create(outFname))
|
||||
/// {
|
||||
/// using (var compressor = new Ionic.BZip2.BZip2OutputStream(output))
|
||||
/// {
|
||||
/// byte[] buffer = new byte[2048];
|
||||
/// int n;
|
||||
/// while ((n = fs.Read(buffer, 0, buffer.Length)) > 0)
|
||||
/// {
|
||||
/// compressor.Write(buffer, 0, n);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public BZip2OutputStream(Stream output)
|
||||
: this(output, BZip2.MaxBlockSize, false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <c>BZip2OutputStream</c> with specified blocksize.
|
||||
/// </summary>
|
||||
/// <param name = "output">the destination stream.</param>
|
||||
/// <param name = "blockSize">
|
||||
/// The blockSize in units of 100000 bytes.
|
||||
/// The valid range is 1..9.
|
||||
/// </param>
|
||||
public BZip2OutputStream(Stream output, int blockSize)
|
||||
: this(output, blockSize, false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <c>BZip2OutputStream</c>.
|
||||
/// </summary>
|
||||
/// <param name = "output">the destination stream.</param>
|
||||
/// <param name = "leaveOpen">
|
||||
/// whether to leave the captive stream open upon closing this stream.
|
||||
/// </param>
|
||||
public BZip2OutputStream(Stream output, bool leaveOpen)
|
||||
: this(output, BZip2.MaxBlockSize, leaveOpen)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <c>BZip2OutputStream</c> with specified blocksize,
|
||||
/// and explicitly specifies whether to leave the wrapped stream open.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name = "output">the destination stream.</param>
|
||||
/// <param name = "blockSize">
|
||||
/// The blockSize in units of 100000 bytes.
|
||||
/// The valid range is 1..9.
|
||||
/// </param>
|
||||
/// <param name = "leaveOpen">
|
||||
/// whether to leave the captive stream open upon closing this stream.
|
||||
/// </param>
|
||||
public BZip2OutputStream(Stream output, int blockSize, bool leaveOpen)
|
||||
{
|
||||
if (blockSize < BZip2.MinBlockSize ||
|
||||
blockSize > BZip2.MaxBlockSize)
|
||||
{
|
||||
var msg = String.Format("blockSize={0} is out of range; must be between {1} and {2}",
|
||||
blockSize,
|
||||
BZip2.MinBlockSize, BZip2.MaxBlockSize);
|
||||
throw new ArgumentException(msg, "blockSize");
|
||||
}
|
||||
|
||||
this.output = output;
|
||||
if (!this.output.CanWrite)
|
||||
throw new ArgumentException("The stream is not writable.", "output");
|
||||
|
||||
this.bw = new BitWriter(this.output);
|
||||
this.blockSize100k = blockSize;
|
||||
this.compressor = new BZip2Compressor(this.bw, blockSize);
|
||||
this.leaveOpen = leaveOpen;
|
||||
this.combinedCRC = 0;
|
||||
EmitHeader();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Close the stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This may or may not close the underlying stream. Check the
|
||||
/// constructors that accept a bool value.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public override void Close()
|
||||
{
|
||||
if (output != null)
|
||||
{
|
||||
Stream o = this.output;
|
||||
Finish();
|
||||
if (!leaveOpen)
|
||||
o.Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Flush the stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
if (this.output != null)
|
||||
{
|
||||
this.bw.Flush();
|
||||
this.output.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitHeader()
|
||||
{
|
||||
var magic = new byte[] {
|
||||
(byte) 'B',
|
||||
(byte) 'Z',
|
||||
(byte) 'h',
|
||||
(byte) ('0' + this.blockSize100k)
|
||||
};
|
||||
|
||||
// not necessary to shred the initial magic bytes
|
||||
this.output.Write(magic, 0, magic.Length);
|
||||
}
|
||||
|
||||
private void EmitTrailer()
|
||||
{
|
||||
// A magic 48-bit number, 0x177245385090, to indicate the end
|
||||
// of the last block. (sqrt(pi), if you want to know)
|
||||
|
||||
TraceOutput(TraceBits.Write, "total written out: {0} (0x{0:X})",
|
||||
this.bw.TotalBytesWrittenOut);
|
||||
|
||||
// must shred
|
||||
this.bw.WriteByte(0x17);
|
||||
this.bw.WriteByte(0x72);
|
||||
this.bw.WriteByte(0x45);
|
||||
this.bw.WriteByte(0x38);
|
||||
this.bw.WriteByte(0x50);
|
||||
this.bw.WriteByte(0x90);
|
||||
|
||||
this.bw.WriteInt(this.combinedCRC);
|
||||
|
||||
this.bw.FinishAndPad();
|
||||
|
||||
TraceOutput(TraceBits.Write, "final total: {0} (0x{0:X})",
|
||||
this.bw.TotalBytesWrittenOut);
|
||||
}
|
||||
|
||||
void Finish()
|
||||
{
|
||||
// Console.WriteLine("BZip2:Finish");
|
||||
|
||||
try
|
||||
{
|
||||
var totalBefore = this.bw.TotalBytesWrittenOut;
|
||||
this.compressor.CompressAndWrite();
|
||||
TraceOutput(TraceBits.Write,"out block length (bytes): {0} (0x{0:X})",
|
||||
this.bw.TotalBytesWrittenOut - totalBefore);
|
||||
|
||||
TraceOutput(TraceBits.Crc, " combined CRC (before): {0:X8}",
|
||||
this.combinedCRC);
|
||||
this.combinedCRC = (this.combinedCRC << 1) | (this.combinedCRC >> 31);
|
||||
this.combinedCRC ^= (uint) compressor.Crc32;
|
||||
TraceOutput(TraceBits.Crc, " block CRC : {0:X8}",
|
||||
this.compressor.Crc32);
|
||||
TraceOutput(TraceBits.Crc, " combined CRC (final) : {0:X8}",
|
||||
this.combinedCRC);
|
||||
|
||||
EmitTrailer();
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.output = null;
|
||||
this.compressor = null;
|
||||
this.bw = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The blocksize parameter specified at construction time.
|
||||
/// </summary>
|
||||
public int BlockSize
|
||||
{
|
||||
get { return this.blockSize100k; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Write data to the stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// Use the <c>BZip2OutputStream</c> to compress data while writing:
|
||||
/// create a <c>BZip2OutputStream</c> with a writable output stream.
|
||||
/// Then call <c>Write()</c> on that <c>BZip2OutputStream</c>, providing
|
||||
/// uncompressed data as input. The data sent to the output stream will
|
||||
/// be the compressed form of the input data.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A <c>BZip2OutputStream</c> can be used only for <c>Write()</c> not for <c>Read()</c>.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="buffer">The buffer holding data to write to the stream.</param>
|
||||
/// <param name="offset">the offset within that data array to find the first byte to write.</param>
|
||||
/// <param name="count">the number of bytes to write.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (offset < 0)
|
||||
throw new IndexOutOfRangeException(String.Format("offset ({0}) must be > 0", offset));
|
||||
if (count < 0)
|
||||
throw new IndexOutOfRangeException(String.Format("count ({0}) must be > 0", count));
|
||||
if (offset + count > buffer.Length)
|
||||
throw new IndexOutOfRangeException(String.Format("offset({0}) count({1}) bLength({2})",
|
||||
offset, count, buffer.Length));
|
||||
if (this.output == null)
|
||||
throw new IOException("the stream is not open");
|
||||
|
||||
if (count == 0) return; // nothing to do
|
||||
|
||||
int bytesWritten = 0;
|
||||
int bytesRemaining = count;
|
||||
|
||||
do
|
||||
{
|
||||
int n = compressor.Fill(buffer, offset, bytesRemaining);
|
||||
if (n != bytesRemaining)
|
||||
{
|
||||
// The compressor data block is full. Compress and
|
||||
// write out the compressed data, then reset the
|
||||
// compressor and continue.
|
||||
|
||||
var totalBefore = this.bw.TotalBytesWrittenOut;
|
||||
this.compressor.CompressAndWrite();
|
||||
TraceOutput(TraceBits.Write,"out block length (bytes): {0} (0x{0:X})",
|
||||
this.bw.TotalBytesWrittenOut - totalBefore);
|
||||
|
||||
// and now any remaining bits
|
||||
TraceOutput(TraceBits.Write,
|
||||
" remaining: {0} 0x{1:X}",
|
||||
this.bw.NumRemainingBits,
|
||||
this.bw.RemainingBits);
|
||||
|
||||
TraceOutput(TraceBits.Crc, " combined CRC (before): {0:X8}",
|
||||
this.combinedCRC);
|
||||
this.combinedCRC = (this.combinedCRC << 1) | (this.combinedCRC >> 31);
|
||||
this.combinedCRC ^= (uint) compressor.Crc32;
|
||||
TraceOutput(TraceBits.Crc, " block CRC : {0:X8}",
|
||||
compressor.Crc32);
|
||||
TraceOutput(TraceBits.Crc, " combined CRC (after) : {0:X8}",
|
||||
this.combinedCRC);
|
||||
offset += n;
|
||||
}
|
||||
bytesRemaining -= n;
|
||||
bytesWritten += n;
|
||||
} while (bytesRemaining > 0);
|
||||
|
||||
totalBytesWrittenIn += bytesWritten;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream can be read.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The return value is always false.
|
||||
/// </remarks>
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream supports Seek operations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Always returns false.
|
||||
/// </remarks>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream can be written.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The return value should always be true, unless and until the
|
||||
/// object is disposed and closed.
|
||||
/// </remarks>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.output == null) throw new ObjectDisposedException("BZip2Stream");
|
||||
return this.output.CanWrite;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reading this property always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The position of the stream pointer.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Setting this property always throws a <see
|
||||
/// cref="NotImplementedException"/>. Reading will return the
|
||||
/// total number of uncompressed bytes written through.
|
||||
/// </remarks>
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.totalBytesWrittenIn;
|
||||
}
|
||||
set { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calling this method always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
/// <param name="offset">this is irrelevant, since it will always throw!</param>
|
||||
/// <param name="origin">this is irrelevant, since it will always throw!</param>
|
||||
/// <returns>irrelevant!</returns>
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calling this method always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">this is irrelevant, since it will always throw!</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calling this method always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
/// <param name='buffer'>this parameter is never used</param>
|
||||
/// <param name='offset'>this parameter is never used</param>
|
||||
/// <param name='count'>this parameter is never used</param>
|
||||
/// <returns>never returns anything; always throws</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
// used only when Trace is defined
|
||||
[Flags]
|
||||
enum TraceBits : uint
|
||||
{
|
||||
None = 0,
|
||||
Crc = 1,
|
||||
Write = 2,
|
||||
All = 0xffffffff,
|
||||
}
|
||||
|
||||
|
||||
[System.Diagnostics.ConditionalAttribute("Trace")]
|
||||
private void TraceOutput(TraceBits bits, string format, params object[] varParams)
|
||||
{
|
||||
if ((bits & this.desiredTrace) != 0)
|
||||
{
|
||||
//lock(outputLock)
|
||||
{
|
||||
int tid = System.Threading.Thread.CurrentThread.GetHashCode();
|
||||
#if !SILVERLIGHT && !NETCF
|
||||
Console.ForegroundColor = (ConsoleColor) (tid % 8 + 10);
|
||||
#endif
|
||||
Console.Write("{0:000} PBOS ", tid);
|
||||
Console.WriteLine(format, varParams);
|
||||
#if !SILVERLIGHT && !NETCF
|
||||
Console.ResetColor();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
// BitWriter.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-July-25 18:57:31>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the BitWriter class, which writes bits at a time
|
||||
// to an output stream. It's used by the BZip2Compressor class, and by
|
||||
// the BZip2OutputStream class and its parallel variant,
|
||||
// ParallelBZip2OutputStream.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Design notes:
|
||||
//
|
||||
// BZip2 employs byte-shredding in its data format - rather than
|
||||
// aligning all data items in a compressed .bz2 file on byte barriers,
|
||||
// the BZip2 format uses portions of bytes to represent independent
|
||||
// pieces of information. This "shredding" starts with the first
|
||||
// "randomised" bit - just 12 bytes or so into a bz2 file or stream. But
|
||||
// the approach is used extensively in bzip2 files - sometimes 5 bits
|
||||
// are used, sometimes 24 or 3 bits, sometimes just 1 bit, and so on.
|
||||
// It's not possible to send this information directly to a stream in
|
||||
// this form; Streams in .NET accept byte-oriented input. Therefore,
|
||||
// when actually writing a bz2 file, the output data must be organized
|
||||
// into a byte-aligned format before being written to the output stream.
|
||||
//
|
||||
// This BitWriter class provides the byte-shredding necessary for BZip2
|
||||
// output. Think of this class as an Adapter that enables Bit-oriented
|
||||
// output to a standard byte-oriented .NET stream. This class writes
|
||||
// data out to the captive output stream only after the data bits have
|
||||
// been accumulated and aligned. For example, suppose that during
|
||||
// operation, the BZip2 compressor emits 5 bits, then 24 bits, then 32
|
||||
// bits. When the first 5 bits are sent to the BitWriter, nothing is
|
||||
// written to the output stream; instead these 5 bits are simply stored
|
||||
// in the internal accumulator. When the next 24 bits are written, the
|
||||
// first 3 bits are gathered with the accumulated bits. The resulting
|
||||
// 5+3 constitutes an entire byte; the BitWriter then actually writes
|
||||
// that byte to the output stream. This leaves 21 bits. BitWriter writes
|
||||
// 2 more whole bytes (16 more bits), in 8-bit chunks, leaving 5 in the
|
||||
// accumulator. BitWriter then follows the same procedure with the 32
|
||||
// new bits. And so on.
|
||||
//
|
||||
// A quick tour of the implementation:
|
||||
//
|
||||
// The accumulator is a uint - so it can accumulate at most 4 bytes of
|
||||
// information. In practice because of the design of this class, it
|
||||
// never accumulates more than 3 bytes.
|
||||
//
|
||||
// The Flush() method emits all whole bytes available. After calling
|
||||
// Flush(), there may be between 0-7 bits yet to be emitted into the
|
||||
// output stream.
|
||||
//
|
||||
// FinishAndPad() emits all data, including the last partial byte and
|
||||
// any necessary padding. In effect, it establishes a byte-alignment
|
||||
// barrier. To support bzip2, FinishAndPad() should be called only once
|
||||
// for a bz2 file, after the last bit of data has been written through
|
||||
// this adapter. Other binary file formats may use byte-alignment at
|
||||
// various points within the file, and FinishAndPad() would support that
|
||||
// scenario.
|
||||
//
|
||||
// The internal fn Reset() is used to reset the state of the adapter;
|
||||
// this class is used by BZip2Compressor, instances of which get re-used
|
||||
// by multiple distinct threads, for different blocks of data.
|
||||
//
|
||||
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Ionic.BZip2
|
||||
{
|
||||
|
||||
internal class BitWriter
|
||||
{
|
||||
uint accumulator;
|
||||
int nAccumulatedBits;
|
||||
Stream output;
|
||||
int totalBytesWrittenOut;
|
||||
|
||||
public BitWriter(Stream s)
|
||||
{
|
||||
this.output = s;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delivers the remaining bits, left-aligned, in a byte.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This is valid only if NumRemainingBits is less than 8;
|
||||
/// in other words it is valid only after a call to Flush().
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public byte RemainingBits
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte) (this.accumulator >> (32 - this.nAccumulatedBits) & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
public int NumRemainingBits
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.nAccumulatedBits;
|
||||
}
|
||||
}
|
||||
|
||||
public int TotalBytesWrittenOut
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.totalBytesWrittenOut;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the BitWriter.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This is useful when the BitWriter writes into a MemoryStream, and
|
||||
/// is used by a BZip2Compressor, which itself is re-used for multiple
|
||||
/// distinct data blocks.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void Reset()
|
||||
{
|
||||
this.accumulator = 0;
|
||||
this.nAccumulatedBits = 0;
|
||||
this.totalBytesWrittenOut = 0;
|
||||
this.output.Seek(0, SeekOrigin.Begin);
|
||||
this.output.SetLength(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write some number of bits from the given value, into the output.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The nbits value should be a max of 25, for safety. For performance
|
||||
/// reasons, this method does not check!
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void WriteBits(int nbits, uint value)
|
||||
{
|
||||
int nAccumulated = this.nAccumulatedBits;
|
||||
uint u = this.accumulator;
|
||||
|
||||
while (nAccumulated >= 8)
|
||||
{
|
||||
this.output.WriteByte ((byte)(u >> 24 & 0xff));
|
||||
this.totalBytesWrittenOut++;
|
||||
u <<= 8;
|
||||
nAccumulated -= 8;
|
||||
}
|
||||
|
||||
this.accumulator = u | (value << (32 - nAccumulated - nbits));
|
||||
this.nAccumulatedBits = nAccumulated + nbits;
|
||||
|
||||
// Console.WriteLine("WriteBits({0}, 0x{1:X2}) => {2:X8} n({3})",
|
||||
// nbits, value, accumulator, nAccumulatedBits);
|
||||
// Console.ReadLine();
|
||||
|
||||
// At this point the accumulator may contain up to 31 bits waiting for
|
||||
// output.
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Write a full 8-bit byte into the output.
|
||||
/// </summary>
|
||||
public void WriteByte(byte b)
|
||||
{
|
||||
WriteBits(8, b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write four 8-bit bytes into the output.
|
||||
/// </summary>
|
||||
public void WriteInt(uint u)
|
||||
{
|
||||
WriteBits(8, (u >> 24) & 0xff);
|
||||
WriteBits(8, (u >> 16) & 0xff);
|
||||
WriteBits(8, (u >> 8) & 0xff);
|
||||
WriteBits(8, u & 0xff);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write all available byte-aligned bytes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method writes no new output, but flushes any accumulated
|
||||
/// bits. At completion, the accumulator may contain up to 7
|
||||
/// bits.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This is necessary when re-assembling output from N independent
|
||||
/// compressors, one for each of N blocks. The output of any
|
||||
/// particular compressor will in general have some fragment of a byte
|
||||
/// remaining. This fragment needs to be accumulated into the
|
||||
/// parent BZip2OutputStream.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void Flush()
|
||||
{
|
||||
WriteBits(0,0);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Writes all available bytes, and emits padding for the final byte as
|
||||
/// necessary. This must be the last method invoked on an instance of
|
||||
/// BitWriter.
|
||||
/// </summary>
|
||||
public void FinishAndPad()
|
||||
{
|
||||
Flush();
|
||||
|
||||
if (this.NumRemainingBits > 0)
|
||||
{
|
||||
byte b = (byte)((this.accumulator >> 24) & 0xff);
|
||||
this.output.WriteByte(b);
|
||||
this.totalBytesWrittenOut++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,999 @@
|
||||
//#define Trace
|
||||
|
||||
// ParallelBZip2OutputStream.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-August-02 16:44:24>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the ParallelBZip2OutputStream class, which is a
|
||||
// BZip2 compressing stream. This code was derived in part from Apache
|
||||
// commons source code. The license below applies to the original Apache
|
||||
// code.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
// flymake: csc.exe /t:module BZip2InputStream.cs BZip2Compressor.cs Rand.cs BCRC32.cs @@FILE@@
|
||||
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
|
||||
// Design Notes:
|
||||
//
|
||||
// This class follows the classic Decorator pattern: it is a Stream that
|
||||
// wraps itself around a Stream, and in doing so provides bzip2
|
||||
// compression as callers Write into it. It is exactly the same in
|
||||
// outward function as the BZip2OutputStream, except that this class can
|
||||
// perform compression using multiple independent threads. Because of
|
||||
// that, and because of the CPU-intensive nature of BZip2 compression,
|
||||
// this class can perform significantly better (in terms of wall-click
|
||||
// time) than the single-threaded variant, at the expense of memory and
|
||||
// CPU utilization.
|
||||
//
|
||||
// BZip2 is a straightforward data format: there are 4 magic bytes at
|
||||
// the top of the file, followed by 1 or more compressed blocks. There
|
||||
// is a small "magic byte" trailer after all compressed blocks.
|
||||
//
|
||||
// In concept parallelizing BZip2 is simple: do the CPU-intensive
|
||||
// compression for each block in a separate thread, then emit the
|
||||
// compressed output, in order, to the output stream. Each block can be
|
||||
// compressed independently, so a block is the natural candidate for the
|
||||
// parcel of work that can be passed to an independent worker thread.
|
||||
//
|
||||
// The design approach used here is simple: within the Write() method of
|
||||
// the stream, fill a block. When the block is full, pass it to a
|
||||
// background worker thread for compression. When the compressor thread
|
||||
// completes its work, the main thread (the application thread that
|
||||
// calls Write()) can send the compressed data to the output stream,
|
||||
// being careful to respect the order of the compressed blocks.
|
||||
//
|
||||
// The challenge of ordering the compressed data is a solved and
|
||||
// well-understood problem - it is the same approach here as DotNetZip
|
||||
// uses in the ParallelDeflateOutputStream. It is a map/reduce approach
|
||||
// in design intent.
|
||||
//
|
||||
// One new twist for BZip2 is that the compressor output is not
|
||||
// byte-aligned. In other words the final output of a compressed block
|
||||
// will in general be a number of bits that is not a multiple of
|
||||
// 8. Therefore, combining the ordered results of the N compressor
|
||||
// threads requires additional byte-shredding by the parent
|
||||
// stream. Hence this stream uses a BitWriter to adapt bit-oriented
|
||||
// BZip2 output to the byte-oriented .NET Stream.
|
||||
//
|
||||
// The approach used here creates N instances of the BZip2Compressor
|
||||
// type, where N is governed by the number of cores (cpus) and limited
|
||||
// by the MaxWorkers property exposed by this class. Each
|
||||
// BZip2Compressor instance gets its own MemoryStream, to which it
|
||||
// writes its data, via a BitWriter.
|
||||
//
|
||||
// along with the bit accumulator described above. The MemoryStream
|
||||
// would gather the byte-aligned compressed output of the compressor.
|
||||
|
||||
// When reducing the output of the various workers, this class must
|
||||
// again do the byte-shredding thing. The data from the compressors is
|
||||
// therefore shredded twice: once when being placed into the
|
||||
// MemoryStream, and again when emitted into the final output stream
|
||||
// that this class decorates. This is an unfortunate and seemingly
|
||||
// unavoidable inefficiency. Two rounds of byte-shredding will use more
|
||||
// CPU than we'd like, but I haven't imagined a way to avoid it.
|
||||
//
|
||||
// The BZip2Compressor is designed to write directly into the parent
|
||||
// stream's accumulator (BitWriter) when possible, and write into a
|
||||
// distinct BitWriter when necessary. The former can be used in a
|
||||
// single-thread scenario, while the latter is required in a
|
||||
// multi-thread scenario.
|
||||
//
|
||||
// ----
|
||||
//
|
||||
// Regarding the Apache code base: Most of the code in this particular
|
||||
// class is related to stream operations and thread synchronization, and
|
||||
// is my own code. It largely does not rely on any code obtained from
|
||||
// Apache commons. If you compare this code with the Apache commons
|
||||
// BZip2OutputStream, you will see very little code that is common,
|
||||
// except for the nearly-boilerplate structure that is common to all
|
||||
// subtypes of System.IO.Stream. There may be some small remnants of
|
||||
// code in this module derived from the Apache stuff, which is why I
|
||||
// left the license in here. Most of the Apache commons compressor magic
|
||||
// has been ported into the BZip2Compressor class.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ionic.BZip2
|
||||
{
|
||||
internal class WorkItem
|
||||
{
|
||||
public int index;
|
||||
public BZip2Compressor Compressor { get; private set; }
|
||||
public MemoryStream ms;
|
||||
public int ordinal;
|
||||
public BitWriter bw;
|
||||
|
||||
public WorkItem(int ix, int blockSize)
|
||||
{
|
||||
// compressed data gets written to a MemoryStream
|
||||
this.ms = new MemoryStream();
|
||||
this.bw = new BitWriter(ms);
|
||||
this.Compressor = new BZip2Compressor(bw, blockSize);
|
||||
this.index = ix;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A write-only decorator stream that compresses data as it is
|
||||
/// written using the BZip2 algorithm. This stream compresses by
|
||||
/// block using multiple threads.
|
||||
/// </summary>
|
||||
/// <para>
|
||||
/// This class performs BZIP2 compression through writing. For
|
||||
/// more information on the BZIP2 algorithm, see
|
||||
/// <see href="http://en.wikipedia.org/wiki/BZIP2"/>.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This class is similar to <see cref="Ionic.BZip2.BZip2OutputStream"/>,
|
||||
/// except that this implementation uses an approach that employs multiple
|
||||
/// worker threads to perform the compression. On a multi-cpu or multi-core
|
||||
/// computer, the performance of this class can be significantly higher than
|
||||
/// the single-threaded BZip2OutputStream, particularly for larger streams.
|
||||
/// How large? Anything over 10mb is a good candidate for parallel
|
||||
/// compression.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The tradeoff is that this class uses more memory and more CPU than the
|
||||
/// vanilla <c>BZip2OutputStream</c>. Also, for small files, the
|
||||
/// <c>ParallelBZip2OutputStream</c> can be much slower than the vanilla
|
||||
/// <c>BZip2OutputStream</c>, because of the overhead associated to using the
|
||||
/// thread pool.
|
||||
/// </para>
|
||||
///
|
||||
/// <seealso cref="Ionic.BZip2.BZip2OutputStream" />
|
||||
public class ParallelBZip2OutputStream : System.IO.Stream
|
||||
{
|
||||
private static readonly int BufferPairsPerCore = 4;
|
||||
private int _maxWorkers;
|
||||
private bool firstWriteDone;
|
||||
private int lastFilled;
|
||||
private int lastWritten;
|
||||
private int latestCompressed;
|
||||
private int currentlyFilling;
|
||||
private volatile Exception pendingException;
|
||||
private bool handlingException;
|
||||
private bool emitting;
|
||||
private System.Collections.Generic.Queue<int> toWrite;
|
||||
private System.Collections.Generic.Queue<int> toFill;
|
||||
private System.Collections.Generic.List<WorkItem> pool;
|
||||
private object latestLock = new object();
|
||||
private object eLock = new object(); // for exceptions
|
||||
private object outputLock = new object(); // for multi-thread output
|
||||
private AutoResetEvent newlyCompressedBlob;
|
||||
|
||||
long totalBytesWrittenIn;
|
||||
long totalBytesWrittenOut;
|
||||
bool leaveOpen;
|
||||
uint combinedCRC;
|
||||
Stream output;
|
||||
BitWriter bw;
|
||||
int blockSize100k; // 0...9
|
||||
|
||||
private TraceBits desiredTrace = TraceBits.Crc | TraceBits.Write;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <c>ParallelBZip2OutputStream</c>, that sends its
|
||||
/// compressed output to the given output stream.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name='output'>
|
||||
/// The destination stream, to which compressed output will be sent.
|
||||
/// </param>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example reads a file, then compresses it with bzip2 file,
|
||||
/// and writes the compressed data into a newly created file.
|
||||
///
|
||||
/// <code>
|
||||
/// var fname = "logfile.log";
|
||||
/// using (var fs = File.OpenRead(fname))
|
||||
/// {
|
||||
/// var outFname = fname + ".bz2";
|
||||
/// using (var output = File.Create(outFname))
|
||||
/// {
|
||||
/// using (var compressor = new Ionic.BZip2.ParallelBZip2OutputStream(output))
|
||||
/// {
|
||||
/// byte[] buffer = new byte[2048];
|
||||
/// int n;
|
||||
/// while ((n = fs.Read(buffer, 0, buffer.Length)) > 0)
|
||||
/// {
|
||||
/// compressor.Write(buffer, 0, n);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public ParallelBZip2OutputStream(Stream output)
|
||||
: this(output, BZip2.MaxBlockSize, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <c>ParallelBZip2OutputStream</c> with specified blocksize.
|
||||
/// </summary>
|
||||
/// <param name = "output">the destination stream.</param>
|
||||
/// <param name = "blockSize">
|
||||
/// The blockSize in units of 100000 bytes.
|
||||
/// The valid range is 1..9.
|
||||
/// </param>
|
||||
public ParallelBZip2OutputStream(Stream output, int blockSize)
|
||||
: this(output, blockSize, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <c>ParallelBZip2OutputStream</c>.
|
||||
/// </summary>
|
||||
/// <param name = "output">the destination stream.</param>
|
||||
/// <param name = "leaveOpen">
|
||||
/// whether to leave the captive stream open upon closing this stream.
|
||||
/// </param>
|
||||
public ParallelBZip2OutputStream(Stream output, bool leaveOpen)
|
||||
: this(output, BZip2.MaxBlockSize, leaveOpen)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <c>ParallelBZip2OutputStream</c> with specified blocksize,
|
||||
/// and explicitly specifies whether to leave the wrapped stream open.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name = "output">the destination stream.</param>
|
||||
/// <param name = "blockSize">
|
||||
/// The blockSize in units of 100000 bytes.
|
||||
/// The valid range is 1..9.
|
||||
/// </param>
|
||||
/// <param name = "leaveOpen">
|
||||
/// whether to leave the captive stream open upon closing this stream.
|
||||
/// </param>
|
||||
public ParallelBZip2OutputStream(Stream output, int blockSize, bool leaveOpen)
|
||||
{
|
||||
if (blockSize < BZip2.MinBlockSize || blockSize > BZip2.MaxBlockSize)
|
||||
{
|
||||
var msg = String.Format("blockSize={0} is out of range; must be between {1} and {2}",
|
||||
blockSize,
|
||||
BZip2.MinBlockSize, BZip2.MaxBlockSize);
|
||||
throw new ArgumentException(msg, "blockSize");
|
||||
}
|
||||
|
||||
this.output = output;
|
||||
if (!this.output.CanWrite)
|
||||
throw new ArgumentException("The stream is not writable.", "output");
|
||||
|
||||
this.bw = new BitWriter(this.output);
|
||||
this.blockSize100k = blockSize;
|
||||
this.leaveOpen = leaveOpen;
|
||||
this.combinedCRC = 0;
|
||||
this.MaxWorkers = 16; // default
|
||||
EmitHeader();
|
||||
}
|
||||
|
||||
|
||||
private void InitializePoolOfWorkItems()
|
||||
{
|
||||
this.toWrite = new Queue<int>();
|
||||
this.toFill = new Queue<int>();
|
||||
this.pool = new System.Collections.Generic.List<WorkItem>();
|
||||
int nWorkers = BufferPairsPerCore * Environment.ProcessorCount;
|
||||
nWorkers = Math.Min(nWorkers, this.MaxWorkers);
|
||||
for(int i=0; i < nWorkers; i++)
|
||||
{
|
||||
this.pool.Add(new WorkItem(i, this.blockSize100k));
|
||||
this.toFill.Enqueue(i);
|
||||
}
|
||||
|
||||
this.newlyCompressedBlob = new AutoResetEvent(false);
|
||||
this.currentlyFilling = -1;
|
||||
this.lastFilled = -1;
|
||||
this.lastWritten = -1;
|
||||
this.latestCompressed = -1;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of concurrent compression worker threads to use.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This property sets an upper limit on the number of concurrent worker
|
||||
/// threads to employ for compression. The implementation of this stream
|
||||
/// employs multiple threads from the .NET thread pool, via <see
|
||||
/// cref="System.Threading.ThreadPool.QueueUserWorkItem(WaitCallback)">
|
||||
/// ThreadPool.QueueUserWorkItem()</see>, to compress the incoming data by
|
||||
/// block. As each block of data is compressed, this stream re-orders the
|
||||
/// compressed blocks and writes them to the output stream.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A higher number of workers enables a higher degree of
|
||||
/// parallelism, which tends to increase the speed of compression on
|
||||
/// multi-cpu computers. On the other hand, a higher number of buffer
|
||||
/// pairs also implies a larger memory consumption, more active worker
|
||||
/// threads, and a higher cpu utilization for any compression. This
|
||||
/// property enables the application to limit its memory consumption and
|
||||
/// CPU utilization behavior depending on requirements.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// By default, DotNetZip allocates 4 workers per CPU core, subject to the
|
||||
/// upper limit specified in this property. For example, suppose the
|
||||
/// application sets this property to 16. Then, on a machine with 2
|
||||
/// cores, DotNetZip will use 8 workers; that number does not exceed the
|
||||
/// upper limit specified by this property, so the actual number of
|
||||
/// workers used will be 4 * 2 = 8. On a machine with 4 cores, DotNetZip
|
||||
/// will use 16 workers; again, the limit does not apply. On a machine
|
||||
/// with 8 cores, DotNetZip will use 16 workers, because of the limit.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// For each compression "worker thread" that occurs in parallel, there is
|
||||
/// up to 2mb of memory allocated, for buffering and processing. The
|
||||
/// actual number depends on the <see cref="BlockSize"/> property.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// CPU utilization will also go up with additional workers, because a
|
||||
/// larger number of buffer pairs allows a larger number of background
|
||||
/// threads to compress in parallel. If you find that parallel
|
||||
/// compression is consuming too much memory or CPU, you can adjust this
|
||||
/// value downward.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The default value is 16. Different values may deliver better or
|
||||
/// worse results, depending on your priorities and the dynamic
|
||||
/// performance characteristics of your storage and compute resources.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The application can set this value at any time, but it is effective
|
||||
/// only before the first call to Write(), which is when the buffers are
|
||||
/// allocated.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public int MaxWorkers
|
||||
{
|
||||
get
|
||||
{
|
||||
return _maxWorkers;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value < 4)
|
||||
throw new ArgumentException("MaxWorkers",
|
||||
"Value must be 4 or greater.");
|
||||
_maxWorkers = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close the stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This may or may not close the underlying stream. Check the
|
||||
/// constructors that accept a bool value.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public override void Close()
|
||||
{
|
||||
if (this.pendingException != null)
|
||||
{
|
||||
this.handlingException = true;
|
||||
var pe = this.pendingException;
|
||||
this.pendingException = null;
|
||||
throw pe;
|
||||
}
|
||||
|
||||
if (this.handlingException)
|
||||
return;
|
||||
|
||||
if (output == null)
|
||||
return;
|
||||
|
||||
Stream o = this.output;
|
||||
|
||||
try
|
||||
{
|
||||
FlushOutput(true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.output = null;
|
||||
this.bw = null;
|
||||
}
|
||||
|
||||
if (!leaveOpen)
|
||||
o.Close();
|
||||
}
|
||||
|
||||
|
||||
private void FlushOutput(bool lastInput)
|
||||
{
|
||||
if (this.emitting) return;
|
||||
|
||||
// compress and write whatever is ready
|
||||
if (this.currentlyFilling >= 0)
|
||||
{
|
||||
WorkItem workitem = this.pool[this.currentlyFilling];
|
||||
CompressOne(workitem);
|
||||
this.currentlyFilling = -1; // get a new buffer next Write()
|
||||
}
|
||||
|
||||
if (lastInput)
|
||||
{
|
||||
EmitPendingBuffers(true, false);
|
||||
EmitTrailer();
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitPendingBuffers(false, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Flush the stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
if (this.output != null)
|
||||
{
|
||||
FlushOutput(false);
|
||||
this.bw.Flush();
|
||||
this.output.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitHeader()
|
||||
{
|
||||
var magic = new byte[] {
|
||||
(byte) 'B',
|
||||
(byte) 'Z',
|
||||
(byte) 'h',
|
||||
(byte) ('0' + this.blockSize100k)
|
||||
};
|
||||
|
||||
// not necessary to shred the initial magic bytes
|
||||
this.output.Write(magic, 0, magic.Length);
|
||||
}
|
||||
|
||||
private void EmitTrailer()
|
||||
{
|
||||
// A magic 48-bit number, 0x177245385090, to indicate the end
|
||||
// of the last block. (sqrt(pi), if you want to know)
|
||||
|
||||
TraceOutput(TraceBits.Write, "total written out: {0} (0x{0:X})",
|
||||
this.bw.TotalBytesWrittenOut);
|
||||
|
||||
// must shred
|
||||
this.bw.WriteByte(0x17);
|
||||
this.bw.WriteByte(0x72);
|
||||
this.bw.WriteByte(0x45);
|
||||
this.bw.WriteByte(0x38);
|
||||
this.bw.WriteByte(0x50);
|
||||
this.bw.WriteByte(0x90);
|
||||
|
||||
this.bw.WriteInt(this.combinedCRC);
|
||||
|
||||
this.bw.FinishAndPad();
|
||||
|
||||
TraceOutput(TraceBits.Write, "final total : {0} (0x{0:X})",
|
||||
this.bw.TotalBytesWrittenOut);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The blocksize parameter specified at construction time.
|
||||
/// </summary>
|
||||
public int BlockSize
|
||||
{
|
||||
get { return this.blockSize100k; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Write data to the stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// Use the <c>ParallelBZip2OutputStream</c> to compress data while
|
||||
/// writing: create a <c>ParallelBZip2OutputStream</c> with a writable
|
||||
/// output stream. Then call <c>Write()</c> on that
|
||||
/// <c>ParallelBZip2OutputStream</c>, providing uncompressed data as
|
||||
/// input. The data sent to the output stream will be the compressed
|
||||
/// form of the input data.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A <c>ParallelBZip2OutputStream</c> can be used only for
|
||||
/// <c>Write()</c> not for <c>Read()</c>.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="buffer">The buffer holding data to write to the stream.</param>
|
||||
/// <param name="offset">the offset within that data array to find the first byte to write.</param>
|
||||
/// <param name="count">the number of bytes to write.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
bool mustWait = false;
|
||||
|
||||
// This method does this:
|
||||
// 0. handles any pending exceptions
|
||||
// 1. write any buffers that are ready to be written
|
||||
// 2. fills a compressor buffer; when full, flip state to 'Filled',
|
||||
// 3. if more data to be written, goto step 1
|
||||
|
||||
if (this.output == null)
|
||||
throw new IOException("the stream is not open");
|
||||
|
||||
// dispense any exceptions that occurred on the BG threads
|
||||
if (this.pendingException != null)
|
||||
{
|
||||
this.handlingException = true;
|
||||
var pe = this.pendingException;
|
||||
this.pendingException = null;
|
||||
throw pe;
|
||||
}
|
||||
|
||||
if (offset < 0)
|
||||
throw new IndexOutOfRangeException(String.Format("offset ({0}) must be > 0", offset));
|
||||
if (count < 0)
|
||||
throw new IndexOutOfRangeException(String.Format("count ({0}) must be > 0", count));
|
||||
if (offset + count > buffer.Length)
|
||||
throw new IndexOutOfRangeException(String.Format("offset({0}) count({1}) bLength({2})",
|
||||
offset, count, buffer.Length));
|
||||
|
||||
|
||||
if (count == 0) return; // nothing to do
|
||||
|
||||
|
||||
if (!this.firstWriteDone)
|
||||
{
|
||||
// Want to do this on first Write, first session, and not in the
|
||||
// constructor. Must allow the MaxWorkers to change after
|
||||
// construction, but before first Write().
|
||||
InitializePoolOfWorkItems();
|
||||
this.firstWriteDone = true;
|
||||
}
|
||||
|
||||
int bytesWritten = 0;
|
||||
int bytesRemaining = count;
|
||||
|
||||
do
|
||||
{
|
||||
// may need to make buffers available
|
||||
EmitPendingBuffers(false, mustWait);
|
||||
|
||||
mustWait = false;
|
||||
|
||||
// get a compressor to fill
|
||||
int ix = -1;
|
||||
if (this.currentlyFilling >= 0)
|
||||
{
|
||||
ix = this.currentlyFilling;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.toFill.Count == 0)
|
||||
{
|
||||
// No compressors available to fill, so... need to emit
|
||||
// compressed buffers.
|
||||
mustWait = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
ix = this.toFill.Dequeue();
|
||||
++this.lastFilled;
|
||||
}
|
||||
|
||||
WorkItem workitem = this.pool[ix];
|
||||
workitem.ordinal = this.lastFilled;
|
||||
|
||||
int n = workitem.Compressor.Fill(buffer, offset, bytesRemaining);
|
||||
if (n != bytesRemaining)
|
||||
{
|
||||
if (!ThreadPool.QueueUserWorkItem( CompressOne, workitem ))
|
||||
throw new Exception("Cannot enqueue workitem");
|
||||
|
||||
this.currentlyFilling = -1; // will get a new buffer next time
|
||||
offset += n;
|
||||
}
|
||||
else
|
||||
this.currentlyFilling = ix;
|
||||
|
||||
bytesRemaining -= n;
|
||||
bytesWritten += n;
|
||||
}
|
||||
while (bytesRemaining > 0);
|
||||
|
||||
totalBytesWrittenIn += bytesWritten;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void EmitPendingBuffers(bool doAll, bool mustWait)
|
||||
{
|
||||
// When combining parallel compression with a ZipSegmentedStream, it's
|
||||
// possible for the ZSS to throw from within this method. In that
|
||||
// case, Close/Dispose will be called on this stream, if this stream
|
||||
// is employed within a using or try/finally pair as required. But
|
||||
// this stream is unaware of the pending exception, so the Close()
|
||||
// method invokes this method AGAIN. This can lead to a deadlock.
|
||||
// Therefore, failfast if re-entering.
|
||||
|
||||
if (emitting) return;
|
||||
emitting = true;
|
||||
|
||||
if (doAll || mustWait)
|
||||
this.newlyCompressedBlob.WaitOne();
|
||||
|
||||
do
|
||||
{
|
||||
int firstSkip = -1;
|
||||
int millisecondsToWait = doAll ? 200 : (mustWait ? -1 : 0);
|
||||
int nextToWrite = -1;
|
||||
|
||||
do
|
||||
{
|
||||
if (Monitor.TryEnter(this.toWrite, millisecondsToWait))
|
||||
{
|
||||
nextToWrite = -1;
|
||||
try
|
||||
{
|
||||
if (this.toWrite.Count > 0)
|
||||
nextToWrite = this.toWrite.Dequeue();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Monitor.Exit(this.toWrite);
|
||||
}
|
||||
|
||||
if (nextToWrite >= 0)
|
||||
{
|
||||
WorkItem workitem = this.pool[nextToWrite];
|
||||
if (workitem.ordinal != this.lastWritten + 1)
|
||||
{
|
||||
// out of order. requeue and try again.
|
||||
lock(this.toWrite)
|
||||
{
|
||||
this.toWrite.Enqueue(nextToWrite);
|
||||
}
|
||||
|
||||
if (firstSkip == nextToWrite)
|
||||
{
|
||||
// We went around the list once.
|
||||
// None of the items in the list is the one we want.
|
||||
// Now wait for a compressor to signal again.
|
||||
this.newlyCompressedBlob.WaitOne();
|
||||
firstSkip = -1;
|
||||
}
|
||||
else if (firstSkip == -1)
|
||||
firstSkip = nextToWrite;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
firstSkip = -1;
|
||||
|
||||
TraceOutput(TraceBits.Write,
|
||||
"Writing block {0}", workitem.ordinal);
|
||||
|
||||
// write the data to the output
|
||||
var bw2 = workitem.bw;
|
||||
bw2.Flush(); // not bw2.FinishAndPad()!
|
||||
var ms = workitem.ms;
|
||||
ms.Seek(0,SeekOrigin.Begin);
|
||||
|
||||
// cannot dump bytes!!
|
||||
// ms.WriteTo(this.output);
|
||||
//
|
||||
// must do byte shredding:
|
||||
int n;
|
||||
int y = -1;
|
||||
long totOut = 0;
|
||||
var buffer = new byte[1024];
|
||||
while ((n = ms.Read(buffer,0,buffer.Length)) > 0)
|
||||
{
|
||||
#if Trace
|
||||
if (y == -1) // diagnostics only
|
||||
{
|
||||
var sb1 = new System.Text.StringBuilder();
|
||||
sb1.Append("first 16 whole bytes in block: ");
|
||||
for (int z=0; z < 16; z++)
|
||||
sb1.Append(String.Format(" {0:X2}", buffer[z]));
|
||||
TraceOutput(TraceBits.Write, sb1.ToString());
|
||||
}
|
||||
#endif
|
||||
y = n;
|
||||
for (int k=0; k < n; k++)
|
||||
{
|
||||
this.bw.WriteByte(buffer[k]);
|
||||
}
|
||||
totOut += n;
|
||||
}
|
||||
#if Trace
|
||||
TraceOutput(TraceBits.Write,"out block length (bytes): {0} (0x{0:X})", totOut);
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.Append("final 16 whole bytes in block: ");
|
||||
for (int z=0; z < 16; z++)
|
||||
sb.Append(String.Format(" {0:X2}", buffer[y-1-12+z]));
|
||||
TraceOutput(TraceBits.Write, sb.ToString());
|
||||
#endif
|
||||
|
||||
// and now any remaining bits
|
||||
TraceOutput(TraceBits.Write,
|
||||
" remaining bits: {0} 0x{1:X}",
|
||||
bw2.NumRemainingBits,
|
||||
bw2.RemainingBits);
|
||||
if (bw2.NumRemainingBits > 0)
|
||||
{
|
||||
this.bw.WriteBits(bw2.NumRemainingBits, bw2.RemainingBits);
|
||||
}
|
||||
|
||||
TraceOutput(TraceBits.Crc," combined CRC (before): {0:X8}",
|
||||
this.combinedCRC);
|
||||
this.combinedCRC = (this.combinedCRC << 1) | (this.combinedCRC >> 31);
|
||||
this.combinedCRC ^= (uint) workitem.Compressor.Crc32;
|
||||
|
||||
TraceOutput(TraceBits.Crc,
|
||||
" block CRC : {0:X8}",
|
||||
workitem.Compressor.Crc32);
|
||||
TraceOutput(TraceBits.Crc,
|
||||
" combined CRC (after) : {0:X8}",
|
||||
this.combinedCRC);
|
||||
TraceOutput(TraceBits.Write,
|
||||
"total written out: {0} (0x{0:X})",
|
||||
this.bw.TotalBytesWrittenOut);
|
||||
TraceOutput(TraceBits.Write | TraceBits.Crc, "");
|
||||
|
||||
this.totalBytesWrittenOut += totOut;
|
||||
|
||||
bw2.Reset();
|
||||
this.lastWritten = workitem.ordinal;
|
||||
workitem.ordinal = -1;
|
||||
this.toFill.Enqueue(workitem.index);
|
||||
|
||||
// don't wait next time through
|
||||
if (millisecondsToWait == -1) millisecondsToWait = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
nextToWrite = -1;
|
||||
|
||||
} while (nextToWrite >= 0);
|
||||
|
||||
} while (doAll && (this.lastWritten != this.latestCompressed));
|
||||
|
||||
if (doAll)
|
||||
{
|
||||
TraceOutput(TraceBits.Crc,
|
||||
" combined CRC (final) : {0:X8}", this.combinedCRC);
|
||||
}
|
||||
|
||||
emitting = false;
|
||||
}
|
||||
|
||||
|
||||
private void CompressOne(Object wi)
|
||||
{
|
||||
// compress and one buffer
|
||||
WorkItem workitem = (WorkItem) wi;
|
||||
try
|
||||
{
|
||||
// compress and write to the compressor's MemoryStream
|
||||
workitem.Compressor.CompressAndWrite();
|
||||
|
||||
lock(this.latestLock)
|
||||
{
|
||||
if (workitem.ordinal > this.latestCompressed)
|
||||
this.latestCompressed = workitem.ordinal;
|
||||
}
|
||||
lock (this.toWrite)
|
||||
{
|
||||
this.toWrite.Enqueue(workitem.index);
|
||||
}
|
||||
this.newlyCompressedBlob.Set();
|
||||
}
|
||||
catch (System.Exception exc1)
|
||||
{
|
||||
lock(this.eLock)
|
||||
{
|
||||
// expose the exception to the main thread
|
||||
if (this.pendingException!=null)
|
||||
this.pendingException = exc1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream can be read.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The return value is always false.
|
||||
/// </remarks>
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream supports Seek operations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Always returns false.
|
||||
/// </remarks>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream can be written.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The return value depends on whether the captive stream supports writing.
|
||||
/// </remarks>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.output == null) throw new ObjectDisposedException("BZip2Stream");
|
||||
return this.output.CanWrite;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reading this property always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The position of the stream pointer.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Setting this property always throws a <see
|
||||
/// cref="NotImplementedException"/>. Reading will return the
|
||||
/// total number of uncompressed bytes written through.
|
||||
/// </remarks>
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.totalBytesWrittenIn;
|
||||
}
|
||||
set { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The total number of bytes written out by the stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This value is meaningful only after a call to Close().
|
||||
/// </remarks>
|
||||
public Int64 BytesWrittenOut { get { return totalBytesWrittenOut; } }
|
||||
|
||||
/// <summary>
|
||||
/// Calling this method always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
/// <param name="offset">this is irrelevant, since it will always throw!</param>
|
||||
/// <param name="origin">this is irrelevant, since it will always throw!</param>
|
||||
/// <returns>irrelevant!</returns>
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calling this method always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">this is irrelevant, since it will always throw!</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calling this method always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
/// <param name='buffer'>this parameter is never used</param>
|
||||
/// <param name='offset'>this parameter is never used</param>
|
||||
/// <param name='count'>this parameter is never used</param>
|
||||
/// <returns>never returns anything; always throws</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
// used only when Trace is defined
|
||||
[Flags]
|
||||
enum TraceBits : uint
|
||||
{
|
||||
None = 0,
|
||||
Crc = 1,
|
||||
Write = 2,
|
||||
All = 0xffffffff,
|
||||
}
|
||||
|
||||
|
||||
[System.Diagnostics.ConditionalAttribute("Trace")]
|
||||
private void TraceOutput(TraceBits bits, string format, params object[] varParams)
|
||||
{
|
||||
if ((bits & this.desiredTrace) != 0)
|
||||
{
|
||||
lock(outputLock)
|
||||
{
|
||||
int tid = Thread.CurrentThread.GetHashCode();
|
||||
#if !SILVERLIGHT
|
||||
Console.ForegroundColor = (ConsoleColor) (tid % 8 + 10);
|
||||
#endif
|
||||
Console.Write("{0:000} PBOS ", tid);
|
||||
Console.WriteLine(format, varParams);
|
||||
#if !SILVERLIGHT
|
||||
Console.ResetColor();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
// Rand.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-July-31 15:09:16>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines a helper class for the BZip2 classes. This code
|
||||
// is derived from the original BZip2 source code.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace Ionic.BZip2
|
||||
{
|
||||
internal static class Rand
|
||||
{
|
||||
private static int[] RNUMS =
|
||||
{
|
||||
619, 720, 127, 481, 931, 816, 813, 233, 566, 247,
|
||||
985, 724, 205, 454, 863, 491, 741, 242, 949, 214,
|
||||
733, 859, 335, 708, 621, 574, 73, 654, 730, 472,
|
||||
419, 436, 278, 496, 867, 210, 399, 680, 480, 51,
|
||||
878, 465, 811, 169, 869, 675, 611, 697, 867, 561,
|
||||
862, 687, 507, 283, 482, 129, 807, 591, 733, 623,
|
||||
150, 238, 59, 379, 684, 877, 625, 169, 643, 105,
|
||||
170, 607, 520, 932, 727, 476, 693, 425, 174, 647,
|
||||
73, 122, 335, 530, 442, 853, 695, 249, 445, 515,
|
||||
909, 545, 703, 919, 874, 474, 882, 500, 594, 612,
|
||||
641, 801, 220, 162, 819, 984, 589, 513, 495, 799,
|
||||
161, 604, 958, 533, 221, 400, 386, 867, 600, 782,
|
||||
382, 596, 414, 171, 516, 375, 682, 485, 911, 276,
|
||||
98, 553, 163, 354, 666, 933, 424, 341, 533, 870,
|
||||
227, 730, 475, 186, 263, 647, 537, 686, 600, 224,
|
||||
469, 68, 770, 919, 190, 373, 294, 822, 808, 206,
|
||||
184, 943, 795, 384, 383, 461, 404, 758, 839, 887,
|
||||
715, 67, 618, 276, 204, 918, 873, 777, 604, 560,
|
||||
951, 160, 578, 722, 79, 804, 96, 409, 713, 940,
|
||||
652, 934, 970, 447, 318, 353, 859, 672, 112, 785,
|
||||
645, 863, 803, 350, 139, 93, 354, 99, 820, 908,
|
||||
609, 772, 154, 274, 580, 184, 79, 626, 630, 742,
|
||||
653, 282, 762, 623, 680, 81, 927, 626, 789, 125,
|
||||
411, 521, 938, 300, 821, 78, 343, 175, 128, 250,
|
||||
170, 774, 972, 275, 999, 639, 495, 78, 352, 126,
|
||||
857, 956, 358, 619, 580, 124, 737, 594, 701, 612,
|
||||
669, 112, 134, 694, 363, 992, 809, 743, 168, 974,
|
||||
944, 375, 748, 52, 600, 747, 642, 182, 862, 81,
|
||||
344, 805, 988, 739, 511, 655, 814, 334, 249, 515,
|
||||
897, 955, 664, 981, 649, 113, 974, 459, 893, 228,
|
||||
433, 837, 553, 268, 926, 240, 102, 654, 459, 51,
|
||||
686, 754, 806, 760, 493, 403, 415, 394, 687, 700,
|
||||
946, 670, 656, 610, 738, 392, 760, 799, 887, 653,
|
||||
978, 321, 576, 617, 626, 502, 894, 679, 243, 440,
|
||||
680, 879, 194, 572, 640, 724, 926, 56, 204, 700,
|
||||
707, 151, 457, 449, 797, 195, 791, 558, 945, 679,
|
||||
297, 59, 87, 824, 713, 663, 412, 693, 342, 606,
|
||||
134, 108, 571, 364, 631, 212, 174, 643, 304, 329,
|
||||
343, 97, 430, 751, 497, 314, 983, 374, 822, 928,
|
||||
140, 206, 73, 263, 980, 736, 876, 478, 430, 305,
|
||||
170, 514, 364, 692, 829, 82, 855, 953, 676, 246,
|
||||
369, 970, 294, 750, 807, 827, 150, 790, 288, 923,
|
||||
804, 378, 215, 828, 592, 281, 565, 555, 710, 82,
|
||||
896, 831, 547, 261, 524, 462, 293, 465, 502, 56,
|
||||
661, 821, 976, 991, 658, 869, 905, 758, 745, 193,
|
||||
768, 550, 608, 933, 378, 286, 215, 979, 792, 961,
|
||||
61, 688, 793, 644, 986, 403, 106, 366, 905, 644,
|
||||
372, 567, 466, 434, 645, 210, 389, 550, 919, 135,
|
||||
780, 773, 635, 389, 707, 100, 626, 958, 165, 504,
|
||||
920, 176, 193, 713, 857, 265, 203, 50, 668, 108,
|
||||
645, 990, 626, 197, 510, 357, 358, 850, 858, 364,
|
||||
936, 638
|
||||
};
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the "random" number at a specific index.
|
||||
/// </summary>
|
||||
/// <param name='i'>the index</param>
|
||||
/// <returns>the random number</returns>
|
||||
internal static int Rnums(int i)
|
||||
{
|
||||
return RNUMS[i];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,814 @@
|
||||
// CRC32.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-August-02 18:25:54>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the CRC32 class, which can do the CRC32 algorithm, using
|
||||
// arbitrary starting polynomials, and bit reversal. The bit reversal is what
|
||||
// distinguishes this CRC-32 used in BZip2 from the CRC-32 that is used in PKZIP
|
||||
// files, or GZIP files. This class does both.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
using System;
|
||||
using Interop = System.Runtime.InteropServices;
|
||||
|
||||
namespace Ionic.Crc
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes a CRC-32. The CRC-32 algorithm is parameterized - you
|
||||
/// can set the polynomial and enable or disable bit
|
||||
/// reversal. This can be used for GZIP, BZip2, or ZIP.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This type is used internally by DotNetZip; it is generally not used
|
||||
/// directly by applications wishing to create, read, or manipulate zip
|
||||
/// archive files.
|
||||
/// </remarks>
|
||||
|
||||
[Interop.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d0000C")]
|
||||
[Interop.ComVisible(true)]
|
||||
#if !NETCF
|
||||
[Interop.ClassInterface(Interop.ClassInterfaceType.AutoDispatch)]
|
||||
#endif
|
||||
public class CRC32
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates the total number of bytes applied to the CRC.
|
||||
/// </summary>
|
||||
public Int64 TotalBytesRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return _TotalBytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the current CRC for all blocks slurped in.
|
||||
/// </summary>
|
||||
public Int32 Crc32Result
|
||||
{
|
||||
get
|
||||
{
|
||||
return unchecked((Int32)(~_register));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the CRC32 for the specified stream.
|
||||
/// </summary>
|
||||
/// <param name="input">The stream over which to calculate the CRC32</param>
|
||||
/// <returns>the CRC32 calculation</returns>
|
||||
public Int32 GetCrc32(System.IO.Stream input)
|
||||
{
|
||||
return GetCrc32AndCopy(input, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the CRC32 for the specified stream, and writes the input into the
|
||||
/// output stream.
|
||||
/// </summary>
|
||||
/// <param name="input">The stream over which to calculate the CRC32</param>
|
||||
/// <param name="output">The stream into which to deflate the input</param>
|
||||
/// <returns>the CRC32 calculation</returns>
|
||||
public Int32 GetCrc32AndCopy(System.IO.Stream input, System.IO.Stream output)
|
||||
{
|
||||
if (input == null)
|
||||
throw new Exception("The input stream must not be null.");
|
||||
|
||||
unchecked
|
||||
{
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
int readSize = BUFFER_SIZE;
|
||||
|
||||
_TotalBytesRead = 0;
|
||||
int count = input.Read(buffer, 0, readSize);
|
||||
if (output != null) output.Write(buffer, 0, count);
|
||||
_TotalBytesRead += count;
|
||||
while (count > 0)
|
||||
{
|
||||
SlurpBlock(buffer, 0, count);
|
||||
count = input.Read(buffer, 0, readSize);
|
||||
if (output != null) output.Write(buffer, 0, count);
|
||||
_TotalBytesRead += count;
|
||||
}
|
||||
|
||||
return (Int32)(~_register);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get the CRC32 for the given (word,byte) combo. This is a
|
||||
/// computation defined by PKzip for PKZIP 2.0 (weak) encryption.
|
||||
/// </summary>
|
||||
/// <param name="W">The word to start with.</param>
|
||||
/// <param name="B">The byte to combine it with.</param>
|
||||
/// <returns>The CRC-ized result.</returns>
|
||||
public Int32 ComputeCrc32(Int32 W, byte B)
|
||||
{
|
||||
return _InternalComputeCrc32((UInt32)W, B);
|
||||
}
|
||||
|
||||
internal Int32 _InternalComputeCrc32(UInt32 W, byte B)
|
||||
{
|
||||
return (Int32)(crc32Table[(W ^ B) & 0xFF] ^ (W >> 8));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Update the value for the running CRC32 using the given block of bytes.
|
||||
/// This is useful when using the CRC32() class in a Stream.
|
||||
/// </summary>
|
||||
/// <param name="block">block of bytes to slurp</param>
|
||||
/// <param name="offset">starting point in the block</param>
|
||||
/// <param name="count">how many bytes within the block to slurp</param>
|
||||
public void SlurpBlock(byte[] block, int offset, int count)
|
||||
{
|
||||
if (block == null)
|
||||
throw new Exception("The data buffer must not be null.");
|
||||
|
||||
// bzip algorithm
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int x = offset + i;
|
||||
byte b = block[x];
|
||||
if (this.reverseBits)
|
||||
{
|
||||
UInt32 temp = (_register >> 24) ^ b;
|
||||
_register = (_register << 8) ^ crc32Table[temp];
|
||||
}
|
||||
else
|
||||
{
|
||||
UInt32 temp = (_register & 0x000000FF) ^ b;
|
||||
_register = (_register >> 8) ^ crc32Table[temp];
|
||||
}
|
||||
}
|
||||
_TotalBytesRead += count;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Process one byte in the CRC.
|
||||
/// </summary>
|
||||
/// <param name = "b">the byte to include into the CRC . </param>
|
||||
public void UpdateCRC(byte b)
|
||||
{
|
||||
if (this.reverseBits)
|
||||
{
|
||||
UInt32 temp = (_register >> 24) ^ b;
|
||||
_register = (_register << 8) ^ crc32Table[temp];
|
||||
}
|
||||
else
|
||||
{
|
||||
UInt32 temp = (_register & 0x000000FF) ^ b;
|
||||
_register = (_register >> 8) ^ crc32Table[temp];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process a run of N identical bytes into the CRC.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method serves as an optimization for updating the CRC when a
|
||||
/// run of identical bytes is found. Rather than passing in a buffer of
|
||||
/// length n, containing all identical bytes b, this method accepts the
|
||||
/// byte value and the length of the (virtual) buffer - the length of
|
||||
/// the run.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name = "b">the byte to include into the CRC. </param>
|
||||
/// <param name = "n">the number of times that byte should be repeated. </param>
|
||||
public void UpdateCRC(byte b, int n)
|
||||
{
|
||||
while (n-- > 0)
|
||||
{
|
||||
if (this.reverseBits)
|
||||
{
|
||||
uint temp = (_register >> 24) ^ b;
|
||||
_register = (_register << 8) ^ crc32Table[(temp >= 0)
|
||||
? temp
|
||||
: (temp + 256)];
|
||||
}
|
||||
else
|
||||
{
|
||||
UInt32 temp = (_register & 0x000000FF) ^ b;
|
||||
_register = (_register >> 8) ^ crc32Table[(temp >= 0)
|
||||
? temp
|
||||
: (temp + 256)];
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static uint ReverseBits(uint data)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
uint ret = data;
|
||||
ret = (ret & 0x55555555) << 1 | (ret >> 1) & 0x55555555;
|
||||
ret = (ret & 0x33333333) << 2 | (ret >> 2) & 0x33333333;
|
||||
ret = (ret & 0x0F0F0F0F) << 4 | (ret >> 4) & 0x0F0F0F0F;
|
||||
ret = (ret << 24) | ((ret & 0xFF00) << 8) | ((ret >> 8) & 0xFF00) | (ret >> 24);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte ReverseBits(byte data)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
uint u = (uint)data * 0x00020202;
|
||||
uint m = 0x01044010;
|
||||
uint s = u & m;
|
||||
uint t = (u << 2) & (m << 1);
|
||||
return (byte)((0x01001001 * (s + t)) >> 24);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void GenerateLookupTable()
|
||||
{
|
||||
crc32Table = new UInt32[256];
|
||||
unchecked
|
||||
{
|
||||
UInt32 dwCrc;
|
||||
byte i = 0;
|
||||
do
|
||||
{
|
||||
dwCrc = i;
|
||||
for (byte j = 8; j > 0; j--)
|
||||
{
|
||||
if ((dwCrc & 1) == 1)
|
||||
{
|
||||
dwCrc = (dwCrc >> 1) ^ dwPolynomial;
|
||||
}
|
||||
else
|
||||
{
|
||||
dwCrc >>= 1;
|
||||
}
|
||||
}
|
||||
if (reverseBits)
|
||||
{
|
||||
crc32Table[ReverseBits(i)] = ReverseBits(dwCrc);
|
||||
}
|
||||
else
|
||||
{
|
||||
crc32Table[i] = dwCrc;
|
||||
}
|
||||
i++;
|
||||
} while (i!=0);
|
||||
}
|
||||
|
||||
#if VERBOSE
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("private static readonly UInt32[] crc32Table = {");
|
||||
for (int i = 0; i < crc32Table.Length; i+=4)
|
||||
{
|
||||
Console.Write(" ");
|
||||
for (int j=0; j < 4; j++)
|
||||
{
|
||||
Console.Write(" 0x{0:X8}U,", crc32Table[i+j]);
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
Console.WriteLine("};");
|
||||
Console.WriteLine();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
private uint gf2_matrix_times(uint[] matrix, uint vec)
|
||||
{
|
||||
uint sum = 0;
|
||||
int i=0;
|
||||
while (vec != 0)
|
||||
{
|
||||
if ((vec & 0x01)== 0x01)
|
||||
sum ^= matrix[i];
|
||||
vec >>= 1;
|
||||
i++;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
private void gf2_matrix_square(uint[] square, uint[] mat)
|
||||
{
|
||||
for (int i = 0; i < 32; i++)
|
||||
square[i] = gf2_matrix_times(mat, mat[i]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Combines the given CRC32 value with the current running total.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is useful when using a divide-and-conquer approach to
|
||||
/// calculating a CRC. Multiple threads can each calculate a
|
||||
/// CRC32 on a segment of the data, and then combine the
|
||||
/// individual CRC32 values at the end.
|
||||
/// </remarks>
|
||||
/// <param name="crc">the crc value to be combined with this one</param>
|
||||
/// <param name="length">the length of data the CRC value was calculated on</param>
|
||||
public void Combine(int crc, int length)
|
||||
{
|
||||
uint[] even = new uint[32]; // even-power-of-two zeros operator
|
||||
uint[] odd = new uint[32]; // odd-power-of-two zeros operator
|
||||
|
||||
if (length == 0)
|
||||
return;
|
||||
|
||||
uint crc1= ~_register;
|
||||
uint crc2= (uint) crc;
|
||||
|
||||
// put operator for one zero bit in odd
|
||||
odd[0] = this.dwPolynomial; // the CRC-32 polynomial
|
||||
uint row = 1;
|
||||
for (int i = 1; i < 32; i++)
|
||||
{
|
||||
odd[i] = row;
|
||||
row <<= 1;
|
||||
}
|
||||
|
||||
// put operator for two zero bits in even
|
||||
gf2_matrix_square(even, odd);
|
||||
|
||||
// put operator for four zero bits in odd
|
||||
gf2_matrix_square(odd, even);
|
||||
|
||||
uint len2 = (uint) length;
|
||||
|
||||
// apply len2 zeros to crc1 (first square will put the operator for one
|
||||
// zero byte, eight zero bits, in even)
|
||||
do {
|
||||
// apply zeros operator for this bit of len2
|
||||
gf2_matrix_square(even, odd);
|
||||
|
||||
if ((len2 & 1)== 1)
|
||||
crc1 = gf2_matrix_times(even, crc1);
|
||||
len2 >>= 1;
|
||||
|
||||
if (len2 == 0)
|
||||
break;
|
||||
|
||||
// another iteration of the loop with odd and even swapped
|
||||
gf2_matrix_square(odd, even);
|
||||
if ((len2 & 1)==1)
|
||||
crc1 = gf2_matrix_times(odd, crc1);
|
||||
len2 >>= 1;
|
||||
|
||||
|
||||
} while (len2 != 0);
|
||||
|
||||
crc1 ^= crc2;
|
||||
|
||||
_register= ~crc1;
|
||||
|
||||
//return (int) crc1;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the CRC32 class using the default settings: no
|
||||
/// bit reversal, and a polynomial of 0xEDB88320.
|
||||
/// </summary>
|
||||
public CRC32() : this(false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the CRC32 class, specifying whether to reverse
|
||||
/// data bits or not.
|
||||
/// </summary>
|
||||
/// <param name='reverseBits'>
|
||||
/// specify true if the instance should reverse data bits.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// In the CRC-32 used by BZip2, the bits are reversed. Therefore if you
|
||||
/// want a CRC32 with compatibility with BZip2, you should pass true
|
||||
/// here. In the CRC-32 used by GZIP and PKZIP, the bits are not
|
||||
/// reversed; Therefore if you want a CRC32 with compatibility with
|
||||
/// those, you should pass false.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public CRC32(bool reverseBits) :
|
||||
this( unchecked((int)0xEDB88320), reverseBits)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the CRC32 class, specifying the polynomial and
|
||||
/// whether to reverse data bits or not.
|
||||
/// </summary>
|
||||
/// <param name='polynomial'>
|
||||
/// The polynomial to use for the CRC, expressed in the reversed (LSB)
|
||||
/// format: the highest ordered bit in the polynomial value is the
|
||||
/// coefficient of the 0th power; the second-highest order bit is the
|
||||
/// coefficient of the 1 power, and so on. Expressed this way, the
|
||||
/// polynomial for the CRC-32C used in IEEE 802.3, is 0xEDB88320.
|
||||
/// </param>
|
||||
/// <param name='reverseBits'>
|
||||
/// specify true if the instance should reverse data bits.
|
||||
/// </param>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// In the CRC-32 used by BZip2, the bits are reversed. Therefore if you
|
||||
/// want a CRC32 with compatibility with BZip2, you should pass true
|
||||
/// here for the <c>reverseBits</c> parameter. In the CRC-32 used by
|
||||
/// GZIP and PKZIP, the bits are not reversed; Therefore if you want a
|
||||
/// CRC32 with compatibility with those, you should pass false for the
|
||||
/// <c>reverseBits</c> parameter.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public CRC32(int polynomial, bool reverseBits)
|
||||
{
|
||||
this.reverseBits = reverseBits;
|
||||
this.dwPolynomial = (uint) polynomial;
|
||||
this.GenerateLookupTable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the CRC-32 class - clear the CRC "remainder register."
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Use this when employing a single instance of this class to compute
|
||||
/// multiple, distinct CRCs on multiple, distinct data blocks.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void Reset()
|
||||
{
|
||||
_register = 0xFFFFFFFFU;
|
||||
}
|
||||
|
||||
// private member vars
|
||||
private UInt32 dwPolynomial;
|
||||
private Int64 _TotalBytesRead;
|
||||
private bool reverseBits;
|
||||
private UInt32[] crc32Table;
|
||||
private const int BUFFER_SIZE = 8192;
|
||||
private UInt32 _register = 0xFFFFFFFFU;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A Stream that calculates a CRC32 (a checksum) on all bytes read,
|
||||
/// or on all bytes written.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This class can be used to verify the CRC of a ZipEntry when
|
||||
/// reading from a stream, or to calculate a CRC when writing to a
|
||||
/// stream. The stream should be used to either read, or write, but
|
||||
/// not both. If you intermix reads and writes, the results are not
|
||||
/// defined.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This class is intended primarily for use internally by the
|
||||
/// DotNetZip library.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class CrcCalculatorStream : System.IO.Stream, System.IDisposable
|
||||
{
|
||||
private static readonly Int64 UnsetLengthLimit = -99;
|
||||
|
||||
internal System.IO.Stream _innerStream;
|
||||
private CRC32 _Crc32;
|
||||
private Int64 _lengthLimit = -99;
|
||||
private bool _leaveOpen;
|
||||
|
||||
/// <summary>
|
||||
/// The default constructor.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Instances returned from this constructor will leave the underlying
|
||||
/// stream open upon Close(). The stream uses the default CRC32
|
||||
/// algorithm, which implies a polynomial of 0xEDB88320.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="stream">The underlying stream</param>
|
||||
public CrcCalculatorStream(System.IO.Stream stream)
|
||||
: this(true, CrcCalculatorStream.UnsetLengthLimit, stream, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The constructor allows the caller to specify how to handle the
|
||||
/// underlying stream at close.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The stream uses the default CRC32 algorithm, which implies a
|
||||
/// polynomial of 0xEDB88320.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="stream">The underlying stream</param>
|
||||
/// <param name="leaveOpen">true to leave the underlying stream
|
||||
/// open upon close of the <c>CrcCalculatorStream</c>; false otherwise.</param>
|
||||
public CrcCalculatorStream(System.IO.Stream stream, bool leaveOpen)
|
||||
: this(leaveOpen, CrcCalculatorStream.UnsetLengthLimit, stream, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A constructor allowing the specification of the length of the stream
|
||||
/// to read.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The stream uses the default CRC32 algorithm, which implies a
|
||||
/// polynomial of 0xEDB88320.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Instances returned from this constructor will leave the underlying
|
||||
/// stream open upon Close().
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="stream">The underlying stream</param>
|
||||
/// <param name="length">The length of the stream to slurp</param>
|
||||
public CrcCalculatorStream(System.IO.Stream stream, Int64 length)
|
||||
: this(true, length, stream, null)
|
||||
{
|
||||
if (length < 0)
|
||||
throw new ArgumentException("length");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A constructor allowing the specification of the length of the stream
|
||||
/// to read, as well as whether to keep the underlying stream open upon
|
||||
/// Close().
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The stream uses the default CRC32 algorithm, which implies a
|
||||
/// polynomial of 0xEDB88320.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="stream">The underlying stream</param>
|
||||
/// <param name="length">The length of the stream to slurp</param>
|
||||
/// <param name="leaveOpen">true to leave the underlying stream
|
||||
/// open upon close of the <c>CrcCalculatorStream</c>; false otherwise.</param>
|
||||
public CrcCalculatorStream(System.IO.Stream stream, Int64 length, bool leaveOpen)
|
||||
: this(leaveOpen, length, stream, null)
|
||||
{
|
||||
if (length < 0)
|
||||
throw new ArgumentException("length");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A constructor allowing the specification of the length of the stream
|
||||
/// to read, as well as whether to keep the underlying stream open upon
|
||||
/// Close(), and the CRC32 instance to use.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The stream uses the specified CRC32 instance, which allows the
|
||||
/// application to specify how the CRC gets calculated.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="stream">The underlying stream</param>
|
||||
/// <param name="length">The length of the stream to slurp</param>
|
||||
/// <param name="leaveOpen">true to leave the underlying stream
|
||||
/// open upon close of the <c>CrcCalculatorStream</c>; false otherwise.</param>
|
||||
/// <param name="crc32">the CRC32 instance to use to calculate the CRC32</param>
|
||||
public CrcCalculatorStream(System.IO.Stream stream, Int64 length, bool leaveOpen,
|
||||
CRC32 crc32)
|
||||
: this(leaveOpen, length, stream, crc32)
|
||||
{
|
||||
if (length < 0)
|
||||
throw new ArgumentException("length");
|
||||
}
|
||||
|
||||
|
||||
// This ctor is private - no validation is done here. This is to allow the use
|
||||
// of a (specific) negative value for the _lengthLimit, to indicate that there
|
||||
// is no length set. So we validate the length limit in those ctors that use an
|
||||
// explicit param, otherwise we don't validate, because it could be our special
|
||||
// value.
|
||||
private CrcCalculatorStream
|
||||
(bool leaveOpen, Int64 length, System.IO.Stream stream, CRC32 crc32)
|
||||
: base()
|
||||
{
|
||||
_innerStream = stream;
|
||||
_Crc32 = crc32 ?? new CRC32();
|
||||
_lengthLimit = length;
|
||||
_leaveOpen = leaveOpen;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of bytes run through the CRC32 calculator.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// This is either the total number of bytes read, or the total number of
|
||||
/// bytes written, depending on the direction of this stream.
|
||||
/// </remarks>
|
||||
public Int64 TotalBytesSlurped
|
||||
{
|
||||
get { return _Crc32.TotalBytesRead; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides the current CRC for all blocks slurped in.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The running total of the CRC is kept as data is written or read
|
||||
/// through the stream. read this property after all reads or writes to
|
||||
/// get an accurate CRC for the entire stream.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public Int32 Crc
|
||||
{
|
||||
get { return _Crc32.Crc32Result; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the underlying stream will be left open when the
|
||||
/// <c>CrcCalculatorStream</c> is Closed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Set this at any point before calling <see cref="Close()"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public bool LeaveOpen
|
||||
{
|
||||
get { return _leaveOpen; }
|
||||
set { _leaveOpen = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read from the stream
|
||||
/// </summary>
|
||||
/// <param name="buffer">the buffer to read</param>
|
||||
/// <param name="offset">the offset at which to start</param>
|
||||
/// <param name="count">the number of bytes to read</param>
|
||||
/// <returns>the number of bytes actually read</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int bytesToRead = count;
|
||||
|
||||
// Need to limit the # of bytes returned, if the stream is intended to have
|
||||
// a definite length. This is especially useful when returning a stream for
|
||||
// the uncompressed data directly to the application. The app won't
|
||||
// necessarily read only the UncompressedSize number of bytes. For example
|
||||
// wrapping the stream returned from OpenReader() into a StreadReader() and
|
||||
// calling ReadToEnd() on it, We can "over-read" the zip data and get a
|
||||
// corrupt string. The length limits that, prevents that problem.
|
||||
|
||||
if (_lengthLimit != CrcCalculatorStream.UnsetLengthLimit)
|
||||
{
|
||||
if (_Crc32.TotalBytesRead >= _lengthLimit) return 0; // EOF
|
||||
Int64 bytesRemaining = _lengthLimit - _Crc32.TotalBytesRead;
|
||||
if (bytesRemaining < count) bytesToRead = (int)bytesRemaining;
|
||||
}
|
||||
int n = _innerStream.Read(buffer, offset, bytesToRead);
|
||||
if (n > 0) _Crc32.SlurpBlock(buffer, offset, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write to the stream.
|
||||
/// </summary>
|
||||
/// <param name="buffer">the buffer from which to write</param>
|
||||
/// <param name="offset">the offset at which to start writing</param>
|
||||
/// <param name="count">the number of bytes to write</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (count > 0) _Crc32.SlurpBlock(buffer, offset, count);
|
||||
_innerStream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream supports reading.
|
||||
/// </summary>
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return _innerStream.CanRead; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream supports seeking.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Always returns false.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream supports writing.
|
||||
/// </summary>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return _innerStream.CanWrite; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flush the stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
_innerStream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the length of the underlying stream.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_lengthLimit == CrcCalculatorStream.UnsetLengthLimit)
|
||||
return _innerStream.Length;
|
||||
else return _lengthLimit;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The getter for this property returns the total bytes read.
|
||||
/// If you use the setter, it will throw
|
||||
/// <see cref="NotSupportedException"/>.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get { return _Crc32.TotalBytesRead; }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seeking is not supported on this stream. This method always throws
|
||||
/// <see cref="NotSupportedException"/>
|
||||
/// </summary>
|
||||
/// <param name="offset">N/A</param>
|
||||
/// <param name="origin">N/A</param>
|
||||
/// <returns>N/A</returns>
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method always throws
|
||||
/// <see cref="NotSupportedException"/>
|
||||
/// </summary>
|
||||
/// <param name="value">N/A</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the stream.
|
||||
/// </summary>
|
||||
public override void Close()
|
||||
{
|
||||
base.Close();
|
||||
if (!_leaveOpen)
|
||||
_innerStream.Close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
// ComHelper.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-June-13 17:04:06>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines a COM Helper class.
|
||||
//
|
||||
// Created: Tue, 08 Sep 2009 22:03
|
||||
//
|
||||
|
||||
using Interop=System.Runtime.InteropServices;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
/// <summary>
|
||||
/// This class exposes a set of COM-accessible wrappers for static
|
||||
/// methods available on the ZipFile class. You don't need this
|
||||
/// class unless you are using DotNetZip from a COM environment.
|
||||
/// </summary>
|
||||
[System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d0000F")]
|
||||
[System.Runtime.InteropServices.ComVisible(true)]
|
||||
#if !NETCF
|
||||
[System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.AutoDispatch)]
|
||||
#endif
|
||||
|
||||
public class ComHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// A wrapper for <see cref="ZipFile.IsZipFile(string)">ZipFile.IsZipFile(string)</see>
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename to of the zip file to check.</param>
|
||||
/// <returns>true if the file contains a valid zip file.</returns>
|
||||
public bool IsZipFile(string filename)
|
||||
{
|
||||
return ZipFile.IsZipFile(filename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A wrapper for <see cref="ZipFile.IsZipFile(string, bool)">ZipFile.IsZipFile(string, bool)</see>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We cannot use "overloaded" Method names in COM interop.
|
||||
/// So, here, we use a unique name.
|
||||
/// </remarks>
|
||||
/// <param name="filename">The filename to of the zip file to check.</param>
|
||||
/// <returns>true if the file contains a valid zip file.</returns>
|
||||
public bool IsZipFileWithExtract(string filename)
|
||||
{
|
||||
return ZipFile.IsZipFile(filename, true);
|
||||
}
|
||||
|
||||
#if !NETCF
|
||||
/// <summary>
|
||||
/// A wrapper for <see cref="ZipFile.CheckZip(string)">ZipFile.CheckZip(string)</see>
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename to of the zip file to check.</param>
|
||||
///
|
||||
/// <returns>true if the named zip file checks OK. Otherwise, false. </returns>
|
||||
public bool CheckZip(string filename)
|
||||
{
|
||||
return ZipFile.CheckZip(filename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A COM-friendly wrapper for the static method <see cref="ZipFile.CheckZipPassword(string,string)"/>.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="filename">The filename to of the zip file to check.</param>
|
||||
///
|
||||
/// <param name="password">The password to check.</param>
|
||||
///
|
||||
/// <returns>true if the named zip file checks OK. Otherwise, false. </returns>
|
||||
public bool CheckZipPassword(string filename, string password)
|
||||
{
|
||||
return ZipFile.CheckZipPassword(filename, password);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A wrapper for <see cref="ZipFile.FixZipDirectory(string)">ZipFile.FixZipDirectory(string)</see>
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename to of the zip file to fix.</param>
|
||||
public void FixZipDirectory(string filename)
|
||||
{
|
||||
ZipFile.FixZipDirectory(filename);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// A wrapper for <see cref="ZipFile.LibraryVersion">ZipFile.LibraryVersion</see>
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// the version number on the DotNetZip assembly, formatted as a string.
|
||||
/// </returns>
|
||||
public string GetZipLibraryVersion()
|
||||
{
|
||||
return ZipFile.LibraryVersion.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
// EncryptionAlgorithm.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-October-21 17:24:45>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the EncryptionAgorithm enum
|
||||
//
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
/// <summary>
|
||||
/// An enum that provides the various encryption algorithms supported by this
|
||||
/// library.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// <c>PkzipWeak</c> implies the use of Zip 2.0 encryption, which is known to be
|
||||
/// weak and subvertible.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A note on interoperability: Values of <c>PkzipWeak</c> and <c>None</c> are
|
||||
/// specified in <see
|
||||
/// href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">PKWARE's zip
|
||||
/// specification</see>, and are considered to be "standard". Zip archives
|
||||
/// produced using these options will be interoperable with many other zip tools
|
||||
/// and libraries, including Windows Explorer.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Values of <c>WinZipAes128</c> and <c>WinZipAes256</c> are not part of the Zip
|
||||
/// specification, but rather imply the use of a vendor-specific extension from
|
||||
/// WinZip. If you want to produce interoperable Zip archives, do not use these
|
||||
/// values. For example, if you produce a zip archive using WinZipAes256, you
|
||||
/// will be able to open it in Windows Explorer on Windows XP and Vista, but you
|
||||
/// will not be able to extract entries; trying this will lead to an "unspecified
|
||||
/// error". For this reason, some people have said that a zip archive that uses
|
||||
/// WinZip's AES encryption is not actually a zip archive at all. A zip archive
|
||||
/// produced this way will be readable with the WinZip tool (Version 11 and
|
||||
/// beyond).
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// There are other third-party tools and libraries, both commercial and
|
||||
/// otherwise, that support WinZip's AES encryption. These will be able to read
|
||||
/// AES-encrypted zip archives produced by DotNetZip, and conversely applications
|
||||
/// that use DotNetZip to read zip archives will be able to read AES-encrypted
|
||||
/// archives produced by those tools or libraries. Consult the documentation for
|
||||
/// those other tools and libraries to find out if WinZip's AES encryption is
|
||||
/// supported.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// In case you care: According to <see
|
||||
/// href="http://www.winzip.com/aes_info.htm">the WinZip specification</see>, the
|
||||
/// actual AES key used is derived from the <see cref="ZipEntry.Password"/> via an
|
||||
/// algorithm that complies with <see
|
||||
/// href="http://www.ietf.org/rfc/rfc2898.txt">RFC 2898</see>, using an iteration
|
||||
/// count of 1000. The algorithm is sometimes referred to as PBKDF2, which stands
|
||||
/// for "Password Based Key Derivation Function #2".
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A word about password strength and length: The AES encryption technology is
|
||||
/// very good, but any system is only as secure as the weakest link. If you want
|
||||
/// to secure your data, be sure to use a password that is hard to guess. To make
|
||||
/// it harder to guess (increase its "entropy"), you should make it longer. If
|
||||
/// you use normal characters from an ASCII keyboard, a password of length 20 will
|
||||
/// be strong enough that it will be impossible to guess. For more information on
|
||||
/// that, I'd encourage you to read <see
|
||||
/// href="http://www.redkestrel.co.uk/Articles/RandomPasswordStrength.html">this
|
||||
/// article.</see>
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The WinZip AES algorithms are not supported with the version of DotNetZip that
|
||||
/// runs on the .NET Compact Framework. This is because .NET CF lacks the
|
||||
/// HMACSHA1 class that is required for producing the archive.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public enum EncryptionAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// No encryption at all.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Traditional or Classic pkzip encryption.
|
||||
/// </summary>
|
||||
PkzipWeak,
|
||||
|
||||
#if AESCRYPTO
|
||||
/// <summary>
|
||||
/// WinZip AES encryption (128 key bits).
|
||||
/// </summary>
|
||||
WinZipAes128,
|
||||
|
||||
/// <summary>
|
||||
/// WinZip AES encryption (256 key bits).
|
||||
/// </summary>
|
||||
WinZipAes256,
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// An encryption algorithm that is not supported by DotNetZip.
|
||||
/// </summary>
|
||||
Unsupported = 4,
|
||||
|
||||
|
||||
// others... not implemented (yet?)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,684 @@
|
||||
// Events.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2006, 2007, 2008, 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-August-06 12:26:24>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines events used by the ZipFile class.
|
||||
//
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate in which the application writes the <c>ZipEntry</c> content for the named entry.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="entryName">The name of the entry that must be written.</param>
|
||||
/// <param name="stream">The stream to which the entry data should be written.</param>
|
||||
///
|
||||
/// <remarks>
|
||||
/// When you add an entry and specify a <c>WriteDelegate</c>, via <see
|
||||
/// cref="Ionic.Zip.ZipFile.AddEntry(string, WriteDelegate)"/>, the application
|
||||
/// code provides the logic that writes the entry data directly into the zip file.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example shows how to define a WriteDelegate that obtains a DataSet, and then
|
||||
/// writes the XML for the DataSet into the zip archive. There's no need to
|
||||
/// save the XML to a disk file first.
|
||||
///
|
||||
/// <code lang="C#">
|
||||
/// private void WriteEntry (String filename, Stream output)
|
||||
/// {
|
||||
/// DataSet ds1 = ObtainDataSet();
|
||||
/// ds1.WriteXml(output);
|
||||
/// }
|
||||
///
|
||||
/// private void Run()
|
||||
/// {
|
||||
/// using (var zip = new ZipFile())
|
||||
/// {
|
||||
/// zip.AddEntry(zipEntryName, WriteEntry);
|
||||
/// zip.Save(zipFileName);
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="vb">
|
||||
/// Private Sub WriteEntry (ByVal filename As String, ByVal output As Stream)
|
||||
/// DataSet ds1 = ObtainDataSet()
|
||||
/// ds1.WriteXml(stream)
|
||||
/// End Sub
|
||||
///
|
||||
/// Public Sub Run()
|
||||
/// Using zip = New ZipFile
|
||||
/// zip.AddEntry(zipEntryName, New WriteDelegate(AddressOf WriteEntry))
|
||||
/// zip.Save(zipFileName)
|
||||
/// End Using
|
||||
/// End Sub
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <seealso cref="Ionic.Zip.ZipFile.AddEntry(string, WriteDelegate)"/>
|
||||
public delegate void WriteDelegate(string entryName, System.IO.Stream stream);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Delegate in which the application opens the stream, just-in-time, for the named entry.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="entryName">
|
||||
/// The name of the ZipEntry that the application should open the stream for.
|
||||
/// </param>
|
||||
///
|
||||
/// <remarks>
|
||||
/// When you add an entry via <see cref="Ionic.Zip.ZipFile.AddEntry(string,
|
||||
/// OpenDelegate, CloseDelegate)"/>, the application code provides the logic that
|
||||
/// opens and closes the stream for the given ZipEntry.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <seealso cref="Ionic.Zip.ZipFile.AddEntry(string, OpenDelegate, CloseDelegate)"/>
|
||||
public delegate System.IO.Stream OpenDelegate(string entryName);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate in which the application closes the stream, just-in-time, for the named entry.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="entryName">
|
||||
/// The name of the ZipEntry that the application should close the stream for.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="stream">The stream to be closed.</param>
|
||||
///
|
||||
/// <remarks>
|
||||
/// When you add an entry via <see cref="Ionic.Zip.ZipFile.AddEntry(string,
|
||||
/// OpenDelegate, CloseDelegate)"/>, the application code provides the logic that
|
||||
/// opens and closes the stream for the given ZipEntry.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <seealso cref="Ionic.Zip.ZipFile.AddEntry(string, OpenDelegate, CloseDelegate)"/>
|
||||
public delegate void CloseDelegate(string entryName, System.IO.Stream stream);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for the callback by which the application tells the
|
||||
/// library the CompressionLevel to use for a file.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Using this callback, the application can, for example, specify that
|
||||
/// previously-compressed files (.mp3, .png, .docx, etc) should use a
|
||||
/// <c>CompressionLevel</c> of <c>None</c>, or can set the compression level based
|
||||
/// on any other factor.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <seealso cref="Ionic.Zip.ZipFile.SetCompression"/>
|
||||
public delegate Ionic.Zlib.CompressionLevel SetCompressionCallback(string localFileName, string fileNameInArchive);
|
||||
|
||||
/// <summary>
|
||||
/// In an EventArgs type, indicates which sort of progress event is being
|
||||
/// reported.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There are events for reading, events for saving, and events for
|
||||
/// extracting. This enumeration allows a single EventArgs type to be sued to
|
||||
/// describe one of multiple subevents. For example, a SaveProgress event is
|
||||
/// invoked before, after, and during the saving of a single entry. The value
|
||||
/// of an enum with this type, specifies which event is being triggered. The
|
||||
/// same applies to Extraction, Reading and Adding events.
|
||||
/// </remarks>
|
||||
public enum ZipProgressEventType
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that a Add() operation has started.
|
||||
/// </summary>
|
||||
Adding_Started,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an individual entry in the archive has been added.
|
||||
/// </summary>
|
||||
Adding_AfterAddEntry,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that a Add() operation has completed.
|
||||
/// </summary>
|
||||
Adding_Completed,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that a Read() operation has started.
|
||||
/// </summary>
|
||||
Reading_Started,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an individual entry in the archive is about to be read.
|
||||
/// </summary>
|
||||
Reading_BeforeReadEntry,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an individual entry in the archive has just been read.
|
||||
/// </summary>
|
||||
Reading_AfterReadEntry,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that a Read() operation has completed.
|
||||
/// </summary>
|
||||
Reading_Completed,
|
||||
|
||||
/// <summary>
|
||||
/// The given event reports the number of bytes read so far
|
||||
/// during a Read() operation.
|
||||
/// </summary>
|
||||
Reading_ArchiveBytesRead,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that a Save() operation has started.
|
||||
/// </summary>
|
||||
Saving_Started,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an individual entry in the archive is about to be written.
|
||||
/// </summary>
|
||||
Saving_BeforeWriteEntry,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an individual entry in the archive has just been saved.
|
||||
/// </summary>
|
||||
Saving_AfterWriteEntry,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that a Save() operation has completed.
|
||||
/// </summary>
|
||||
Saving_Completed,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the zip archive has been created in a
|
||||
/// temporary location during a Save() operation.
|
||||
/// </summary>
|
||||
Saving_AfterSaveTempArchive,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the temporary file is about to be renamed to the final archive
|
||||
/// name during a Save() operation.
|
||||
/// </summary>
|
||||
Saving_BeforeRenameTempArchive,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the temporary file is has just been renamed to the final archive
|
||||
/// name during a Save() operation.
|
||||
/// </summary>
|
||||
Saving_AfterRenameTempArchive,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the self-extracting archive has been compiled
|
||||
/// during a Save() operation.
|
||||
/// </summary>
|
||||
Saving_AfterCompileSelfExtractor,
|
||||
|
||||
/// <summary>
|
||||
/// The given event is reporting the number of source bytes that have run through the compressor so far
|
||||
/// during a Save() operation.
|
||||
/// </summary>
|
||||
Saving_EntryBytesRead,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an entry is about to be extracted.
|
||||
/// </summary>
|
||||
Extracting_BeforeExtractEntry,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an entry has just been extracted.
|
||||
/// </summary>
|
||||
Extracting_AfterExtractEntry,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that extraction of an entry would overwrite an existing
|
||||
/// filesystem file. You must use
|
||||
/// <see cref="ExtractExistingFileAction.InvokeExtractProgressEvent">
|
||||
/// ExtractExistingFileAction.InvokeExtractProgressEvent</see> in the call
|
||||
/// to <c>ZipEntry.Extract()</c> in order to receive this event.
|
||||
/// </summary>
|
||||
Extracting_ExtractEntryWouldOverwrite,
|
||||
|
||||
/// <summary>
|
||||
/// The given event is reporting the number of bytes written so far for
|
||||
/// the current entry during an Extract() operation.
|
||||
/// </summary>
|
||||
Extracting_EntryBytesWritten,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an ExtractAll operation is about to begin.
|
||||
/// </summary>
|
||||
Extracting_BeforeExtractAll,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an ExtractAll operation has completed.
|
||||
/// </summary>
|
||||
Extracting_AfterExtractAll,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an error has occurred while saving a zip file.
|
||||
/// This generally means the file cannot be opened, because it has been
|
||||
/// removed, or because it is locked by another process. It can also
|
||||
/// mean that the file cannot be Read, because of a range lock conflict.
|
||||
/// </summary>
|
||||
Error_Saving,
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides information about the progress of a save, read, or extract operation.
|
||||
/// This is a base class; you will probably use one of the classes derived from this one.
|
||||
/// </summary>
|
||||
public class ZipProgressEventArgs : EventArgs
|
||||
{
|
||||
private int _entriesTotal;
|
||||
private bool _cancel;
|
||||
private ZipEntry _latestEntry;
|
||||
private ZipProgressEventType _flavor;
|
||||
private String _archiveName;
|
||||
private Int64 _bytesTransferred;
|
||||
private Int64 _totalBytesToTransfer;
|
||||
|
||||
|
||||
internal ZipProgressEventArgs() { }
|
||||
|
||||
internal ZipProgressEventArgs(string archiveName, ZipProgressEventType flavor)
|
||||
{
|
||||
this._archiveName = archiveName;
|
||||
this._flavor = flavor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The total number of entries to be saved or extracted.
|
||||
/// </summary>
|
||||
public int EntriesTotal
|
||||
{
|
||||
get { return _entriesTotal; }
|
||||
set { _entriesTotal = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name of the last entry saved or extracted.
|
||||
/// </summary>
|
||||
public ZipEntry CurrentEntry
|
||||
{
|
||||
get { return _latestEntry; }
|
||||
set { _latestEntry = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// In an event handler, set this to cancel the save or extract
|
||||
/// operation that is in progress.
|
||||
/// </summary>
|
||||
public bool Cancel
|
||||
{
|
||||
get { return _cancel; }
|
||||
set { _cancel = _cancel || value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type of event being reported.
|
||||
/// </summary>
|
||||
public ZipProgressEventType EventType
|
||||
{
|
||||
get { return _flavor; }
|
||||
set { _flavor = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the archive name associated to this event.
|
||||
/// </summary>
|
||||
public String ArchiveName
|
||||
{
|
||||
get { return _archiveName; }
|
||||
set { _archiveName = value; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The number of bytes read or written so far for this entry.
|
||||
/// </summary>
|
||||
public Int64 BytesTransferred
|
||||
{
|
||||
get { return _bytesTransferred; }
|
||||
set { _bytesTransferred = value; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Total number of bytes that will be read or written for this entry.
|
||||
/// This number will be -1 if the value cannot be determined.
|
||||
/// </summary>
|
||||
public Int64 TotalBytesToTransfer
|
||||
{
|
||||
get { return _totalBytesToTransfer; }
|
||||
set { _totalBytesToTransfer = value; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides information about the progress of a Read operation.
|
||||
/// </summary>
|
||||
public class ReadProgressEventArgs : ZipProgressEventArgs
|
||||
{
|
||||
|
||||
internal ReadProgressEventArgs() { }
|
||||
|
||||
private ReadProgressEventArgs(string archiveName, ZipProgressEventType flavor)
|
||||
: base(archiveName, flavor)
|
||||
{ }
|
||||
|
||||
internal static ReadProgressEventArgs Before(string archiveName, int entriesTotal)
|
||||
{
|
||||
var x = new ReadProgressEventArgs(archiveName, ZipProgressEventType.Reading_BeforeReadEntry);
|
||||
x.EntriesTotal = entriesTotal;
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static ReadProgressEventArgs After(string archiveName, ZipEntry entry, int entriesTotal)
|
||||
{
|
||||
var x = new ReadProgressEventArgs(archiveName, ZipProgressEventType.Reading_AfterReadEntry);
|
||||
x.EntriesTotal = entriesTotal;
|
||||
x.CurrentEntry = entry;
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static ReadProgressEventArgs Started(string archiveName)
|
||||
{
|
||||
var x = new ReadProgressEventArgs(archiveName, ZipProgressEventType.Reading_Started);
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static ReadProgressEventArgs ByteUpdate(string archiveName, ZipEntry entry, Int64 bytesXferred, Int64 totalBytes)
|
||||
{
|
||||
var x = new ReadProgressEventArgs(archiveName, ZipProgressEventType.Reading_ArchiveBytesRead);
|
||||
x.CurrentEntry = entry;
|
||||
x.BytesTransferred = bytesXferred;
|
||||
x.TotalBytesToTransfer = totalBytes;
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static ReadProgressEventArgs Completed(string archiveName)
|
||||
{
|
||||
var x = new ReadProgressEventArgs(archiveName, ZipProgressEventType.Reading_Completed);
|
||||
return x;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides information about the progress of a Add operation.
|
||||
/// </summary>
|
||||
public class AddProgressEventArgs : ZipProgressEventArgs
|
||||
{
|
||||
internal AddProgressEventArgs() { }
|
||||
|
||||
private AddProgressEventArgs(string archiveName, ZipProgressEventType flavor)
|
||||
: base(archiveName, flavor)
|
||||
{ }
|
||||
|
||||
internal static AddProgressEventArgs AfterEntry(string archiveName, ZipEntry entry, int entriesTotal)
|
||||
{
|
||||
var x = new AddProgressEventArgs(archiveName, ZipProgressEventType.Adding_AfterAddEntry);
|
||||
x.EntriesTotal = entriesTotal;
|
||||
x.CurrentEntry = entry;
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static AddProgressEventArgs Started(string archiveName)
|
||||
{
|
||||
var x = new AddProgressEventArgs(archiveName, ZipProgressEventType.Adding_Started);
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static AddProgressEventArgs Completed(string archiveName)
|
||||
{
|
||||
var x = new AddProgressEventArgs(archiveName, ZipProgressEventType.Adding_Completed);
|
||||
return x;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides information about the progress of a save operation.
|
||||
/// </summary>
|
||||
public class SaveProgressEventArgs : ZipProgressEventArgs
|
||||
{
|
||||
private int _entriesSaved;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for the SaveProgressEventArgs.
|
||||
/// </summary>
|
||||
/// <param name="archiveName">the name of the zip archive.</param>
|
||||
/// <param name="before">whether this is before saving the entry, or after</param>
|
||||
/// <param name="entriesTotal">The total number of entries in the zip archive.</param>
|
||||
/// <param name="entriesSaved">Number of entries that have been saved.</param>
|
||||
/// <param name="entry">The entry involved in the event.</param>
|
||||
internal SaveProgressEventArgs(string archiveName, bool before, int entriesTotal, int entriesSaved, ZipEntry entry)
|
||||
: base(archiveName, (before) ? ZipProgressEventType.Saving_BeforeWriteEntry : ZipProgressEventType.Saving_AfterWriteEntry)
|
||||
{
|
||||
this.EntriesTotal = entriesTotal;
|
||||
this.CurrentEntry = entry;
|
||||
this._entriesSaved = entriesSaved;
|
||||
}
|
||||
|
||||
internal SaveProgressEventArgs() { }
|
||||
|
||||
internal SaveProgressEventArgs(string archiveName, ZipProgressEventType flavor)
|
||||
: base(archiveName, flavor)
|
||||
{ }
|
||||
|
||||
|
||||
internal static SaveProgressEventArgs ByteUpdate(string archiveName, ZipEntry entry, Int64 bytesXferred, Int64 totalBytes)
|
||||
{
|
||||
var x = new SaveProgressEventArgs(archiveName, ZipProgressEventType.Saving_EntryBytesRead);
|
||||
x.ArchiveName = archiveName;
|
||||
x.CurrentEntry = entry;
|
||||
x.BytesTransferred = bytesXferred;
|
||||
x.TotalBytesToTransfer = totalBytes;
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static SaveProgressEventArgs Started(string archiveName)
|
||||
{
|
||||
var x = new SaveProgressEventArgs(archiveName, ZipProgressEventType.Saving_Started);
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static SaveProgressEventArgs Completed(string archiveName)
|
||||
{
|
||||
var x = new SaveProgressEventArgs(archiveName, ZipProgressEventType.Saving_Completed);
|
||||
return x;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of entries saved so far.
|
||||
/// </summary>
|
||||
public int EntriesSaved
|
||||
{
|
||||
get { return _entriesSaved; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides information about the progress of the extract operation.
|
||||
/// </summary>
|
||||
public class ExtractProgressEventArgs : ZipProgressEventArgs
|
||||
{
|
||||
private int _entriesExtracted;
|
||||
private string _target;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for the ExtractProgressEventArgs.
|
||||
/// </summary>
|
||||
/// <param name="archiveName">the name of the zip archive.</param>
|
||||
/// <param name="before">whether this is before saving the entry, or after</param>
|
||||
/// <param name="entriesTotal">The total number of entries in the zip archive.</param>
|
||||
/// <param name="entriesExtracted">Number of entries that have been extracted.</param>
|
||||
/// <param name="entry">The entry involved in the event.</param>
|
||||
/// <param name="extractLocation">The location to which entries are extracted.</param>
|
||||
internal ExtractProgressEventArgs(string archiveName, bool before, int entriesTotal, int entriesExtracted, ZipEntry entry, string extractLocation)
|
||||
: base(archiveName, (before) ? ZipProgressEventType.Extracting_BeforeExtractEntry : ZipProgressEventType.Extracting_AfterExtractEntry)
|
||||
{
|
||||
this.EntriesTotal = entriesTotal;
|
||||
this.CurrentEntry = entry;
|
||||
this._entriesExtracted = entriesExtracted;
|
||||
this._target = extractLocation;
|
||||
}
|
||||
|
||||
internal ExtractProgressEventArgs(string archiveName, ZipProgressEventType flavor)
|
||||
: base(archiveName, flavor)
|
||||
{ }
|
||||
|
||||
internal ExtractProgressEventArgs()
|
||||
{ }
|
||||
|
||||
|
||||
internal static ExtractProgressEventArgs BeforeExtractEntry(string archiveName, ZipEntry entry, string extractLocation)
|
||||
{
|
||||
var x = new ExtractProgressEventArgs
|
||||
{
|
||||
ArchiveName = archiveName,
|
||||
EventType = ZipProgressEventType.Extracting_BeforeExtractEntry,
|
||||
CurrentEntry = entry,
|
||||
_target = extractLocation,
|
||||
};
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static ExtractProgressEventArgs ExtractExisting(string archiveName, ZipEntry entry, string extractLocation)
|
||||
{
|
||||
var x = new ExtractProgressEventArgs
|
||||
{
|
||||
ArchiveName = archiveName,
|
||||
EventType = ZipProgressEventType.Extracting_ExtractEntryWouldOverwrite,
|
||||
CurrentEntry = entry,
|
||||
_target = extractLocation,
|
||||
};
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static ExtractProgressEventArgs AfterExtractEntry(string archiveName, ZipEntry entry, string extractLocation)
|
||||
{
|
||||
var x = new ExtractProgressEventArgs
|
||||
{
|
||||
ArchiveName = archiveName,
|
||||
EventType = ZipProgressEventType.Extracting_AfterExtractEntry,
|
||||
CurrentEntry = entry,
|
||||
_target = extractLocation,
|
||||
};
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static ExtractProgressEventArgs ExtractAllStarted(string archiveName, string extractLocation)
|
||||
{
|
||||
var x = new ExtractProgressEventArgs(archiveName, ZipProgressEventType.Extracting_BeforeExtractAll);
|
||||
x._target = extractLocation;
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static ExtractProgressEventArgs ExtractAllCompleted(string archiveName, string extractLocation)
|
||||
{
|
||||
var x = new ExtractProgressEventArgs(archiveName, ZipProgressEventType.Extracting_AfterExtractAll);
|
||||
x._target = extractLocation;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
internal static ExtractProgressEventArgs ByteUpdate(string archiveName, ZipEntry entry, Int64 bytesWritten, Int64 totalBytes)
|
||||
{
|
||||
var x = new ExtractProgressEventArgs(archiveName, ZipProgressEventType.Extracting_EntryBytesWritten);
|
||||
x.ArchiveName = archiveName;
|
||||
x.CurrentEntry = entry;
|
||||
x.BytesTransferred = bytesWritten;
|
||||
x.TotalBytesToTransfer = totalBytes;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Number of entries extracted so far. This is set only if the
|
||||
/// EventType is Extracting_BeforeExtractEntry or Extracting_AfterExtractEntry, and
|
||||
/// the Extract() is occurring witin the scope of a call to ExtractAll().
|
||||
/// </summary>
|
||||
public int EntriesExtracted
|
||||
{
|
||||
get { return _entriesExtracted; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the extraction target location, a filesystem path.
|
||||
/// </summary>
|
||||
public String ExtractLocation
|
||||
{
|
||||
get { return _target; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides information about the an error that occurred while zipping.
|
||||
/// </summary>
|
||||
public class ZipErrorEventArgs : ZipProgressEventArgs
|
||||
{
|
||||
private Exception _exc;
|
||||
private ZipErrorEventArgs() { }
|
||||
internal static ZipErrorEventArgs Saving(string archiveName, ZipEntry entry, Exception exception)
|
||||
{
|
||||
var x = new ZipErrorEventArgs
|
||||
{
|
||||
EventType = ZipProgressEventType.Error_Saving,
|
||||
ArchiveName = archiveName,
|
||||
CurrentEntry = entry,
|
||||
_exc = exception
|
||||
};
|
||||
return x;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the exception that occurred, if any.
|
||||
/// </summary>
|
||||
public Exception @Exception
|
||||
{
|
||||
get { return _exc; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the name of the file that caused the exception, if any.
|
||||
/// </summary>
|
||||
public String FileName
|
||||
{
|
||||
get { return CurrentEntry.LocalFileName; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
// Exceptions.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2008, 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-12 12:19:10>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines exceptions used in the class library.
|
||||
//
|
||||
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
#if !NETCF
|
||||
using System.Runtime.Serialization;
|
||||
#endif
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
///// <summary>
|
||||
///// Base exception type for all custom exceptions in the Zip library. It acts as a marker class.
|
||||
///// </summary>
|
||||
//[AttributeUsage(AttributeTargets.Class)]
|
||||
//public class ZipExceptionAttribute : Attribute { }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Issued when an <c>ZipEntry.ExtractWithPassword()</c> method is invoked
|
||||
/// with an incorrect password.
|
||||
/// </summary>
|
||||
#if !SILVERLIGHT
|
||||
[Serializable]
|
||||
#endif
|
||||
[System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d0000B")]
|
||||
public class BadPasswordException : ZipException
|
||||
{
|
||||
/// <summary>
|
||||
/// Default ctor.
|
||||
/// </summary>
|
||||
public BadPasswordException() { }
|
||||
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="message">The message in the exception.</param>
|
||||
public BadPasswordException(String message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="message">The message in the exception.</param>
|
||||
/// <param name="innerException">The innerException for this exception.</param>
|
||||
public BadPasswordException(String message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#if ! (NETCF || SILVERLIGHT)
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="info">The serialization info for the exception.</param>
|
||||
/// <param name="context">The streaming context from which to deserialize.</param>
|
||||
protected BadPasswordException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that a read was attempted on a stream, and bad or incomplete data was
|
||||
/// received.
|
||||
/// </summary>
|
||||
#if !SILVERLIGHT
|
||||
[Serializable]
|
||||
#endif
|
||||
[System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d0000A")]
|
||||
public class BadReadException : ZipException
|
||||
{
|
||||
/// <summary>
|
||||
/// Default ctor.
|
||||
/// </summary>
|
||||
public BadReadException() { }
|
||||
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="message">The message in the exception.</param>
|
||||
public BadReadException(String message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="message">The message in the exception.</param>
|
||||
/// <param name="innerException">The innerException for this exception.</param>
|
||||
public BadReadException(String message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
#if ! (NETCF || SILVERLIGHT)
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="info">The serialization info for the exception.</param>
|
||||
/// <param name="context">The streaming context from which to deserialize.</param>
|
||||
protected BadReadException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Issued when an CRC check fails upon extracting an entry from a zip archive.
|
||||
/// </summary>
|
||||
#if !SILVERLIGHT
|
||||
[Serializable]
|
||||
#endif
|
||||
[System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d00009")]
|
||||
public class BadCrcException : ZipException
|
||||
{
|
||||
/// <summary>
|
||||
/// Default ctor.
|
||||
/// </summary>
|
||||
public BadCrcException() { }
|
||||
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="message">The message in the exception.</param>
|
||||
public BadCrcException(String message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
|
||||
#if ! (NETCF || SILVERLIGHT)
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="info">The serialization info for the exception.</param>
|
||||
/// <param name="context">The streaming context from which to deserialize.</param>
|
||||
protected BadCrcException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Issued when errors occur saving a self-extracting archive.
|
||||
/// </summary>
|
||||
#if !SILVERLIGHT
|
||||
[Serializable]
|
||||
#endif
|
||||
[System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d00008")]
|
||||
public class SfxGenerationException : ZipException
|
||||
{
|
||||
/// <summary>
|
||||
/// Default ctor.
|
||||
/// </summary>
|
||||
public SfxGenerationException() { }
|
||||
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="message">The message in the exception.</param>
|
||||
public SfxGenerationException(String message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
#if ! (NETCF || SILVERLIGHT)
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="info">The serialization info for the exception.</param>
|
||||
/// <param name="context">The streaming context from which to deserialize.</param>
|
||||
protected SfxGenerationException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an operation was attempted on a ZipFile which was not possible
|
||||
/// given the state of the instance. For example, if you call <c>Save()</c> on a ZipFile
|
||||
/// which has no filename set, you can get this exception.
|
||||
/// </summary>
|
||||
#if !SILVERLIGHT
|
||||
[Serializable]
|
||||
#endif
|
||||
[System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d00007")]
|
||||
public class BadStateException : ZipException
|
||||
{
|
||||
/// <summary>
|
||||
/// Default ctor.
|
||||
/// </summary>
|
||||
public BadStateException() { }
|
||||
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="message">The message in the exception.</param>
|
||||
public BadStateException(String message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="message">The message in the exception.</param>
|
||||
/// <param name="innerException">The innerException for this exception.</param>
|
||||
public BadStateException(String message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{}
|
||||
|
||||
#if ! (NETCF || SILVERLIGHT)
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="info">The serialization info for the exception.</param>
|
||||
/// <param name="context">The streaming context from which to deserialize.</param>
|
||||
protected BadStateException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for all exceptions defined by and throw by the Zip library.
|
||||
/// </summary>
|
||||
#if !SILVERLIGHT
|
||||
[Serializable]
|
||||
#endif
|
||||
[System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d00006")]
|
||||
public class ZipException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Default ctor.
|
||||
/// </summary>
|
||||
public ZipException() { }
|
||||
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="message">The message in the exception.</param>
|
||||
public ZipException(String message) : base(message) { }
|
||||
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="message">The message in the exception.</param>
|
||||
/// <param name="innerException">The innerException for this exception.</param>
|
||||
public ZipException(String message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
#if ! (NETCF || SILVERLIGHT)
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="info">The serialization info for the exception.</param>
|
||||
/// <param name="context">The streaming context from which to deserialize.</param>
|
||||
protected ZipException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
// ExtractExistingFileAction.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-August-25 08:44:37>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the ExtractExistingFileAction enum
|
||||
//
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// An enum for the options when extracting an entry would overwrite an existing file.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This enum describes the actions that the library can take when an
|
||||
/// <c>Extract()</c> or <c>ExtractWithPassword()</c> method is called to extract an
|
||||
/// entry to a filesystem, and the extraction would overwrite an existing filesystem
|
||||
/// file.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
///
|
||||
public enum ExtractExistingFileAction
|
||||
{
|
||||
/// <summary>
|
||||
/// Throw an exception when extraction would overwrite an existing file. (For
|
||||
/// COM clients, this is a 0 (zero).)
|
||||
/// </summary>
|
||||
Throw,
|
||||
|
||||
/// <summary>
|
||||
/// When extraction would overwrite an existing file, overwrite the file silently.
|
||||
/// The overwrite will happen even if the target file is marked as read-only.
|
||||
/// (For COM clients, this is a 1.)
|
||||
/// </summary>
|
||||
OverwriteSilently,
|
||||
|
||||
/// <summary>
|
||||
/// When extraction would overwrite an existing file, don't overwrite the file, silently.
|
||||
/// (For COM clients, this is a 2.)
|
||||
/// </summary>
|
||||
DoNotOverwrite,
|
||||
|
||||
/// <summary>
|
||||
/// When extraction would overwrite an existing file, invoke the ExtractProgress
|
||||
/// event, using an event type of <see
|
||||
/// cref="ZipProgressEventType.Extracting_ExtractEntryWouldOverwrite"/>. In
|
||||
/// this way, the application can decide, just-in-time, whether to overwrite the
|
||||
/// file. For example, a GUI application may wish to pop up a dialog to allow
|
||||
/// the user to choose. You may want to examine the <see
|
||||
/// cref="ExtractProgressEventArgs.ExtractLocation"/> property before making
|
||||
/// the decision. If, after your processing in the Extract progress event, you
|
||||
/// want to NOT extract the file, set <see cref="ZipEntry.ExtractExistingFile"/>
|
||||
/// on the <c>ZipProgressEventArgs.CurrentEntry</c> to <c>DoNotOverwrite</c>.
|
||||
/// If you do want to extract the file, set <c>ZipEntry.ExtractExistingFile</c>
|
||||
/// to <c>OverwriteSilently</c>. If you want to cancel the Extraction, set
|
||||
/// <c>ZipProgressEventArgs.Cancel</c> to true. Cancelling differs from using
|
||||
/// DoNotOverwrite in that a cancel will not extract any further entries, if
|
||||
/// there are any. (For COM clients, the value of this enum is a 3.)
|
||||
/// </summary>
|
||||
InvokeExtractProgressEvent,
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,114 @@
|
||||
// OffsetStream.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-August-27 12:50:35>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines logic for handling reading of zip archives embedded
|
||||
// into larger streams. The initial position of the stream serves as
|
||||
// the base offset for all future Seek() operations.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
internal class OffsetStream : System.IO.Stream, System.IDisposable
|
||||
{
|
||||
private Int64 _originalPosition;
|
||||
private Stream _innerStream;
|
||||
|
||||
public OffsetStream(Stream s)
|
||||
: base()
|
||||
{
|
||||
_originalPosition = s.Position;
|
||||
_innerStream = s;
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
return _innerStream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return _innerStream.CanRead; }
|
||||
}
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return _innerStream.CanSeek; }
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
_innerStream.Flush();
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return _innerStream.Length;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { return _innerStream.Position - _originalPosition; }
|
||||
set { _innerStream.Position = _originalPosition + value; }
|
||||
}
|
||||
|
||||
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
return _innerStream.Seek(_originalPosition + offset, origin) - _originalPosition;
|
||||
}
|
||||
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
base.Close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,901 @@
|
||||
// Shared.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2006-2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-August-02 19:41:01>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines some shared utility classes and methods.
|
||||
//
|
||||
// Created: Tue, 27 Mar 2007 15:30
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Permissions;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
/// <summary>
|
||||
/// Collects general purpose utility methods.
|
||||
/// </summary>
|
||||
internal static class SharedUtilities
|
||||
{
|
||||
/// private null constructor
|
||||
//private SharedUtilities() { }
|
||||
|
||||
// workitem 8423
|
||||
public static Int64 GetFileLength(string fileName)
|
||||
{
|
||||
if (!File.Exists(fileName))
|
||||
throw new System.IO.FileNotFoundException(fileName);
|
||||
|
||||
long fileLength = 0L;
|
||||
FileShare fs = FileShare.ReadWrite;
|
||||
#if !NETCF
|
||||
// FileShare.Delete is not defined for the Compact Framework
|
||||
fs |= FileShare.Delete;
|
||||
#endif
|
||||
using (var s = File.Open(fileName, FileMode.Open, FileAccess.Read, fs))
|
||||
{
|
||||
fileLength = s.Length;
|
||||
}
|
||||
return fileLength;
|
||||
}
|
||||
|
||||
|
||||
[System.Diagnostics.Conditional("NETCF")]
|
||||
public static void Workaround_Ladybug318918(Stream s)
|
||||
{
|
||||
// This is a workaround for this issue:
|
||||
// https://connect.microsoft.com/VisualStudio/feedback/details/318918
|
||||
// It's required only on NETCF.
|
||||
s.Flush();
|
||||
}
|
||||
|
||||
|
||||
#if LEGACY
|
||||
/// <summary>
|
||||
/// Round the given DateTime value to an even second value.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Round up in the case of an odd second value. The rounding does not consider
|
||||
/// fractional seconds.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This is useful because the Zip spec allows storage of time only to the nearest
|
||||
/// even second. So if you want to compare the time of an entry in the archive with
|
||||
/// it's actual time in the filesystem, you need to round the actual filesystem
|
||||
/// time, or use a 2-second threshold for the comparison.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This is most nautrally an extension method for the DateTime class but this
|
||||
/// library is built for .NET 2.0, not for .NET 3.5; This means extension methods
|
||||
/// are a no-no.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="source">The DateTime value to round</param>
|
||||
/// <returns>The ruonded DateTime value</returns>
|
||||
public static DateTime RoundToEvenSecond(DateTime source)
|
||||
{
|
||||
// round to nearest second:
|
||||
if ((source.Second % 2) == 1)
|
||||
source += new TimeSpan(0, 0, 1);
|
||||
|
||||
DateTime dtRounded = new DateTime(source.Year, source.Month, source.Day, source.Hour, source.Minute, source.Second);
|
||||
//if (source.Millisecond >= 500) dtRounded = dtRounded.AddSeconds(1);
|
||||
return dtRounded;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if YOU_LIKE_REDUNDANT_CODE
|
||||
internal static string NormalizePath(string path)
|
||||
{
|
||||
// remove leading single dot slash
|
||||
if (path.StartsWith(".\\")) path = path.Substring(2);
|
||||
|
||||
// remove intervening dot-slash
|
||||
path = path.Replace("\\.\\", "\\");
|
||||
|
||||
// remove double dot when preceded by a directory name
|
||||
var re = new System.Text.RegularExpressions.Regex(@"^(.*\\)?([^\\\.]+\\\.\.\\)(.+)$");
|
||||
path = re.Replace(path, "$1$3");
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
|
||||
private static System.Text.RegularExpressions.Regex doubleDotRegex1 =
|
||||
new System.Text.RegularExpressions.Regex(@"^(.*/)?([^/\\.]+/\\.\\./)(.+)$");
|
||||
|
||||
private static string SimplifyFwdSlashPath(string path)
|
||||
{
|
||||
if (path.StartsWith("./")) path = path.Substring(2);
|
||||
path = path.Replace("/./", "/");
|
||||
|
||||
// Replace foo/anything/../bar with foo/bar
|
||||
path = doubleDotRegex1.Replace(path, "$1$3");
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Utility routine for transforming path names from filesystem format (on Windows that means backslashes) to
|
||||
/// a format suitable for use within zipfiles. This means trimming the volume letter and colon (if any) And
|
||||
/// swapping backslashes for forward slashes.
|
||||
/// </summary>
|
||||
/// <param name="pathName">source path.</param>
|
||||
/// <returns>transformed path</returns>
|
||||
public static string NormalizePathForUseInZipFile(string pathName)
|
||||
{
|
||||
// boundary case
|
||||
if (String.IsNullOrEmpty(pathName)) return pathName;
|
||||
|
||||
// trim volume if necessary
|
||||
if ((pathName.Length >= 2) && ((pathName[1] == ':') && (pathName[2] == '\\')))
|
||||
pathName = pathName.Substring(3);
|
||||
|
||||
// swap slashes
|
||||
pathName = pathName.Replace('\\', '/');
|
||||
|
||||
// trim all leading slashes
|
||||
while (pathName.StartsWith("/")) pathName = pathName.Substring(1);
|
||||
|
||||
return SimplifyFwdSlashPath(pathName);
|
||||
}
|
||||
|
||||
|
||||
static System.Text.Encoding ibm437 = System.Text.Encoding.GetEncoding("IBM437");
|
||||
static System.Text.Encoding utf8 = System.Text.Encoding.GetEncoding("UTF-8");
|
||||
|
||||
internal static byte[] StringToByteArray(string value, System.Text.Encoding encoding)
|
||||
{
|
||||
byte[] a = encoding.GetBytes(value);
|
||||
return a;
|
||||
}
|
||||
internal static byte[] StringToByteArray(string value)
|
||||
{
|
||||
return StringToByteArray(value, ibm437);
|
||||
}
|
||||
|
||||
//internal static byte[] Utf8StringToByteArray(string value)
|
||||
//{
|
||||
// return StringToByteArray(value, utf8);
|
||||
//}
|
||||
|
||||
//internal static string StringFromBuffer(byte[] buf, int maxlength)
|
||||
//{
|
||||
// return StringFromBuffer(buf, maxlength, ibm437);
|
||||
//}
|
||||
|
||||
internal static string Utf8StringFromBuffer(byte[] buf)
|
||||
{
|
||||
return StringFromBuffer(buf, utf8);
|
||||
}
|
||||
|
||||
internal static string StringFromBuffer(byte[] buf, System.Text.Encoding encoding)
|
||||
{
|
||||
// this form of the GetString() method is required for .NET CF compatibility
|
||||
string s = encoding.GetString(buf, 0, buf.Length);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
internal static int ReadSignature(System.IO.Stream s)
|
||||
{
|
||||
int x = 0;
|
||||
try { x = _ReadFourBytes(s, "n/a"); }
|
||||
catch (BadReadException) { }
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
internal static int ReadEntrySignature(System.IO.Stream s)
|
||||
{
|
||||
// handle the case of ill-formatted zip archives - includes a data descriptor
|
||||
// when none is expected.
|
||||
int x = 0;
|
||||
try
|
||||
{
|
||||
x = _ReadFourBytes(s, "n/a");
|
||||
if (x == ZipConstants.ZipEntryDataDescriptorSignature)
|
||||
{
|
||||
// advance past data descriptor - 12 bytes if not zip64
|
||||
s.Seek(12, SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Workaround_Ladybug318918(s);
|
||||
x = _ReadFourBytes(s, "n/a");
|
||||
if (x != ZipConstants.ZipEntrySignature)
|
||||
{
|
||||
// Maybe zip64 was in use for the prior entry.
|
||||
// Therefore, skip another 8 bytes.
|
||||
s.Seek(8, SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Workaround_Ladybug318918(s);
|
||||
x = _ReadFourBytes(s, "n/a");
|
||||
if (x != ZipConstants.ZipEntrySignature)
|
||||
{
|
||||
// seek back to the first spot
|
||||
s.Seek(-24, SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Workaround_Ladybug318918(s);
|
||||
x = _ReadFourBytes(s, "n/a");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (BadReadException) { }
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
internal static int ReadInt(System.IO.Stream s)
|
||||
{
|
||||
return _ReadFourBytes(s, "Could not read block - no data! (position 0x{0:X8})");
|
||||
}
|
||||
|
||||
private static int _ReadFourBytes(System.IO.Stream s, string message)
|
||||
{
|
||||
int n = 0;
|
||||
byte[] block = new byte[4];
|
||||
#if NETCF
|
||||
// workitem 9181
|
||||
// Reading here in NETCF sometimes reads "backwards". Seems to happen for
|
||||
// larger files. Not sure why. Maybe an error in caching. If the data is:
|
||||
//
|
||||
// 00100210: 9efa 0f00 7072 6f6a 6563 742e 6963 7750 ....project.icwP
|
||||
// 00100220: 4b05 0600 0000 0006 0006 0091 0100 008e K...............
|
||||
// 00100230: 0010 0000 00 .....
|
||||
//
|
||||
// ...and the stream Position is 10021F, then a Read of 4 bytes is returning
|
||||
// 50776369, instead of 06054b50. This seems to happen the 2nd time Read()
|
||||
// is called from that Position..
|
||||
//
|
||||
// submitted to connect.microsoft.com
|
||||
// https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=318918#tabs
|
||||
//
|
||||
for (int i = 0; i < block.Length; i++)
|
||||
{
|
||||
n+= s.Read(block, i, 1);
|
||||
}
|
||||
#else
|
||||
n = s.Read(block, 0, block.Length);
|
||||
#endif
|
||||
if (n != block.Length) throw new BadReadException(String.Format(message, s.Position));
|
||||
int data = unchecked((((block[3] * 256 + block[2]) * 256) + block[1]) * 256 + block[0]);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Finds a signature in the zip stream. This is useful for finding
|
||||
/// the end of a zip entry, for example, or the beginning of the next ZipEntry.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Scans through 64k at a time.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// If the method fails to find the requested signature, the stream Position
|
||||
/// after completion of this method is unchanged. If the method succeeds in
|
||||
/// finding the requested signature, the stream position after completion is
|
||||
/// direct AFTER the signature found in the stream.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="stream">The stream to search</param>
|
||||
/// <param name="SignatureToFind">The 4-byte signature to find</param>
|
||||
/// <returns>The number of bytes read</returns>
|
||||
internal static long FindSignature(System.IO.Stream stream, int SignatureToFind)
|
||||
{
|
||||
long startingPosition = stream.Position;
|
||||
|
||||
int BATCH_SIZE = 65536; // 8192;
|
||||
byte[] targetBytes = new byte[4];
|
||||
targetBytes[0] = (byte)(SignatureToFind >> 24);
|
||||
targetBytes[1] = (byte)((SignatureToFind & 0x00FF0000) >> 16);
|
||||
targetBytes[2] = (byte)((SignatureToFind & 0x0000FF00) >> 8);
|
||||
targetBytes[3] = (byte)(SignatureToFind & 0x000000FF);
|
||||
byte[] batch = new byte[BATCH_SIZE];
|
||||
int n = 0;
|
||||
bool success = false;
|
||||
do
|
||||
{
|
||||
n = stream.Read(batch, 0, batch.Length);
|
||||
if (n != 0)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
if (batch[i] == targetBytes[3])
|
||||
{
|
||||
long curPosition = stream.Position;
|
||||
stream.Seek(i - n, System.IO.SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Workaround_Ladybug318918(stream);
|
||||
|
||||
// workitem 7711
|
||||
int sig = ReadSignature(stream);
|
||||
|
||||
success = (sig == SignatureToFind);
|
||||
if (!success)
|
||||
{
|
||||
stream.Seek(curPosition, System.IO.SeekOrigin.Begin);
|
||||
// workitem 10178
|
||||
Workaround_Ladybug318918(stream);
|
||||
}
|
||||
else
|
||||
break; // out of for loop
|
||||
}
|
||||
}
|
||||
}
|
||||
else break;
|
||||
if (success) break;
|
||||
|
||||
} while (true);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
stream.Seek(startingPosition, System.IO.SeekOrigin.Begin);
|
||||
// workitem 10178
|
||||
Workaround_Ladybug318918(stream);
|
||||
return -1; // or throw?
|
||||
}
|
||||
|
||||
// subtract 4 for the signature.
|
||||
long bytesRead = (stream.Position - startingPosition) - 4;
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
|
||||
// If I have a time in the .NET environment, and I want to use it for
|
||||
// SetWastWriteTime() etc, then I need to adjust it for Win32.
|
||||
internal static DateTime AdjustTime_Reverse(DateTime time)
|
||||
{
|
||||
if (time.Kind == DateTimeKind.Utc) return time;
|
||||
DateTime adjusted = time;
|
||||
if (DateTime.Now.IsDaylightSavingTime() && !time.IsDaylightSavingTime())
|
||||
adjusted = time - new System.TimeSpan(1, 0, 0);
|
||||
|
||||
else if (!DateTime.Now.IsDaylightSavingTime() && time.IsDaylightSavingTime())
|
||||
adjusted = time + new System.TimeSpan(1, 0, 0);
|
||||
|
||||
return adjusted;
|
||||
}
|
||||
|
||||
#if NECESSARY
|
||||
// If I read a time from a file with GetLastWriteTime() (etc), I need
|
||||
// to adjust it for display in the .NET environment.
|
||||
internal static DateTime AdjustTime_Forward(DateTime time)
|
||||
{
|
||||
if (time.Kind == DateTimeKind.Utc) return time;
|
||||
DateTime adjusted = time;
|
||||
if (DateTime.Now.IsDaylightSavingTime() && !time.IsDaylightSavingTime())
|
||||
adjusted = time + new System.TimeSpan(1, 0, 0);
|
||||
|
||||
else if (!DateTime.Now.IsDaylightSavingTime() && time.IsDaylightSavingTime())
|
||||
adjusted = time - new System.TimeSpan(1, 0, 0);
|
||||
|
||||
return adjusted;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
internal static DateTime PackedToDateTime(Int32 packedDateTime)
|
||||
{
|
||||
// workitem 7074 & workitem 7170
|
||||
if (packedDateTime == 0xFFFF || packedDateTime == 0)
|
||||
return new System.DateTime(1995, 1, 1, 0, 0, 0, 0); // return a fixed date when none is supplied.
|
||||
|
||||
Int16 packedTime = unchecked((Int16)(packedDateTime & 0x0000ffff));
|
||||
Int16 packedDate = unchecked((Int16)((packedDateTime & 0xffff0000) >> 16));
|
||||
|
||||
int year = 1980 + ((packedDate & 0xFE00) >> 9);
|
||||
int month = (packedDate & 0x01E0) >> 5;
|
||||
int day = packedDate & 0x001F;
|
||||
|
||||
int hour = (packedTime & 0xF800) >> 11;
|
||||
int minute = (packedTime & 0x07E0) >> 5;
|
||||
//int second = packedTime & 0x001F;
|
||||
int second = (packedTime & 0x001F) * 2;
|
||||
|
||||
// validation and error checking.
|
||||
// this is not foolproof but will catch most errors.
|
||||
if (second >= 60) { minute++; second = 0; }
|
||||
if (minute >= 60) { hour++; minute = 0; }
|
||||
if (hour >= 24) { day++; hour = 0; }
|
||||
|
||||
DateTime d = System.DateTime.Now;
|
||||
bool success= false;
|
||||
try
|
||||
{
|
||||
d = new System.DateTime(year, month, day, hour, minute, second, 0);
|
||||
success= true;
|
||||
}
|
||||
catch (System.ArgumentOutOfRangeException)
|
||||
{
|
||||
if (year == 1980 && (month == 0 || day == 0))
|
||||
{
|
||||
try
|
||||
{
|
||||
d = new System.DateTime(1980, 1, 1, hour, minute, second, 0);
|
||||
success= true;
|
||||
}
|
||||
catch (System.ArgumentOutOfRangeException)
|
||||
{
|
||||
try
|
||||
{
|
||||
d = new System.DateTime(1980, 1, 1, 0, 0, 0, 0);
|
||||
success= true;
|
||||
}
|
||||
catch (System.ArgumentOutOfRangeException) { }
|
||||
|
||||
}
|
||||
}
|
||||
// workitem 8814
|
||||
// my god, I can't believe how many different ways applications
|
||||
// can mess up a simple date format.
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
while (year < 1980) year++;
|
||||
while (year > 2030) year--;
|
||||
while (month < 1) month++;
|
||||
while (month > 12) month--;
|
||||
while (day < 1) day++;
|
||||
while (day > 28) day--;
|
||||
while (minute < 0) minute++;
|
||||
while (minute > 59) minute--;
|
||||
while (second < 0) second++;
|
||||
while (second > 59) second--;
|
||||
d = new System.DateTime(year, month, day, hour, minute, second, 0);
|
||||
success= true;
|
||||
}
|
||||
catch (System.ArgumentOutOfRangeException) { }
|
||||
}
|
||||
}
|
||||
if (!success)
|
||||
{
|
||||
string msg = String.Format("y({0}) m({1}) d({2}) h({3}) m({4}) s({5})", year, month, day, hour, minute, second);
|
||||
throw new ZipException(String.Format("Bad date/time format in the zip file. ({0})", msg));
|
||||
|
||||
}
|
||||
// workitem 6191
|
||||
//d = AdjustTime_Reverse(d);
|
||||
d = DateTime.SpecifyKind(d, DateTimeKind.Local);
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
internal
|
||||
static Int32 DateTimeToPacked(DateTime time)
|
||||
{
|
||||
// The time is passed in here only for purposes of writing LastModified to the
|
||||
// zip archive. It should always be LocalTime, but we convert anyway. And,
|
||||
// since the time is being written out, it needs to be adjusted.
|
||||
|
||||
time = time.ToLocalTime();
|
||||
// workitem 7966
|
||||
//time = AdjustTime_Forward(time);
|
||||
|
||||
// see http://www.vsft.com/hal/dostime.htm for the format
|
||||
UInt16 packedDate = (UInt16)((time.Day & 0x0000001F) | ((time.Month << 5) & 0x000001E0) | (((time.Year - 1980) << 9) & 0x0000FE00));
|
||||
UInt16 packedTime = (UInt16)((time.Second / 2 & 0x0000001F) | ((time.Minute << 5) & 0x000007E0) | ((time.Hour << 11) & 0x0000F800));
|
||||
|
||||
Int32 result = (Int32)(((UInt32)(packedDate << 16)) | packedTime);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a pseudo-random filename, suitable for use as a temporary
|
||||
/// file, and open it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The System.IO.Path.GetRandomFileName() method is not available on
|
||||
/// the Compact Framework, so this library provides its own substitute
|
||||
/// on NETCF.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This method produces a filename of the form
|
||||
/// DotNetZip-xxxxxxxx.tmp, where xxxxxxxx is replaced by randomly
|
||||
/// chosen characters, and creates that file.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static void CreateAndOpenUniqueTempFile(string dir,
|
||||
out Stream fs,
|
||||
out string filename)
|
||||
{
|
||||
// workitem 9763
|
||||
// http://dotnet.org.za/markn/archive/2006/04/15/51594.aspx
|
||||
// try 3 times:
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
filename = Path.Combine(dir, InternalGetTempFileName());
|
||||
fs = new FileStream(filename, FileMode.CreateNew);
|
||||
return;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
if (i == 2) throw;
|
||||
}
|
||||
}
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
#if NETCF || SILVERLIGHT
|
||||
public static string InternalGetTempFileName()
|
||||
{
|
||||
return "DotNetZip-" + GenerateRandomStringImpl(8,0) + ".tmp";
|
||||
}
|
||||
|
||||
internal static string GenerateRandomStringImpl(int length, int delta)
|
||||
{
|
||||
bool WantMixedCase = (delta == 0);
|
||||
System.Random rnd = new System.Random();
|
||||
|
||||
string result = "";
|
||||
char[] a = new char[length];
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
// delta == 65 means uppercase
|
||||
// delta == 97 means lowercase
|
||||
if (WantMixedCase)
|
||||
delta = (rnd.Next(2) == 0) ? 65 : 97;
|
||||
a[i] = (char)(rnd.Next(26) + delta);
|
||||
}
|
||||
|
||||
result = new System.String(a);
|
||||
return result;
|
||||
}
|
||||
#else
|
||||
public static string InternalGetTempFileName()
|
||||
{
|
||||
return "DotNetZip-" + Path.GetRandomFileName().Substring(0, 8) + ".tmp";
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Workitem 7889: handle ERROR_LOCK_VIOLATION during read
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This could be gracefully handled with an extension attribute, but
|
||||
/// This assembly is built for .NET 2.0, so I cannot use them.
|
||||
/// </remarks>
|
||||
internal static int ReadWithRetry(System.IO.Stream s, byte[] buffer, int offset, int count, string FileName)
|
||||
{
|
||||
int n = 0;
|
||||
bool done = false;
|
||||
#if !NETCF && !SILVERLIGHT
|
||||
int retries = 0;
|
||||
#endif
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
n = s.Read(buffer, offset, count);
|
||||
done = true;
|
||||
}
|
||||
#if NETCF || SILVERLIGHT
|
||||
catch (System.IO.IOException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
#else
|
||||
catch (System.IO.IOException ioexc1)
|
||||
{
|
||||
// Check if we can call GetHRForException,
|
||||
// which makes unmanaged code calls.
|
||||
var p = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
|
||||
if (p.IsUnrestricted())
|
||||
{
|
||||
uint hresult = _HRForException(ioexc1);
|
||||
if (hresult != 0x80070021) // ERROR_LOCK_VIOLATION
|
||||
throw new System.IO.IOException(String.Format("Cannot read file {0}", FileName), ioexc1);
|
||||
retries++;
|
||||
if (retries > 10)
|
||||
throw new System.IO.IOException(String.Format("Cannot read file {0}, at offset 0x{1:X8} after 10 retries", FileName, offset), ioexc1);
|
||||
|
||||
// max time waited on last retry = 250 + 10*550 = 5.75s
|
||||
// aggregate time waited after 10 retries: 250 + 55*550 = 30.5s
|
||||
System.Threading.Thread.Sleep(250 + retries * 550);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The permission.Demand() failed. Therefore, we cannot call
|
||||
// GetHRForException, and cannot do the subtle handling of
|
||||
// ERROR_LOCK_VIOLATION. Just bail.
|
||||
throw;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
while (!done);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
#if !NETCF
|
||||
// workitem 8009
|
||||
//
|
||||
// This method must remain separate.
|
||||
//
|
||||
// Marshal.GetHRForException() is needed to do special exception handling for
|
||||
// the read. But, that method requires UnmanagedCode permissions, and is marked
|
||||
// with LinkDemand for UnmanagedCode. In an ASP.NET medium trust environment,
|
||||
// where UnmanagedCode is restricted, will generate a SecurityException at the
|
||||
// time of JIT of the method that calls a method that is marked with LinkDemand
|
||||
// for UnmanagedCode. The SecurityException, if it is restricted, will occur
|
||||
// when this method is JITed.
|
||||
//
|
||||
// The Marshal.GetHRForException() is factored out of ReadWithRetry in order to
|
||||
// avoid the SecurityException at JIT compile time. Because _HRForException is
|
||||
// called only when the UnmanagedCode is allowed. This means .NET never
|
||||
// JIT-compiles this method when UnmanagedCode is disallowed, and thus never
|
||||
// generates the JIT-compile time exception.
|
||||
//
|
||||
#endif
|
||||
private static uint _HRForException(System.Exception ex1)
|
||||
{
|
||||
return unchecked((uint)System.Runtime.InteropServices.Marshal.GetHRForException(ex1));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A decorator stream. It wraps another stream, and performs bookkeeping
|
||||
/// to keep track of the stream Position.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// In some cases, it is not possible to get the Position of a stream, let's
|
||||
/// say, on a write-only output stream like ASP.NET's
|
||||
/// <c>Response.OutputStream</c>, or on a different write-only stream
|
||||
/// provided as the destination for the zip by the application. In this
|
||||
/// case, programmers can use this counting stream to count the bytes read
|
||||
/// or written.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Consider the scenario of an application that saves a self-extracting
|
||||
/// archive (SFX), that uses a custom SFX stub.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Saving to a filesystem file, the application would open the
|
||||
/// filesystem file (getting a <c>FileStream</c>), save the custom sfx stub
|
||||
/// into it, and then call <c>ZipFile.Save()</c>, specifying the same
|
||||
/// FileStream. <c>ZipFile.Save()</c> does the right thing for the zipentry
|
||||
/// offsets, by inquiring the Position of the <c>FileStream</c> before writing
|
||||
/// any data, and then adding that initial offset into any ZipEntry
|
||||
/// offsets in the zip directory. Everything works fine.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Now suppose the application is an ASPNET application and it saves
|
||||
/// directly to <c>Response.OutputStream</c>. It's not possible for DotNetZip to
|
||||
/// inquire the <c>Position</c>, so the offsets for the SFX will be wrong.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The workaround is for the application to use this class to wrap
|
||||
/// <c>HttpResponse.OutputStream</c>, then write the SFX stub and the ZipFile
|
||||
/// into that wrapper stream. Because <c>ZipFile.Save()</c> can inquire the
|
||||
/// <c>Position</c>, it will then do the right thing with the offsets.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class CountingStream : System.IO.Stream
|
||||
{
|
||||
// workitem 12374: this class is now public
|
||||
private System.IO.Stream _s;
|
||||
private Int64 _bytesWritten;
|
||||
private Int64 _bytesRead;
|
||||
private Int64 _initialOffset;
|
||||
|
||||
/// <summary>
|
||||
/// The constructor.
|
||||
/// </summary>
|
||||
/// <param name="stream">The underlying stream</param>
|
||||
public CountingStream(System.IO.Stream stream)
|
||||
: base()
|
||||
{
|
||||
_s = stream;
|
||||
try
|
||||
{
|
||||
_initialOffset = _s.Position;
|
||||
}
|
||||
catch
|
||||
{
|
||||
_initialOffset = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the wrapped stream.
|
||||
/// </summary>
|
||||
public Stream WrappedStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return _s;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The count of bytes written out to the stream.
|
||||
/// </summary>
|
||||
public Int64 BytesWritten
|
||||
{
|
||||
get { return _bytesWritten; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// the count of bytes that have been read from the stream.
|
||||
/// </summary>
|
||||
public Int64 BytesRead
|
||||
{
|
||||
get { return _bytesRead; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjust the byte count on the stream.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name='delta'>
|
||||
/// the number of bytes to subtract from the count.
|
||||
/// </param>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Subtract delta from the count of bytes written to the stream.
|
||||
/// This is necessary when seeking back, and writing additional data,
|
||||
/// as happens in some cases when saving Zip files.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void Adjust(Int64 delta)
|
||||
{
|
||||
_bytesWritten -= delta;
|
||||
if (_bytesWritten < 0)
|
||||
throw new InvalidOperationException();
|
||||
if (_s as CountingStream != null)
|
||||
((CountingStream)_s).Adjust(delta);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The read method.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to hold the data read from the stream.</param>
|
||||
/// <param name="offset">the offset within the buffer to copy the first byte read.</param>
|
||||
/// <param name="count">the number of bytes to read.</param>
|
||||
/// <returns>the number of bytes read, after decryption and decompression.</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int n = _s.Read(buffer, offset, count);
|
||||
_bytesRead += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write data into the stream.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer holding data to write to the stream.</param>
|
||||
/// <param name="offset">the offset within that data array to find the first byte to write.</param>
|
||||
/// <param name="count">the number of bytes to write.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (count == 0) return;
|
||||
_s.Write(buffer, offset, count);
|
||||
_bytesWritten += count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the stream can be read.
|
||||
/// </summary>
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return _s.CanRead; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether it is possible to call Seek() on the stream.
|
||||
/// </summary>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return _s.CanSeek; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether it is possible to call Write() on the stream.
|
||||
/// </summary>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return _s.CanWrite; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the underlying stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
_s.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The length of the underlying stream.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get { return _s.Length; } // bytesWritten??
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of number of bytes written, plus the initial
|
||||
/// offset before writing.
|
||||
/// </summary>
|
||||
public long ComputedPosition
|
||||
{
|
||||
get { return _initialOffset + _bytesWritten; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The Position of the stream.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get { return _s.Position; }
|
||||
set
|
||||
{
|
||||
_s.Seek(value, System.IO.SeekOrigin.Begin);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_s);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seek in the stream.
|
||||
/// </summary>
|
||||
/// <param name="offset">the offset point to seek to</param>
|
||||
/// <param name="origin">the reference point from which to seek</param>
|
||||
/// <returns>The new position</returns>
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
return _s.Seek(offset, origin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the length of the underlying stream. Be careful with this!
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name='value'>the length to set on the underlying stream.</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
_s.SetLength(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,941 @@
|
||||
//#define Trace
|
||||
|
||||
// WinZipAes.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-12 13:42:06>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the classes for dealing with WinZip's AES encryption,
|
||||
// according to the specifications for the format available on WinZip's website.
|
||||
//
|
||||
// Created: January 2009
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
#if AESCRYPTO
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a helper class supporting WinZip AES encryption.
|
||||
/// This class is intended for use only by the DotNetZip library.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Most uses of the DotNetZip library will not involve direct calls into
|
||||
/// the WinZipAesCrypto class. Instead, the WinZipAesCrypto class is
|
||||
/// instantiated and used by the ZipEntry() class when WinZip AES
|
||||
/// encryption or decryption on an entry is employed.
|
||||
/// </remarks>
|
||||
internal class WinZipAesCrypto
|
||||
{
|
||||
internal byte[] _Salt;
|
||||
internal byte[] _providedPv;
|
||||
internal byte[] _generatedPv;
|
||||
internal int _KeyStrengthInBits;
|
||||
private byte[] _MacInitializationVector;
|
||||
private byte[] _StoredMac;
|
||||
private byte[] _keyBytes;
|
||||
private Int16 PasswordVerificationStored;
|
||||
private Int16 PasswordVerificationGenerated;
|
||||
private int Rfc2898KeygenIterations = 1000;
|
||||
private string _Password;
|
||||
private bool _cryptoGenerated ;
|
||||
|
||||
private WinZipAesCrypto(string password, int KeyStrengthInBits)
|
||||
{
|
||||
_Password = password;
|
||||
_KeyStrengthInBits = KeyStrengthInBits;
|
||||
}
|
||||
|
||||
public static WinZipAesCrypto Generate(string password, int KeyStrengthInBits)
|
||||
{
|
||||
WinZipAesCrypto c = new WinZipAesCrypto(password, KeyStrengthInBits);
|
||||
|
||||
int saltSizeInBytes = c._KeyStrengthInBytes / 2;
|
||||
c._Salt = new byte[saltSizeInBytes];
|
||||
Random rnd = new Random();
|
||||
rnd.NextBytes(c._Salt);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static WinZipAesCrypto ReadFromStream(string password, int KeyStrengthInBits, Stream s)
|
||||
{
|
||||
// from http://www.winzip.com/aes_info.htm
|
||||
//
|
||||
// Size(bytes) Content
|
||||
// -----------------------------------
|
||||
// Variable Salt value
|
||||
// 2 Password verification value
|
||||
// Variable Encrypted file data
|
||||
// 10 Authentication code
|
||||
//
|
||||
// ZipEntry.CompressedSize represents the size of all of those elements.
|
||||
|
||||
// salt size varies with key length:
|
||||
// 128 bit key => 8 bytes salt
|
||||
// 192 bits => 12 bytes salt
|
||||
// 256 bits => 16 bytes salt
|
||||
|
||||
WinZipAesCrypto c = new WinZipAesCrypto(password, KeyStrengthInBits);
|
||||
|
||||
int saltSizeInBytes = c._KeyStrengthInBytes / 2;
|
||||
c._Salt = new byte[saltSizeInBytes];
|
||||
c._providedPv = new byte[2];
|
||||
|
||||
s.Read(c._Salt, 0, c._Salt.Length);
|
||||
s.Read(c._providedPv, 0, c._providedPv.Length);
|
||||
|
||||
c.PasswordVerificationStored = (Int16)(c._providedPv[0] + c._providedPv[1] * 256);
|
||||
if (password != null)
|
||||
{
|
||||
c.PasswordVerificationGenerated = (Int16)(c.GeneratedPV[0] + c.GeneratedPV[1] * 256);
|
||||
if (c.PasswordVerificationGenerated != c.PasswordVerificationStored)
|
||||
throw new BadPasswordException("bad password");
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
public byte[] GeneratedPV
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_cryptoGenerated) _GenerateCryptoBytes();
|
||||
return _generatedPv;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public byte[] Salt
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Salt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private int _KeyStrengthInBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
return _KeyStrengthInBits / 8;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public int SizeOfEncryptionMetadata
|
||||
{
|
||||
get
|
||||
{
|
||||
// 10 bytes after, (n-10) before the compressed data
|
||||
return _KeyStrengthInBytes / 2 + 10 + 2;
|
||||
}
|
||||
}
|
||||
|
||||
public string Password
|
||||
{
|
||||
set
|
||||
{
|
||||
_Password = value;
|
||||
if (_Password != null)
|
||||
{
|
||||
PasswordVerificationGenerated = (Int16)(GeneratedPV[0] + GeneratedPV[1] * 256);
|
||||
if (PasswordVerificationGenerated != PasswordVerificationStored)
|
||||
throw new Ionic.Zip.BadPasswordException();
|
||||
}
|
||||
}
|
||||
private get
|
||||
{
|
||||
return _Password;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void _GenerateCryptoBytes()
|
||||
{
|
||||
//Console.WriteLine(" provided password: '{0}'", _Password);
|
||||
|
||||
System.Security.Cryptography.Rfc2898DeriveBytes rfc2898 =
|
||||
new System.Security.Cryptography.Rfc2898DeriveBytes(_Password, Salt, Rfc2898KeygenIterations);
|
||||
|
||||
_keyBytes = rfc2898.GetBytes(_KeyStrengthInBytes); // 16 or 24 or 32 ???
|
||||
_MacInitializationVector = rfc2898.GetBytes(_KeyStrengthInBytes);
|
||||
_generatedPv = rfc2898.GetBytes(2);
|
||||
|
||||
_cryptoGenerated = true;
|
||||
}
|
||||
|
||||
|
||||
public byte[] KeyBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_cryptoGenerated) _GenerateCryptoBytes();
|
||||
return _keyBytes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public byte[] MacIv
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_cryptoGenerated) _GenerateCryptoBytes();
|
||||
return _MacInitializationVector;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] CalculatedMac;
|
||||
|
||||
|
||||
public void ReadAndVerifyMac(System.IO.Stream s)
|
||||
{
|
||||
bool invalid = false;
|
||||
|
||||
// read integrityCheckVector.
|
||||
// caller must ensure that the file pointer is in the right spot!
|
||||
_StoredMac = new byte[10]; // aka "authentication code"
|
||||
s.Read(_StoredMac, 0, _StoredMac.Length);
|
||||
|
||||
if (_StoredMac.Length != CalculatedMac.Length)
|
||||
invalid = true;
|
||||
|
||||
if (!invalid)
|
||||
{
|
||||
for (int i = 0; i < _StoredMac.Length; i++)
|
||||
{
|
||||
if (_StoredMac[i] != CalculatedMac[i])
|
||||
invalid = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (invalid)
|
||||
throw new Ionic.Zip.BadStateException("The MAC does not match.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#region DONT_COMPILE_BUT_KEEP_FOR_POTENTIAL_FUTURE_USE
|
||||
#if NO
|
||||
internal class Util
|
||||
{
|
||||
private static void _Format(System.Text.StringBuilder sb1,
|
||||
byte[] b,
|
||||
int offset,
|
||||
int length)
|
||||
{
|
||||
|
||||
System.Text.StringBuilder sb2 = new System.Text.StringBuilder();
|
||||
sb1.Append("0000 ");
|
||||
int i;
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
int x = offset+i;
|
||||
if (i != 0 && i % 16 == 0)
|
||||
{
|
||||
sb1.Append(" ")
|
||||
.Append(sb2)
|
||||
.Append("\n")
|
||||
.Append(String.Format("{0:X4} ", i));
|
||||
sb2.Remove(0,sb2.Length);
|
||||
}
|
||||
sb1.Append(System.String.Format("{0:X2} ", b[x]));
|
||||
if (b[x] >=32 && b[x] <= 126)
|
||||
sb2.Append((char)b[x]);
|
||||
else
|
||||
sb2.Append(".");
|
||||
}
|
||||
if (sb2.Length > 0)
|
||||
{
|
||||
sb1.Append(new String(' ', ((16 - i%16) * 3) + 4))
|
||||
.Append(sb2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal static string FormatByteArray(byte[] b, int limit)
|
||||
{
|
||||
System.Text.StringBuilder sb1 = new System.Text.StringBuilder();
|
||||
|
||||
if ((limit * 2 > b.Length) || limit == 0)
|
||||
{
|
||||
_Format(sb1, b, 0, b.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
// first N bytes of the buffer
|
||||
_Format(sb1, b, 0, limit);
|
||||
|
||||
if (b.Length > limit * 2)
|
||||
sb1.Append(String.Format("\n ...({0} other bytes here)....\n", b.Length - limit * 2));
|
||||
|
||||
// last N bytes of the buffer
|
||||
_Format(sb1, b, b.Length - limit, limit);
|
||||
}
|
||||
|
||||
return sb1.ToString();
|
||||
}
|
||||
|
||||
|
||||
internal static string FormatByteArray(byte[] b)
|
||||
{
|
||||
return FormatByteArray(b, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A stream that encrypts as it writes, or decrypts as it reads. The
|
||||
/// Crypto is AES in CTR (counter) mode, which is compatible with the AES
|
||||
/// encryption employed by WinZip 12.0.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The AES/CTR encryption protocol used by WinZip works like this:
|
||||
///
|
||||
/// - start with a counter, initialized to zero.
|
||||
///
|
||||
/// - to encrypt, take the data by 16-byte blocks. For each block:
|
||||
/// - apply the transform to the counter
|
||||
/// - increement the counter
|
||||
/// - XOR the result of the transform with the plaintext to
|
||||
/// get the ciphertext.
|
||||
/// - compute the mac on the encrypted bytes
|
||||
/// - when finished with all blocks, store the computed MAC.
|
||||
///
|
||||
/// - to decrypt, take the data by 16-byte blocks. For each block:
|
||||
/// - compute the mac on the encrypted bytes,
|
||||
/// - apply the transform to the counter
|
||||
/// - increement the counter
|
||||
/// - XOR the result of the transform with the ciphertext to
|
||||
/// get the plaintext.
|
||||
/// - when finished with all blocks, compare the computed MAC against
|
||||
/// the stored MAC
|
||||
///
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
//
|
||||
internal class WinZipAesCipherStream : Stream
|
||||
{
|
||||
private WinZipAesCrypto _params;
|
||||
private System.IO.Stream _s;
|
||||
private CryptoMode _mode;
|
||||
private int _nonce;
|
||||
private bool _finalBlock;
|
||||
|
||||
internal HMACSHA1 _mac;
|
||||
|
||||
// Use RijndaelManaged from .NET 2.0.
|
||||
// AesManaged came in .NET 3.5, but we want to limit
|
||||
// dependency to .NET 2.0. AES is just a restricted form
|
||||
// of Rijndael (fixed block size of 128, some crypto modes not supported).
|
||||
|
||||
internal RijndaelManaged _aesCipher;
|
||||
internal ICryptoTransform _xform;
|
||||
|
||||
private const int BLOCK_SIZE_IN_BYTES = 16;
|
||||
|
||||
private byte[] counter = new byte[BLOCK_SIZE_IN_BYTES];
|
||||
private byte[] counterOut = new byte[BLOCK_SIZE_IN_BYTES];
|
||||
|
||||
// I've had a problem when wrapping a WinZipAesCipherStream inside
|
||||
// a DeflateStream. Calling Read() on the DeflateStream results in
|
||||
// a Read() on the WinZipAesCipherStream, but the buffer is larger
|
||||
// than the total size of the encrypted data, and larger than the
|
||||
// initial Read() on the DeflateStream! When the encrypted
|
||||
// bytestream is embedded within a larger stream (As in a zip
|
||||
// archive), the Read() doesn't fail with EOF. This causes bad
|
||||
// data to be returned, and it messes up the MAC.
|
||||
|
||||
// This field is used to provide a hard-stop to the size of
|
||||
// data that can be read from the stream. In Read(), if the buffer or
|
||||
// read request goes beyond the stop, we truncate it.
|
||||
|
||||
private long _length;
|
||||
private long _totalBytesXferred;
|
||||
private byte[] _PendingWriteBlock;
|
||||
private int _pendingCount;
|
||||
private byte[] _iobuf;
|
||||
|
||||
/// <summary>
|
||||
/// The constructor.
|
||||
/// </summary>
|
||||
/// <param name="s">The underlying stream</param>
|
||||
/// <param name="mode">To either encrypt or decrypt.</param>
|
||||
/// <param name="cryptoParams">The pre-initialized WinZipAesCrypto object.</param>
|
||||
/// <param name="length">The maximum number of bytes to read from the stream.</param>
|
||||
internal WinZipAesCipherStream(System.IO.Stream s, WinZipAesCrypto cryptoParams, long length, CryptoMode mode)
|
||||
: this(s, cryptoParams, mode)
|
||||
{
|
||||
// don't read beyond this limit!
|
||||
_length = length;
|
||||
//Console.WriteLine("max length of AES stream: {0}", _length);
|
||||
}
|
||||
|
||||
|
||||
#if WANT_TRACE
|
||||
Stream untransformed;
|
||||
String traceFileUntransformed;
|
||||
Stream transformed;
|
||||
String traceFileTransformed;
|
||||
#endif
|
||||
|
||||
|
||||
internal WinZipAesCipherStream(System.IO.Stream s, WinZipAesCrypto cryptoParams, CryptoMode mode)
|
||||
: base()
|
||||
{
|
||||
TraceOutput("-------------------------------------------------------");
|
||||
TraceOutput("Create {0:X8}", this.GetHashCode());
|
||||
|
||||
_params = cryptoParams;
|
||||
_s = s;
|
||||
_mode = mode;
|
||||
_nonce = 1;
|
||||
|
||||
if (_params == null)
|
||||
throw new BadPasswordException("Supply a password to use AES encryption.");
|
||||
|
||||
int keySizeInBits = _params.KeyBytes.Length * 8;
|
||||
if (keySizeInBits != 256 && keySizeInBits != 128 && keySizeInBits != 192)
|
||||
throw new ArgumentOutOfRangeException("keysize",
|
||||
"size of key must be 128, 192, or 256");
|
||||
|
||||
_mac = new HMACSHA1(_params.MacIv);
|
||||
|
||||
_aesCipher = new System.Security.Cryptography.RijndaelManaged();
|
||||
_aesCipher.BlockSize = 128;
|
||||
_aesCipher.KeySize = keySizeInBits; // 128, 192, 256
|
||||
_aesCipher.Mode = CipherMode.ECB;
|
||||
_aesCipher.Padding = PaddingMode.None;
|
||||
|
||||
byte[] iv = new byte[BLOCK_SIZE_IN_BYTES]; // all zeroes
|
||||
|
||||
// Create an ENCRYPTOR, regardless whether doing decryption or encryption.
|
||||
// It is reflexive.
|
||||
_xform = _aesCipher.CreateEncryptor(_params.KeyBytes, iv);
|
||||
|
||||
if (_mode == CryptoMode.Encrypt)
|
||||
{
|
||||
_iobuf = new byte[2048];
|
||||
_PendingWriteBlock = new byte[BLOCK_SIZE_IN_BYTES];
|
||||
}
|
||||
|
||||
|
||||
#if WANT_TRACE
|
||||
traceFileUntransformed = "unpack\\WinZipAesCipherStream.trace.untransformed.out";
|
||||
traceFileTransformed = "unpack\\WinZipAesCipherStream.trace.transformed.out";
|
||||
|
||||
untransformed = System.IO.File.Create(traceFileUntransformed);
|
||||
transformed = System.IO.File.Create(traceFileTransformed);
|
||||
#endif
|
||||
}
|
||||
|
||||
private void XorInPlace(byte[] buffer, int offset, int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
buffer[offset + i] = (byte)(counterOut[i] ^ buffer[offset + i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteTransformOneBlock(byte[] buffer, int offset)
|
||||
{
|
||||
System.Array.Copy(BitConverter.GetBytes(_nonce++), 0, counter, 0, 4);
|
||||
_xform.TransformBlock(counter,
|
||||
0,
|
||||
BLOCK_SIZE_IN_BYTES,
|
||||
counterOut,
|
||||
0);
|
||||
XorInPlace(buffer, offset, BLOCK_SIZE_IN_BYTES);
|
||||
_mac.TransformBlock(buffer, offset, BLOCK_SIZE_IN_BYTES, null, 0);
|
||||
}
|
||||
|
||||
|
||||
private void WriteTransformBlocks(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int posn = offset;
|
||||
int last = count + offset;
|
||||
|
||||
while (posn < buffer.Length && posn < last)
|
||||
{
|
||||
WriteTransformOneBlock (buffer, posn);
|
||||
posn += BLOCK_SIZE_IN_BYTES;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void WriteTransformFinalBlock()
|
||||
{
|
||||
if (_pendingCount == 0)
|
||||
throw new InvalidOperationException("No bytes available.");
|
||||
|
||||
if (_finalBlock)
|
||||
throw new InvalidOperationException("The final block has already been transformed.");
|
||||
|
||||
System.Array.Copy(BitConverter.GetBytes(_nonce++), 0, counter, 0, 4);
|
||||
counterOut = _xform.TransformFinalBlock(counter,
|
||||
0,
|
||||
BLOCK_SIZE_IN_BYTES);
|
||||
XorInPlace(_PendingWriteBlock, 0, _pendingCount);
|
||||
_mac.TransformFinalBlock(_PendingWriteBlock, 0, _pendingCount);
|
||||
_finalBlock = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private int ReadTransformOneBlock(byte[] buffer, int offset, int last)
|
||||
{
|
||||
if (_finalBlock)
|
||||
throw new NotSupportedException();
|
||||
|
||||
int bytesRemaining = last - offset;
|
||||
int bytesToRead = (bytesRemaining > BLOCK_SIZE_IN_BYTES)
|
||||
? BLOCK_SIZE_IN_BYTES
|
||||
: bytesRemaining;
|
||||
|
||||
// update the counter
|
||||
System.Array.Copy(BitConverter.GetBytes(_nonce++), 0, counter, 0, 4);
|
||||
|
||||
// Determine if this is the final block
|
||||
if ((bytesToRead == bytesRemaining) &&
|
||||
(_length > 0) &&
|
||||
(_totalBytesXferred + last == _length))
|
||||
{
|
||||
_mac.TransformFinalBlock(buffer, offset, bytesToRead);
|
||||
counterOut = _xform.TransformFinalBlock(counter,
|
||||
0,
|
||||
BLOCK_SIZE_IN_BYTES);
|
||||
_finalBlock = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_mac.TransformBlock(buffer, offset, bytesToRead, null, 0);
|
||||
_xform.TransformBlock(counter,
|
||||
0, // offset
|
||||
BLOCK_SIZE_IN_BYTES,
|
||||
counterOut,
|
||||
0); // offset
|
||||
}
|
||||
|
||||
XorInPlace(buffer, offset, bytesToRead);
|
||||
return bytesToRead;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void ReadTransformBlocks(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int posn = offset;
|
||||
int last = count + offset;
|
||||
|
||||
while (posn < buffer.Length && posn < last )
|
||||
{
|
||||
int n = ReadTransformOneBlock (buffer, posn, last);
|
||||
posn += n;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_mode == CryptoMode.Encrypt)
|
||||
throw new NotSupportedException();
|
||||
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException("buffer");
|
||||
|
||||
if (offset < 0)
|
||||
throw new ArgumentOutOfRangeException("offset",
|
||||
"Must not be less than zero.");
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException("count",
|
||||
"Must not be less than zero.");
|
||||
|
||||
if (buffer.Length < offset + count)
|
||||
throw new ArgumentException("The buffer is too small");
|
||||
|
||||
// When I wrap a WinZipAesStream in a DeflateStream, the
|
||||
// DeflateStream asks its captive to read 4k blocks, even if the
|
||||
// encrypted bytestream is smaller than that. This is a way to
|
||||
// limit the number of bytes read.
|
||||
|
||||
int bytesToRead = count;
|
||||
|
||||
if (_totalBytesXferred >= _length)
|
||||
{
|
||||
return 0; // EOF
|
||||
}
|
||||
|
||||
long bytesRemaining = _length - _totalBytesXferred;
|
||||
if (bytesRemaining < count) bytesToRead = (int)bytesRemaining;
|
||||
|
||||
int n = _s.Read(buffer, offset, bytesToRead);
|
||||
|
||||
|
||||
#if WANT_TRACE
|
||||
untransformed.Write(buffer, offset, bytesToRead);
|
||||
#endif
|
||||
|
||||
ReadTransformBlocks(buffer, offset, bytesToRead);
|
||||
|
||||
#if WANT_TRACE
|
||||
transformed.Write(buffer, offset, bytesToRead);
|
||||
#endif
|
||||
_totalBytesXferred += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the final HMAC-SHA1-80 for the data that was encrypted.
|
||||
/// </summary>
|
||||
public byte[] FinalAuthentication
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_finalBlock)
|
||||
{
|
||||
// special-case zero-byte files
|
||||
if ( _totalBytesXferred != 0)
|
||||
throw new BadStateException("The final hash has not been computed.");
|
||||
|
||||
// Must call ComputeHash on an empty byte array when no data
|
||||
// has run through the MAC.
|
||||
|
||||
byte[] b = { };
|
||||
_mac.ComputeHash(b);
|
||||
// fall through
|
||||
}
|
||||
byte[] macBytes10 = new byte[10];
|
||||
System.Array.Copy(_mac.Hash, 0, macBytes10, 0, 10);
|
||||
return macBytes10;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_finalBlock)
|
||||
throw new InvalidOperationException("The final block has already been transformed.");
|
||||
|
||||
if (_mode == CryptoMode.Decrypt)
|
||||
throw new NotSupportedException();
|
||||
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException("buffer");
|
||||
|
||||
if (offset < 0)
|
||||
throw new ArgumentOutOfRangeException("offset",
|
||||
"Must not be less than zero.");
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException("count",
|
||||
"Must not be less than zero.");
|
||||
if (buffer.Length < offset + count)
|
||||
throw new ArgumentException("The offset and count are too large");
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
TraceOutput("Write off({0}) count({1})", offset, count);
|
||||
|
||||
#if WANT_TRACE
|
||||
untransformed.Write(buffer, offset, count);
|
||||
#endif
|
||||
|
||||
// For proper AES encryption, an AES encryptor application calls
|
||||
// TransformBlock repeatedly for all 16-byte blocks except the
|
||||
// last. For the last block, it then calls TransformFinalBlock().
|
||||
//
|
||||
// This class is a stream that encrypts via Write(). But, it's not
|
||||
// possible to recognize which are the "last" bytes from within the call
|
||||
// to Write(). The caller can call Write() several times in succession,
|
||||
// with varying buffers. This class only "knows" that the last bytes
|
||||
// have been written when the app calls Close().
|
||||
//
|
||||
// Therefore, this class buffers writes: After completion every Write(),
|
||||
// a 16-byte "pending" block (_PendingWriteBlock) must hold between 1
|
||||
// and 16 bytes, which will be used in TransformFinalBlock if the app
|
||||
// calls Close() immediately thereafter. Also, every write must
|
||||
// transform any pending bytes, before transforming the data passed in
|
||||
// to the current call.
|
||||
//
|
||||
// In operation, after the first call to Write() and before the call to
|
||||
// Close(), one full or partial block of bytes is always available,
|
||||
// pending. At time of Close(), this class calls
|
||||
// WriteTransformFinalBlock() to flush the pending bytes.
|
||||
//
|
||||
// This approach works whether the caller writes in odd-sized batches,
|
||||
// for example 5000 bytes, or in batches that are neat multiples of the
|
||||
// blocksize (16).
|
||||
//
|
||||
// Logicaly, what we do is this:
|
||||
//
|
||||
// 1. if there are fewer than 16 bytes (pending + current), then
|
||||
// just copy them into th pending buffer and return.
|
||||
//
|
||||
// 2. there are more than 16 bytes to write. So, take the leading slice
|
||||
// of bytes from the current buffer, enough to fill the pending
|
||||
// buffer. Transform the pending block, and write it out.
|
||||
//
|
||||
// 3. Take the trailing slice of bytes (a full block or a partial block),
|
||||
// and copy it to the pending block for next time.
|
||||
//
|
||||
// 4. transform and write all the other blocks, the middle slice.
|
||||
//
|
||||
|
||||
// There are 16 or fewer bytes, so just buffer the bytes.
|
||||
if (count + _pendingCount <= BLOCK_SIZE_IN_BYTES)
|
||||
{
|
||||
Buffer.BlockCopy(buffer,
|
||||
offset,
|
||||
_PendingWriteBlock,
|
||||
_pendingCount,
|
||||
count);
|
||||
_pendingCount += count;
|
||||
|
||||
// At this point, _PendingWriteBlock contains up to
|
||||
// BLOCK_SIZE_IN_BYTES bytes, and _pendingCount ranges from 0 to
|
||||
// BLOCK_SIZE_IN_BYTES. We don't want to xform+write them yet,
|
||||
// because this may have been the last block. The last block gets
|
||||
// written at Close().
|
||||
return;
|
||||
}
|
||||
|
||||
// We know there are at least 17 bytes, counting those in the current
|
||||
// buffer, along with the (possibly empty) pending block.
|
||||
|
||||
int bytesRemaining = count;
|
||||
int curOffset = offset;
|
||||
|
||||
// workitem 12815
|
||||
//
|
||||
// xform chunkwise ... Cannot transform in place using the original
|
||||
// buffer because that is user-maintained.
|
||||
|
||||
if (_pendingCount != 0)
|
||||
{
|
||||
// We have more than one block of data to write, therefore it is safe
|
||||
// to xform+write.
|
||||
int fillCount = BLOCK_SIZE_IN_BYTES - _pendingCount;
|
||||
|
||||
// fillCount is possibly zero here. That happens when the pending
|
||||
// buffer held 16 bytes (one complete block) before this call to
|
||||
// Write.
|
||||
if (fillCount > 0)
|
||||
{
|
||||
Buffer.BlockCopy(buffer,
|
||||
offset,
|
||||
_PendingWriteBlock,
|
||||
_pendingCount,
|
||||
fillCount);
|
||||
|
||||
// adjust counts:
|
||||
bytesRemaining -= fillCount;
|
||||
curOffset += fillCount;
|
||||
}
|
||||
|
||||
// xform and write:
|
||||
WriteTransformOneBlock(_PendingWriteBlock, 0);
|
||||
_s.Write(_PendingWriteBlock, 0, BLOCK_SIZE_IN_BYTES);
|
||||
_totalBytesXferred += BLOCK_SIZE_IN_BYTES;
|
||||
_pendingCount = 0;
|
||||
}
|
||||
|
||||
// At this point _PendingWriteBlock is empty, and bytesRemaining is
|
||||
// always greater than 0.
|
||||
|
||||
// Now, xform N blocks, where N = floor((bytesRemaining-1)/16). If
|
||||
// writing 32 bytes, then xform 1 block, and stage the remaining 16. If
|
||||
// writing 10037 bytes, xform 627 blocks of 16 bytes, then stage the
|
||||
// remaining 5 bytes.
|
||||
|
||||
int blocksToXform = (bytesRemaining-1)/BLOCK_SIZE_IN_BYTES;
|
||||
_pendingCount = bytesRemaining - (blocksToXform * BLOCK_SIZE_IN_BYTES);
|
||||
|
||||
// _pendingCount is ALWAYS between 1 and 16.
|
||||
// Put the last _pendingCount bytes into the pending block.
|
||||
Buffer.BlockCopy(buffer,
|
||||
curOffset + bytesRemaining - _pendingCount,
|
||||
_PendingWriteBlock,
|
||||
0,
|
||||
_pendingCount);
|
||||
bytesRemaining -= _pendingCount;
|
||||
_totalBytesXferred += bytesRemaining; // will be true after the loop
|
||||
|
||||
// now, transform all the full blocks preceding that.
|
||||
// bytesRemaining is always a multiple of 16 .
|
||||
if (blocksToXform > 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
int c = _iobuf.Length;
|
||||
if (c > bytesRemaining) c = bytesRemaining;
|
||||
Buffer.BlockCopy(buffer,
|
||||
curOffset,
|
||||
_iobuf,
|
||||
0,
|
||||
c);
|
||||
|
||||
WriteTransformBlocks(_iobuf, 0, c);
|
||||
_s.Write(_iobuf, 0, c);
|
||||
bytesRemaining -= c;
|
||||
curOffset += c;
|
||||
} while(bytesRemaining > 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Close the stream.
|
||||
/// </summary>
|
||||
public override void Close()
|
||||
{
|
||||
TraceOutput("Close {0:X8}", this.GetHashCode());
|
||||
|
||||
// In the degenerate case, no bytes have been written to the
|
||||
// stream at all. Need to check here, and NOT emit the
|
||||
// final block if Write has not been called.
|
||||
if (_pendingCount > 0)
|
||||
{
|
||||
WriteTransformFinalBlock();
|
||||
_s.Write(_PendingWriteBlock, 0, _pendingCount);
|
||||
_totalBytesXferred += _pendingCount;
|
||||
_pendingCount = 0;
|
||||
}
|
||||
_s.Close();
|
||||
|
||||
#if WANT_TRACE
|
||||
untransformed.Close();
|
||||
transformed.Close();
|
||||
Console.WriteLine("\nuntransformed bytestream is in {0}", traceFileUntransformed);
|
||||
Console.WriteLine("\ntransformed bytestream is in {0}", traceFileTransformed);
|
||||
#endif
|
||||
TraceOutput("-------------------------------------------------------");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the stream can be read.
|
||||
/// </summary>
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_mode != CryptoMode.Decrypt) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Always returns false.
|
||||
/// </summary>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the CryptoMode is Encrypt.
|
||||
/// </summary>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return (_mode == CryptoMode.Encrypt); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flush the content in the stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
_s.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Getting this property throws a NotImplementedException.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Getting or Setting this property throws a NotImplementedException.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
set { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method throws a NotImplementedException.
|
||||
/// </summary>
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method throws a NotImplementedException.
|
||||
/// </summary>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
|
||||
[System.Diagnostics.ConditionalAttribute("Trace")]
|
||||
private void TraceOutput(string format, params object[] varParams)
|
||||
{
|
||||
lock(_outputLock)
|
||||
{
|
||||
int tid = System.Threading.Thread.CurrentThread.GetHashCode();
|
||||
Console.ForegroundColor = (ConsoleColor) (tid % 8 + 8);
|
||||
Console.Write("{0:000} WZACS ", tid);
|
||||
Console.WriteLine(format, varParams);
|
||||
Console.ResetColor();
|
||||
}
|
||||
}
|
||||
|
||||
private object _outputLock = new Object();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,51 @@
|
||||
// ZipConstants.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2006, 2007, 2008, 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-August-27 23:22:32>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines a few constants that are used in the project.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
static class ZipConstants
|
||||
{
|
||||
public const UInt32 PackedToRemovableMedia = 0x30304b50;
|
||||
public const UInt32 Zip64EndOfCentralDirectoryRecordSignature = 0x06064b50;
|
||||
public const UInt32 Zip64EndOfCentralDirectoryLocatorSignature = 0x07064b50;
|
||||
public const UInt32 EndOfCentralDirectorySignature = 0x06054b50;
|
||||
public const int ZipEntrySignature = 0x04034b50;
|
||||
public const int ZipEntryDataDescriptorSignature = 0x08074b50;
|
||||
public const int SplitArchiveSignature = 0x08074b50;
|
||||
public const int ZipDirEntrySignature = 0x02014b50;
|
||||
|
||||
|
||||
// These are dictated by the Zip Spec.See APPNOTE.txt
|
||||
public const int AesKeySize = 192; // 128, 192, 256
|
||||
public const int AesBlockSize = 128; // ???
|
||||
|
||||
public const UInt16 AesAlgId128 = 0x660E;
|
||||
public const UInt16 AesAlgId192 = 0x660F;
|
||||
public const UInt16 AesAlgId256 = 0x6610;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,455 @@
|
||||
// ZipCrypto.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2008, 2009, 2011 Dino Chiesa
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-28 06:30:59>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module provides the implementation for "traditional" Zip encryption.
|
||||
//
|
||||
// Created Tue Apr 15 17:39:56 2008
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
/// <summary>
|
||||
/// This class implements the "traditional" or "classic" PKZip encryption,
|
||||
/// which today is considered to be weak. On the other hand it is
|
||||
/// ubiquitous. This class is intended for use only by the DotNetZip
|
||||
/// library.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Most uses of the DotNetZip library will not involve direct calls into
|
||||
/// the ZipCrypto class. Instead, the ZipCrypto class is instantiated and
|
||||
/// used by the ZipEntry() class when encryption or decryption on an entry
|
||||
/// is employed. If for some reason you really wanted to use a weak
|
||||
/// encryption algorithm in some other application, you might use this
|
||||
/// library. But you would be much better off using one of the built-in
|
||||
/// strong encryption libraries in the .NET Framework, like the AES
|
||||
/// algorithm or SHA.
|
||||
/// </remarks>
|
||||
internal class ZipCrypto
|
||||
{
|
||||
/// <summary>
|
||||
/// The default constructor for ZipCrypto.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// This class is intended for internal use by the library only. It's
|
||||
/// probably not useful to you. Seriously. Stop reading this
|
||||
/// documentation. It's a waste of your time. Go do something else.
|
||||
/// Check the football scores. Go get an ice cream with a friend.
|
||||
/// Seriously.
|
||||
/// </remarks>
|
||||
///
|
||||
private ZipCrypto() { }
|
||||
|
||||
public static ZipCrypto ForWrite(string password)
|
||||
{
|
||||
ZipCrypto z = new ZipCrypto();
|
||||
if (password == null)
|
||||
throw new BadPasswordException("This entry requires a password.");
|
||||
z.InitCipher(password);
|
||||
return z;
|
||||
}
|
||||
|
||||
|
||||
public static ZipCrypto ForRead(string password, ZipEntry e)
|
||||
{
|
||||
System.IO.Stream s = e._archiveStream;
|
||||
e._WeakEncryptionHeader = new byte[12];
|
||||
byte[] eh = e._WeakEncryptionHeader;
|
||||
ZipCrypto z = new ZipCrypto();
|
||||
|
||||
if (password == null)
|
||||
throw new BadPasswordException("This entry requires a password.");
|
||||
|
||||
z.InitCipher(password);
|
||||
|
||||
ZipEntry.ReadWeakEncryptionHeader(s, eh);
|
||||
|
||||
// Decrypt the header. This has a side effect of "further initializing the
|
||||
// encryption keys" in the traditional zip encryption.
|
||||
byte[] DecryptedHeader = z.DecryptMessage(eh, eh.Length);
|
||||
|
||||
// CRC check
|
||||
// According to the pkzip spec, the final byte in the decrypted header
|
||||
// is the highest-order byte in the CRC. We check it here.
|
||||
if (DecryptedHeader[11] != (byte)((e._Crc32 >> 24) & 0xff))
|
||||
{
|
||||
// In the case that bit 3 of the general purpose bit flag is set to
|
||||
// indicate the presence of an 'Extended File Header' or a 'data
|
||||
// descriptor' (signature 0x08074b50), the last byte of the decrypted
|
||||
// header is sometimes compared with the high-order byte of the
|
||||
// lastmodified time, rather than the high-order byte of the CRC, to
|
||||
// verify the password.
|
||||
//
|
||||
// This is not documented in the PKWare Appnote.txt. It was
|
||||
// discovered this by analysis of the Crypt.c source file in the
|
||||
// InfoZip library http://www.info-zip.org/pub/infozip/
|
||||
//
|
||||
// The reason for this is that the CRC for a file cannot be known
|
||||
// until the entire contents of the file have been streamed. This
|
||||
// means a tool would have to read the file content TWICE in its
|
||||
// entirety in order to perform PKZIP encryption - once to compute
|
||||
// the CRC, and again to actually encrypt.
|
||||
//
|
||||
// This is so important for performance that using the timeblob as
|
||||
// the verification should be the standard practice for DotNetZip
|
||||
// when using PKZIP encryption. This implies that bit 3 must be
|
||||
// set. The downside is that some tools still cannot cope with ZIP
|
||||
// files that use bit 3. Therefore, DotNetZip DOES NOT force bit 3
|
||||
// when PKZIP encryption is in use, and instead, reads the stream
|
||||
// twice.
|
||||
//
|
||||
|
||||
if ((e._BitField & 0x0008) != 0x0008)
|
||||
{
|
||||
throw new BadPasswordException("The password did not match.");
|
||||
}
|
||||
else if (DecryptedHeader[11] != (byte)((e._TimeBlob >> 8) & 0xff))
|
||||
{
|
||||
throw new BadPasswordException("The password did not match.");
|
||||
}
|
||||
|
||||
// We have a good password.
|
||||
}
|
||||
else
|
||||
{
|
||||
// A-OK
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// From AppNote.txt:
|
||||
/// unsigned char decrypt_byte()
|
||||
/// local unsigned short temp
|
||||
/// temp :=- Key(2) | 2
|
||||
/// decrypt_byte := (temp * (temp ^ 1)) bitshift-right 8
|
||||
/// end decrypt_byte
|
||||
/// </summary>
|
||||
private byte MagicByte
|
||||
{
|
||||
get
|
||||
{
|
||||
UInt16 t = (UInt16)((UInt16)(_Keys[2] & 0xFFFF) | 2);
|
||||
return (byte)((t * (t ^ 1)) >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
// Decrypting:
|
||||
// From AppNote.txt:
|
||||
// loop for i from 0 to 11
|
||||
// C := buffer(i) ^ decrypt_byte()
|
||||
// update_keys(C)
|
||||
// buffer(i) := C
|
||||
// end loop
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Call this method on a cipher text to render the plaintext. You must
|
||||
/// first initialize the cipher with a call to InitCipher.
|
||||
/// </summary>
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var cipher = new ZipCrypto();
|
||||
/// cipher.InitCipher(Password);
|
||||
/// // Decrypt the header. This has a side effect of "further initializing the
|
||||
/// // encryption keys" in the traditional zip encryption.
|
||||
/// byte[] DecryptedMessage = cipher.DecryptMessage(EncryptedMessage);
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// <param name="cipherText">The encrypted buffer.</param>
|
||||
/// <param name="length">
|
||||
/// The number of bytes to encrypt.
|
||||
/// Should be less than or equal to CipherText.Length.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The plaintext.</returns>
|
||||
public byte[] DecryptMessage(byte[] cipherText, int length)
|
||||
{
|
||||
if (cipherText == null)
|
||||
throw new ArgumentNullException("cipherText");
|
||||
|
||||
if (length > cipherText.Length)
|
||||
throw new ArgumentOutOfRangeException("length",
|
||||
"Bad length during Decryption: the length parameter must be smaller than or equal to the size of the destination array.");
|
||||
|
||||
byte[] plainText = new byte[length];
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
byte C = (byte)(cipherText[i] ^ MagicByte);
|
||||
UpdateKeys(C);
|
||||
plainText[i] = C;
|
||||
}
|
||||
return plainText;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is the converse of DecryptMessage. It encrypts the plaintext
|
||||
/// and produces a ciphertext.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="plainText">The plain text buffer.</param>
|
||||
///
|
||||
/// <param name="length">
|
||||
/// The number of bytes to encrypt.
|
||||
/// Should be less than or equal to plainText.Length.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The ciphertext.</returns>
|
||||
public byte[] EncryptMessage(byte[] plainText, int length)
|
||||
{
|
||||
if (plainText == null)
|
||||
throw new ArgumentNullException("plaintext");
|
||||
|
||||
if (length > plainText.Length)
|
||||
throw new ArgumentOutOfRangeException("length",
|
||||
"Bad length during Encryption: The length parameter must be smaller than or equal to the size of the destination array.");
|
||||
|
||||
byte[] cipherText = new byte[length];
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
byte C = plainText[i];
|
||||
cipherText[i] = (byte)(plainText[i] ^ MagicByte);
|
||||
UpdateKeys(C);
|
||||
}
|
||||
return cipherText;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This initializes the cipher with the given password.
|
||||
/// See AppNote.txt for details.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="passphrase">
|
||||
/// The passphrase for encrypting or decrypting with this cipher.
|
||||
/// </param>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <code>
|
||||
/// Step 1 - Initializing the encryption keys
|
||||
/// -----------------------------------------
|
||||
/// Start with these keys:
|
||||
/// Key(0) := 305419896 (0x12345678)
|
||||
/// Key(1) := 591751049 (0x23456789)
|
||||
/// Key(2) := 878082192 (0x34567890)
|
||||
///
|
||||
/// Then, initialize the keys with a password:
|
||||
///
|
||||
/// loop for i from 0 to length(password)-1
|
||||
/// update_keys(password(i))
|
||||
/// end loop
|
||||
///
|
||||
/// Where update_keys() is defined as:
|
||||
///
|
||||
/// update_keys(char):
|
||||
/// Key(0) := crc32(key(0),char)
|
||||
/// Key(1) := Key(1) + (Key(0) bitwiseAND 000000ffH)
|
||||
/// Key(1) := Key(1) * 134775813 + 1
|
||||
/// Key(2) := crc32(key(2),key(1) rightshift 24)
|
||||
/// end update_keys
|
||||
///
|
||||
/// Where crc32(old_crc,char) is a routine that given a CRC value and a
|
||||
/// character, returns an updated CRC value after applying the CRC-32
|
||||
/// algorithm described elsewhere in this document.
|
||||
///
|
||||
/// </code>
|
||||
///
|
||||
/// <para>
|
||||
/// After the keys are initialized, then you can use the cipher to
|
||||
/// encrypt the plaintext.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Essentially we encrypt the password with the keys, then discard the
|
||||
/// ciphertext for the password. This initializes the keys for later use.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
public void InitCipher(string passphrase)
|
||||
{
|
||||
byte[] p = SharedUtilities.StringToByteArray(passphrase);
|
||||
for (int i = 0; i < passphrase.Length; i++)
|
||||
UpdateKeys(p[i]);
|
||||
}
|
||||
|
||||
|
||||
private void UpdateKeys(byte byteValue)
|
||||
{
|
||||
_Keys[0] = (UInt32)crc32.ComputeCrc32((int)_Keys[0], byteValue);
|
||||
_Keys[1] = _Keys[1] + (byte)_Keys[0];
|
||||
_Keys[1] = _Keys[1] * 0x08088405 + 1;
|
||||
_Keys[2] = (UInt32)crc32.ComputeCrc32((int)_Keys[2], (byte)(_Keys[1] >> 24));
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// The byte array representing the seed keys used.
|
||||
///// Get this after calling InitCipher. The 12 bytes represents
|
||||
///// what the zip spec calls the "EncryptionHeader".
|
||||
///// </summary>
|
||||
//public byte[] KeyHeader
|
||||
//{
|
||||
// get
|
||||
// {
|
||||
// byte[] result = new byte[12];
|
||||
// result[0] = (byte)(_Keys[0] & 0xff);
|
||||
// result[1] = (byte)((_Keys[0] >> 8) & 0xff);
|
||||
// result[2] = (byte)((_Keys[0] >> 16) & 0xff);
|
||||
// result[3] = (byte)((_Keys[0] >> 24) & 0xff);
|
||||
// result[4] = (byte)(_Keys[1] & 0xff);
|
||||
// result[5] = (byte)((_Keys[1] >> 8) & 0xff);
|
||||
// result[6] = (byte)((_Keys[1] >> 16) & 0xff);
|
||||
// result[7] = (byte)((_Keys[1] >> 24) & 0xff);
|
||||
// result[8] = (byte)(_Keys[2] & 0xff);
|
||||
// result[9] = (byte)((_Keys[2] >> 8) & 0xff);
|
||||
// result[10] = (byte)((_Keys[2] >> 16) & 0xff);
|
||||
// result[11] = (byte)((_Keys[2] >> 24) & 0xff);
|
||||
// return result;
|
||||
// }
|
||||
//}
|
||||
|
||||
// private fields for the crypto stuff:
|
||||
private UInt32[] _Keys = { 0x12345678, 0x23456789, 0x34567890 };
|
||||
private Ionic.Crc.CRC32 crc32 = new Ionic.Crc.CRC32();
|
||||
|
||||
}
|
||||
|
||||
internal enum CryptoMode
|
||||
{
|
||||
Encrypt,
|
||||
Decrypt
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A Stream for reading and concurrently decrypting data from a zip file,
|
||||
/// or for writing and concurrently encrypting data to a zip file.
|
||||
/// </summary>
|
||||
internal class ZipCipherStream : System.IO.Stream
|
||||
{
|
||||
private ZipCrypto _cipher;
|
||||
private System.IO.Stream _s;
|
||||
private CryptoMode _mode;
|
||||
|
||||
/// <summary> The constructor. </summary>
|
||||
/// <param name="s">The underlying stream</param>
|
||||
/// <param name="mode">To either encrypt or decrypt.</param>
|
||||
/// <param name="cipher">The pre-initialized ZipCrypto object.</param>
|
||||
public ZipCipherStream(System.IO.Stream s, ZipCrypto cipher, CryptoMode mode)
|
||||
: base()
|
||||
{
|
||||
_cipher = cipher;
|
||||
_s = s;
|
||||
_mode = mode;
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_mode == CryptoMode.Encrypt)
|
||||
throw new NotSupportedException("This stream does not encrypt via Read()");
|
||||
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException("buffer");
|
||||
|
||||
byte[] db = new byte[count];
|
||||
int n = _s.Read(db, 0, count);
|
||||
byte[] decrypted = _cipher.DecryptMessage(db, n);
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
buffer[offset + i] = decrypted[i];
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_mode == CryptoMode.Decrypt)
|
||||
throw new NotSupportedException("This stream does not Decrypt via Write()");
|
||||
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException("buffer");
|
||||
|
||||
// workitem 7696
|
||||
if (count == 0) return;
|
||||
|
||||
byte[] plaintext = null;
|
||||
if (offset != 0)
|
||||
{
|
||||
plaintext = new byte[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
plaintext[i] = buffer[offset + i];
|
||||
}
|
||||
}
|
||||
else plaintext = buffer;
|
||||
|
||||
byte[] encrypted = _cipher.EncryptMessage(plaintext, count);
|
||||
_s.Write(encrypted, 0, encrypted.Length);
|
||||
}
|
||||
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return (_mode == CryptoMode.Decrypt); }
|
||||
}
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return (_mode == CryptoMode.Encrypt); }
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
//throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,381 @@
|
||||
// ZipDirEntry.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2006-2011 Dino Chiesa .
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-11 12:03:03>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines members of the ZipEntry class for reading the
|
||||
// Zip file central directory.
|
||||
//
|
||||
// Created: Tue, 27 Mar 2007 15:30
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
|
||||
partial class ZipEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// True if the referenced entry is a directory.
|
||||
/// </summary>
|
||||
internal bool AttributesIndicateDirectory
|
||||
{
|
||||
get { return ((_InternalFileAttrs == 0) && ((_ExternalFileAttrs & 0x0010) == 0x0010)); }
|
||||
}
|
||||
|
||||
|
||||
internal void ResetDirEntry()
|
||||
{
|
||||
// __FileDataPosition is the position of the file data for an entry.
|
||||
// It is _RelativeOffsetOfLocalHeader + size of local header.
|
||||
|
||||
// We cannot know the __FileDataPosition until we read the local
|
||||
// header.
|
||||
|
||||
// The local header is not necessarily the same length as the record
|
||||
// in the central directory.
|
||||
|
||||
// Set to -1, to indicate we need to read this later.
|
||||
this.__FileDataPosition = -1;
|
||||
|
||||
// set _LengthOfHeader to 0, to indicate we need to read later.
|
||||
this._LengthOfHeader = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides a human-readable string with information about the ZipEntry.
|
||||
/// </summary>
|
||||
public string Info
|
||||
{
|
||||
get
|
||||
{
|
||||
var builder = new System.Text.StringBuilder();
|
||||
builder
|
||||
.Append(string.Format(" ZipEntry: {0}\n", this.FileName))
|
||||
.Append(string.Format(" Version Made By: {0}\n", this._VersionMadeBy))
|
||||
.Append(string.Format(" Needed to extract: {0}\n", this.VersionNeeded));
|
||||
|
||||
if (this._IsDirectory)
|
||||
builder.Append(" Entry type: directory\n");
|
||||
else
|
||||
{
|
||||
builder.Append(string.Format(" File type: {0}\n", this._IsText? "text":"binary"))
|
||||
.Append(string.Format(" Compression: {0}\n", this.CompressionMethod))
|
||||
.Append(string.Format(" Compressed: 0x{0:X}\n", this.CompressedSize))
|
||||
.Append(string.Format(" Uncompressed: 0x{0:X}\n", this.UncompressedSize))
|
||||
.Append(string.Format(" CRC32: 0x{0:X8}\n", this._Crc32));
|
||||
}
|
||||
builder.Append(string.Format(" Disk Number: {0}\n", this._diskNumber));
|
||||
if (this._RelativeOffsetOfLocalHeader > 0xFFFFFFFF)
|
||||
builder
|
||||
.Append(string.Format(" Relative Offset: 0x{0:X16}\n", this._RelativeOffsetOfLocalHeader));
|
||||
else
|
||||
builder
|
||||
.Append(string.Format(" Relative Offset: 0x{0:X8}\n", this._RelativeOffsetOfLocalHeader));
|
||||
|
||||
builder
|
||||
.Append(string.Format(" Bit Field: 0x{0:X4}\n", this._BitField))
|
||||
.Append(string.Format(" Encrypted?: {0}\n", this._sourceIsEncrypted))
|
||||
.Append(string.Format(" Timeblob: 0x{0:X8}\n", this._TimeBlob))
|
||||
.Append(string.Format(" Time: {0}\n", Ionic.Zip.SharedUtilities.PackedToDateTime(this._TimeBlob)));
|
||||
|
||||
builder.Append(string.Format(" Is Zip64?: {0}\n", this._InputUsesZip64));
|
||||
if (!string.IsNullOrEmpty(this._Comment))
|
||||
{
|
||||
builder.Append(string.Format(" Comment: {0}\n", this._Comment));
|
||||
}
|
||||
builder.Append("\n");
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// workitem 10330
|
||||
private class CopyHelper
|
||||
{
|
||||
private static System.Text.RegularExpressions.Regex re =
|
||||
new System.Text.RegularExpressions.Regex(" \\(copy (\\d+)\\)$");
|
||||
|
||||
private static int callCount = 0;
|
||||
|
||||
internal static string AppendCopyToFileName(string f)
|
||||
{
|
||||
callCount++;
|
||||
if (callCount > 25)
|
||||
throw new OverflowException("overflow while creating filename");
|
||||
|
||||
int n = 1;
|
||||
int r = f.LastIndexOf(".");
|
||||
|
||||
if (r == -1)
|
||||
{
|
||||
// there is no extension
|
||||
System.Text.RegularExpressions.Match m = re.Match(f);
|
||||
if (m.Success)
|
||||
{
|
||||
n = Int32.Parse(m.Groups[1].Value) + 1;
|
||||
string copy = String.Format(" (copy {0})", n);
|
||||
f = f.Substring(0, m.Index) + copy;
|
||||
}
|
||||
else
|
||||
{
|
||||
string copy = String.Format(" (copy {0})", n);
|
||||
f = f + copy;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//System.Console.WriteLine("HasExtension");
|
||||
System.Text.RegularExpressions.Match m = re.Match(f.Substring(0, r));
|
||||
if (m.Success)
|
||||
{
|
||||
n = Int32.Parse(m.Groups[1].Value) + 1;
|
||||
string copy = String.Format(" (copy {0})", n);
|
||||
f = f.Substring(0, m.Index) + copy + f.Substring(r);
|
||||
}
|
||||
else
|
||||
{
|
||||
string copy = String.Format(" (copy {0})", n);
|
||||
f = f.Substring(0, r) + copy + f.Substring(r);
|
||||
}
|
||||
|
||||
//System.Console.WriteLine("returning f({0})", f);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads one entry from the zip directory structure in the zip file.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="zf">
|
||||
/// The zipfile for which a directory entry will be read. From this param, the
|
||||
/// method gets the ReadStream and the expected text encoding
|
||||
/// (ProvisionalAlternateEncoding) which is used if the entry is not marked
|
||||
/// UTF-8.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="previouslySeen">
|
||||
/// a list of previously seen entry names; used to prevent duplicates.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>the entry read from the archive.</returns>
|
||||
internal static ZipEntry ReadDirEntry(ZipFile zf,
|
||||
Dictionary<String,Object> previouslySeen)
|
||||
{
|
||||
System.IO.Stream s = zf.ReadStream;
|
||||
System.Text.Encoding expectedEncoding = (zf.AlternateEncodingUsage == ZipOption.Always)
|
||||
? zf.AlternateEncoding
|
||||
: ZipFile.DefaultEncoding;
|
||||
|
||||
int signature = Ionic.Zip.SharedUtilities.ReadSignature(s);
|
||||
// return null if this is not a local file header signature
|
||||
if (IsNotValidZipDirEntrySig(signature))
|
||||
{
|
||||
s.Seek(-4, System.IO.SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
|
||||
|
||||
// Getting "not a ZipDirEntry signature" here is not always wrong or an
|
||||
// error. This can happen when walking through a zipfile. After the
|
||||
// last ZipDirEntry, we expect to read an
|
||||
// EndOfCentralDirectorySignature. When we get this is how we know
|
||||
// we've reached the end of the central directory.
|
||||
if (signature != ZipConstants.EndOfCentralDirectorySignature &&
|
||||
signature != ZipConstants.Zip64EndOfCentralDirectoryRecordSignature &&
|
||||
signature != ZipConstants.ZipEntrySignature // workitem 8299
|
||||
)
|
||||
{
|
||||
throw new BadReadException(String.Format(" Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, s.Position));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
int bytesRead = 42 + 4;
|
||||
byte[] block = new byte[42];
|
||||
int n = s.Read(block, 0, block.Length);
|
||||
if (n != block.Length) return null;
|
||||
|
||||
int i = 0;
|
||||
ZipEntry zde = new ZipEntry();
|
||||
zde.AlternateEncoding = expectedEncoding;
|
||||
zde._Source = ZipEntrySource.ZipFile;
|
||||
zde._container = new ZipContainer(zf);
|
||||
|
||||
unchecked
|
||||
{
|
||||
zde._VersionMadeBy = (short)(block[i++] + block[i++] * 256);
|
||||
zde._VersionNeeded = (short)(block[i++] + block[i++] * 256);
|
||||
zde._BitField = (short)(block[i++] + block[i++] * 256);
|
||||
zde._CompressionMethod = (Int16)(block[i++] + block[i++] * 256);
|
||||
zde._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
|
||||
zde._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(zde._TimeBlob);
|
||||
zde._timestamp |= ZipEntryTimestamp.DOS;
|
||||
|
||||
zde._Crc32 = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
|
||||
zde._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
||||
zde._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
||||
}
|
||||
|
||||
// preserve
|
||||
zde._CompressionMethod_FromZipFile = zde._CompressionMethod;
|
||||
|
||||
zde._filenameLength = (short)(block[i++] + block[i++] * 256);
|
||||
zde._extraFieldLength = (short)(block[i++] + block[i++] * 256);
|
||||
zde._commentLength = (short)(block[i++] + block[i++] * 256);
|
||||
zde._diskNumber = (UInt32)(block[i++] + block[i++] * 256);
|
||||
|
||||
zde._InternalFileAttrs = (short)(block[i++] + block[i++] * 256);
|
||||
zde._ExternalFileAttrs = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
|
||||
|
||||
zde._RelativeOffsetOfLocalHeader = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
||||
|
||||
// workitem 7801
|
||||
zde.IsText = ((zde._InternalFileAttrs & 0x01) == 0x01);
|
||||
|
||||
block = new byte[zde._filenameLength];
|
||||
n = s.Read(block, 0, block.Length);
|
||||
bytesRead += n;
|
||||
if ((zde._BitField & 0x0800) == 0x0800)
|
||||
{
|
||||
// UTF-8 is in use
|
||||
zde._FileNameInArchive = Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block);
|
||||
}
|
||||
else
|
||||
{
|
||||
zde._FileNameInArchive = Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding);
|
||||
}
|
||||
|
||||
// workitem 10330
|
||||
// insure unique entry names
|
||||
while (previouslySeen.ContainsKey(zde._FileNameInArchive))
|
||||
{
|
||||
zde._FileNameInArchive = CopyHelper.AppendCopyToFileName(zde._FileNameInArchive);
|
||||
zde._metadataChanged = true;
|
||||
}
|
||||
|
||||
if (zde.AttributesIndicateDirectory)
|
||||
zde.MarkAsDirectory(); // may append a slash to filename if nec.
|
||||
// workitem 6898
|
||||
else if (zde._FileNameInArchive.EndsWith("/")) zde.MarkAsDirectory();
|
||||
|
||||
zde._CompressedFileDataSize = zde._CompressedSize;
|
||||
if ((zde._BitField & 0x01) == 0x01)
|
||||
{
|
||||
// this may change after processing the Extra field
|
||||
zde._Encryption_FromZipFile = zde._Encryption =
|
||||
EncryptionAlgorithm.PkzipWeak;
|
||||
zde._sourceIsEncrypted = true;
|
||||
}
|
||||
|
||||
if (zde._extraFieldLength > 0)
|
||||
{
|
||||
zde._InputUsesZip64 = (zde._CompressedSize == 0xFFFFFFFF ||
|
||||
zde._UncompressedSize == 0xFFFFFFFF ||
|
||||
zde._RelativeOffsetOfLocalHeader == 0xFFFFFFFF);
|
||||
|
||||
// Console.WriteLine(" Input uses Z64?: {0}", zde._InputUsesZip64);
|
||||
|
||||
bytesRead += zde.ProcessExtraField(s, zde._extraFieldLength);
|
||||
zde._CompressedFileDataSize = zde._CompressedSize;
|
||||
}
|
||||
|
||||
// we've processed the extra field, so we know the encryption method is set now.
|
||||
if (zde._Encryption == EncryptionAlgorithm.PkzipWeak)
|
||||
{
|
||||
// the "encryption header" of 12 bytes precedes the file data
|
||||
zde._CompressedFileDataSize -= 12;
|
||||
}
|
||||
#if AESCRYPTO
|
||||
else if (zde.Encryption == EncryptionAlgorithm.WinZipAes128 ||
|
||||
zde.Encryption == EncryptionAlgorithm.WinZipAes256)
|
||||
{
|
||||
zde._CompressedFileDataSize = zde.CompressedSize -
|
||||
(ZipEntry.GetLengthOfCryptoHeaderBytes(zde.Encryption) + 10);
|
||||
zde._LengthOfTrailer = 10;
|
||||
}
|
||||
#endif
|
||||
|
||||
// tally the trailing descriptor
|
||||
if ((zde._BitField & 0x0008) == 0x0008)
|
||||
{
|
||||
// sig, CRC, Comp and Uncomp sizes
|
||||
if (zde._InputUsesZip64)
|
||||
zde._LengthOfTrailer += 24;
|
||||
else
|
||||
zde._LengthOfTrailer += 16;
|
||||
}
|
||||
|
||||
// workitem 12744
|
||||
zde.AlternateEncoding = ((zde._BitField & 0x0800) == 0x0800)
|
||||
? System.Text.Encoding.UTF8
|
||||
:expectedEncoding;
|
||||
|
||||
zde.AlternateEncodingUsage = ZipOption.Always;
|
||||
|
||||
if (zde._commentLength > 0)
|
||||
{
|
||||
block = new byte[zde._commentLength];
|
||||
n = s.Read(block, 0, block.Length);
|
||||
bytesRead += n;
|
||||
if ((zde._BitField & 0x0800) == 0x0800)
|
||||
{
|
||||
// UTF-8 is in use
|
||||
zde._Comment = Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block);
|
||||
}
|
||||
else
|
||||
{
|
||||
zde._Comment = Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding);
|
||||
}
|
||||
}
|
||||
//zde._LengthOfDirEntry = bytesRead;
|
||||
return zde;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the passed-in value is a valid signature for a ZipDirEntry.
|
||||
/// </summary>
|
||||
/// <param name="signature">the candidate 4-byte signature value.</param>
|
||||
/// <returns>true, if the signature is valid according to the PKWare spec.</returns>
|
||||
internal static bool IsNotValidZipDirEntrySig(int signature)
|
||||
{
|
||||
return (signature != ZipConstants.ZipDirEntrySignature);
|
||||
}
|
||||
|
||||
|
||||
private Int16 _VersionMadeBy;
|
||||
private Int16 _InternalFileAttrs;
|
||||
private Int32 _ExternalFileAttrs;
|
||||
|
||||
//private Int32 _LengthOfDirEntry;
|
||||
private Int16 _filenameLength;
|
||||
private Int16 _extraFieldLength;
|
||||
private Int16 _commentLength;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,798 @@
|
||||
// ZipEntry.Read.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2011 Dino Chiesa
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-09 21:31:28>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines logic for Reading the ZipEntry from a
|
||||
// zip file.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
public partial class ZipEntry
|
||||
{
|
||||
private int _readExtraDepth;
|
||||
private void ReadExtraField()
|
||||
{
|
||||
_readExtraDepth++;
|
||||
// workitem 8098: ok (restore)
|
||||
long posn = this.ArchiveStream.Position;
|
||||
this.ArchiveStream.Seek(this._RelativeOffsetOfLocalHeader, SeekOrigin.Begin);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream);
|
||||
|
||||
byte[] block = new byte[30];
|
||||
this.ArchiveStream.Read(block, 0, block.Length);
|
||||
int i = 26;
|
||||
Int16 filenameLength = (short)(block[i++] + block[i++] * 256);
|
||||
Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256);
|
||||
|
||||
// workitem 8098: ok (relative)
|
||||
this.ArchiveStream.Seek(filenameLength, SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream);
|
||||
|
||||
ProcessExtraField(this.ArchiveStream, extraFieldLength);
|
||||
|
||||
// workitem 8098: ok (restore)
|
||||
this.ArchiveStream.Seek(posn, SeekOrigin.Begin);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream);
|
||||
_readExtraDepth--;
|
||||
}
|
||||
|
||||
|
||||
private static bool ReadHeader(ZipEntry ze, System.Text.Encoding defaultEncoding)
|
||||
{
|
||||
int bytesRead = 0;
|
||||
|
||||
// change for workitem 8098
|
||||
ze._RelativeOffsetOfLocalHeader = ze.ArchiveStream.Position;
|
||||
|
||||
int signature = Ionic.Zip.SharedUtilities.ReadEntrySignature(ze.ArchiveStream);
|
||||
bytesRead += 4;
|
||||
|
||||
// Return false if this is not a local file header signature.
|
||||
if (ZipEntry.IsNotValidSig(signature))
|
||||
{
|
||||
// Getting "not a ZipEntry signature" is not always wrong or an error.
|
||||
// This will happen after the last entry in a zipfile. In that case, we
|
||||
// expect to read :
|
||||
// a ZipDirEntry signature (if a non-empty zip file) or
|
||||
// a ZipConstants.EndOfCentralDirectorySignature.
|
||||
//
|
||||
// Anything else is a surprise.
|
||||
|
||||
ze.ArchiveStream.Seek(-4, SeekOrigin.Current); // unread the signature
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream);
|
||||
if (ZipEntry.IsNotValidZipDirEntrySig(signature) && (signature != ZipConstants.EndOfCentralDirectorySignature))
|
||||
{
|
||||
throw new BadReadException(String.Format(" Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, ze.ArchiveStream.Position));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] block = new byte[26];
|
||||
int n = ze.ArchiveStream.Read(block, 0, block.Length);
|
||||
if (n != block.Length) return false;
|
||||
bytesRead += n;
|
||||
|
||||
int i = 0;
|
||||
ze._VersionNeeded = (Int16)(block[i++] + block[i++] * 256);
|
||||
ze._BitField = (Int16)(block[i++] + block[i++] * 256);
|
||||
ze._CompressionMethod_FromZipFile = ze._CompressionMethod = (Int16)(block[i++] + block[i++] * 256);
|
||||
ze._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
|
||||
// transform the time data into something usable (a DateTime)
|
||||
ze._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(ze._TimeBlob);
|
||||
ze._timestamp |= ZipEntryTimestamp.DOS;
|
||||
|
||||
if ((ze._BitField & 0x01) == 0x01)
|
||||
{
|
||||
ze._Encryption_FromZipFile = ze._Encryption = EncryptionAlgorithm.PkzipWeak; // this *may* change after processing the Extra field
|
||||
ze._sourceIsEncrypted = true;
|
||||
}
|
||||
|
||||
// NB: if ((ze._BitField & 0x0008) != 0x0008), then the Compressed, uncompressed and
|
||||
// CRC values are not true values; the true values will follow the entry data.
|
||||
// But, regardless of the status of bit 3 in the bitfield, the slots for
|
||||
// the three amigos may contain marker values for ZIP64. So we must read them.
|
||||
{
|
||||
ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
||||
ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
||||
ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
||||
|
||||
if ((uint)ze._CompressedSize == 0xFFFFFFFF ||
|
||||
(uint)ze._UncompressedSize == 0xFFFFFFFF)
|
||||
|
||||
ze._InputUsesZip64 = true;
|
||||
}
|
||||
|
||||
Int16 filenameLength = (short)(block[i++] + block[i++] * 256);
|
||||
Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256);
|
||||
|
||||
block = new byte[filenameLength];
|
||||
n = ze.ArchiveStream.Read(block, 0, block.Length);
|
||||
bytesRead += n;
|
||||
|
||||
// if the UTF8 bit is set for this entry, override the
|
||||
// encoding the application requested.
|
||||
|
||||
if ((ze._BitField & 0x0800) == 0x0800)
|
||||
{
|
||||
// workitem 12744
|
||||
ze.AlternateEncoding = System.Text.Encoding.UTF8;
|
||||
ze.AlternateEncodingUsage = ZipOption.Always;
|
||||
}
|
||||
|
||||
// need to use this form of GetString() for .NET CF
|
||||
ze._FileNameInArchive = ze.AlternateEncoding.GetString(block, 0, block.Length);
|
||||
|
||||
// workitem 6898
|
||||
if (ze._FileNameInArchive.EndsWith("/")) ze.MarkAsDirectory();
|
||||
|
||||
bytesRead += ze.ProcessExtraField(ze.ArchiveStream, extraFieldLength);
|
||||
|
||||
ze._LengthOfTrailer = 0;
|
||||
|
||||
// workitem 6607 - don't read for directories
|
||||
// actually get the compressed size and CRC if necessary
|
||||
if (!ze._FileNameInArchive.EndsWith("/") && (ze._BitField & 0x0008) == 0x0008)
|
||||
{
|
||||
// This descriptor exists only if bit 3 of the general
|
||||
// purpose bit flag is set (see below). It is byte aligned
|
||||
// and immediately follows the last byte of compressed data,
|
||||
// as well as any encryption trailer, as with AES.
|
||||
// This descriptor is used only when it was not possible to
|
||||
// seek in the output .ZIP file, e.g., when the output .ZIP file
|
||||
// was standard output or a non-seekable device. For ZIP64(tm) format
|
||||
// archives, the compressed and uncompressed sizes are 8 bytes each.
|
||||
|
||||
// workitem 8098: ok (restore)
|
||||
long posn = ze.ArchiveStream.Position;
|
||||
|
||||
// Here, we're going to loop until we find a ZipEntryDataDescriptorSignature and
|
||||
// a consistent data record after that. To be consistent, the data record must
|
||||
// indicate the length of the entry data.
|
||||
bool wantMore = true;
|
||||
long SizeOfDataRead = 0;
|
||||
int tries = 0;
|
||||
while (wantMore)
|
||||
{
|
||||
tries++;
|
||||
// We call the FindSignature shared routine to find the specified signature
|
||||
// in the already-opened zip archive, starting from the current cursor
|
||||
// position in that filestream. If we cannot find the signature, then the
|
||||
// routine returns -1, and the ReadHeader() method returns false,
|
||||
// indicating we cannot read a legal entry header. If we have found it,
|
||||
// then the FindSignature() method returns the number of bytes in the
|
||||
// stream we had to seek forward, to find the sig. We need this to
|
||||
// determine if the zip entry is valid, later.
|
||||
|
||||
if (ze._container.ZipFile != null)
|
||||
ze._container.ZipFile.OnReadBytes(ze);
|
||||
|
||||
long d = Ionic.Zip.SharedUtilities.FindSignature(ze.ArchiveStream, ZipConstants.ZipEntryDataDescriptorSignature);
|
||||
if (d == -1) return false;
|
||||
|
||||
// total size of data read (through all loops of this).
|
||||
SizeOfDataRead += d;
|
||||
|
||||
if (ze._InputUsesZip64)
|
||||
{
|
||||
// read 1x 4-byte (CRC) and 2x 8-bytes (Compressed Size, Uncompressed Size)
|
||||
block = new byte[20];
|
||||
n = ze.ArchiveStream.Read(block, 0, block.Length);
|
||||
if (n != 20) return false;
|
||||
|
||||
// do not increment bytesRead - it is for entry header only.
|
||||
// the data we have just read is a footer (falls after the file data)
|
||||
//bytesRead += n;
|
||||
|
||||
i = 0;
|
||||
ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
||||
ze._CompressedSize = BitConverter.ToInt64(block, i);
|
||||
i += 8;
|
||||
ze._UncompressedSize = BitConverter.ToInt64(block, i);
|
||||
i += 8;
|
||||
|
||||
ze._LengthOfTrailer += 24; // bytes including sig, CRC, Comp and Uncomp sizes
|
||||
}
|
||||
else
|
||||
{
|
||||
// read 3x 4-byte fields (CRC, Compressed Size, Uncompressed Size)
|
||||
block = new byte[12];
|
||||
n = ze.ArchiveStream.Read(block, 0, block.Length);
|
||||
if (n != 12) return false;
|
||||
|
||||
// do not increment bytesRead - it is for entry header only.
|
||||
// the data we have just read is a footer (falls after the file data)
|
||||
//bytesRead += n;
|
||||
|
||||
i = 0;
|
||||
ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
||||
ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
||||
ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
||||
|
||||
ze._LengthOfTrailer += 16; // bytes including sig, CRC, Comp and Uncomp sizes
|
||||
|
||||
}
|
||||
|
||||
wantMore = (SizeOfDataRead != ze._CompressedSize);
|
||||
|
||||
if (wantMore)
|
||||
{
|
||||
// Seek back to un-read the last 12 bytes - maybe THEY contain
|
||||
// the ZipEntryDataDescriptorSignature.
|
||||
// (12 bytes for the CRC, Comp and Uncomp size.)
|
||||
ze.ArchiveStream.Seek(-12, SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream);
|
||||
|
||||
// Adjust the size to account for the false signature read in
|
||||
// FindSignature().
|
||||
SizeOfDataRead += 4;
|
||||
}
|
||||
}
|
||||
|
||||
// seek back to previous position, to prepare to read file data
|
||||
// workitem 8098: ok (restore)
|
||||
ze.ArchiveStream.Seek(posn, SeekOrigin.Begin);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream);
|
||||
}
|
||||
|
||||
ze._CompressedFileDataSize = ze._CompressedSize;
|
||||
|
||||
|
||||
// bit 0 set indicates that some kind of encryption is in use
|
||||
if ((ze._BitField & 0x01) == 0x01)
|
||||
{
|
||||
#if AESCRYPTO
|
||||
if (ze.Encryption == EncryptionAlgorithm.WinZipAes128 ||
|
||||
ze.Encryption == EncryptionAlgorithm.WinZipAes256)
|
||||
{
|
||||
int bits = ZipEntry.GetKeyStrengthInBits(ze._Encryption_FromZipFile);
|
||||
// read in the WinZip AES metadata: salt + PV. 18 bytes for AES256. 10 bytes for AES128.
|
||||
ze._aesCrypto_forExtract = WinZipAesCrypto.ReadFromStream(null, bits, ze.ArchiveStream);
|
||||
bytesRead += ze._aesCrypto_forExtract.SizeOfEncryptionMetadata - 10; // MAC (follows crypto bytes)
|
||||
// according to WinZip, the CompressedSize includes the AES Crypto framing data.
|
||||
ze._CompressedFileDataSize -= ze._aesCrypto_forExtract.SizeOfEncryptionMetadata;
|
||||
ze._LengthOfTrailer += 10; // MAC
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// read in the header data for "weak" encryption
|
||||
ze._WeakEncryptionHeader = new byte[12];
|
||||
bytesRead += ZipEntry.ReadWeakEncryptionHeader(ze._archiveStream, ze._WeakEncryptionHeader);
|
||||
// decrease the filedata size by 12 bytes
|
||||
ze._CompressedFileDataSize -= 12;
|
||||
}
|
||||
}
|
||||
|
||||
// Remember the size of the blob for this entry.
|
||||
// We also have the starting position in the stream for this entry.
|
||||
ze._LengthOfHeader = bytesRead;
|
||||
ze._TotalEntrySize = ze._LengthOfHeader + ze._CompressedFileDataSize + ze._LengthOfTrailer;
|
||||
|
||||
|
||||
// We've read in the regular entry header, the extra field, and any
|
||||
// encryption header. The pointer in the file is now at the start of the
|
||||
// filedata, which is potentially compressed and encrypted. Just ahead in
|
||||
// the file, there are _CompressedFileDataSize bytes of data, followed by
|
||||
// potentially a non-zero length trailer, consisting of optionally, some
|
||||
// encryption stuff (10 byte MAC for AES), and the bit-3 trailer (16 or 24
|
||||
// bytes).
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal static int ReadWeakEncryptionHeader(Stream s, byte[] buffer)
|
||||
{
|
||||
// PKZIP encrypts the compressed data stream. Encrypted files must
|
||||
// be decrypted before they can be extracted.
|
||||
|
||||
// Each PKZIP-encrypted file has an extra 12 bytes stored at the start of the data
|
||||
// area defining the encryption header for that file. The encryption header is
|
||||
// originally set to random values, and then itself encrypted, using three, 32-bit
|
||||
// keys. The key values are initialized using the supplied encryption password.
|
||||
// After each byte is encrypted, the keys are then updated using pseudo-random
|
||||
// number generation techniques in combination with the same CRC-32 algorithm used
|
||||
// in PKZIP and implemented in the CRC32.cs module in this project.
|
||||
|
||||
// read the 12-byte encryption header
|
||||
int additionalBytesRead = s.Read(buffer, 0, 12);
|
||||
if (additionalBytesRead != 12)
|
||||
throw new ZipException(String.Format("Unexpected end of data at position 0x{0:X8}", s.Position));
|
||||
|
||||
return additionalBytesRead;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static bool IsNotValidSig(int signature)
|
||||
{
|
||||
return (signature != ZipConstants.ZipEntrySignature);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads one <c>ZipEntry</c> from the given stream. The content for
|
||||
/// the entry does not get decompressed or decrypted. This method
|
||||
/// basically reads metadata, and seeks.
|
||||
/// </summary>
|
||||
/// <param name="zc">the ZipContainer this entry belongs to.</param>
|
||||
/// <param name="first">
|
||||
/// true of this is the first entry being read from the stream.
|
||||
/// </param>
|
||||
/// <returns>the <c>ZipEntry</c> read from the stream.</returns>
|
||||
internal static ZipEntry ReadEntry(ZipContainer zc, bool first)
|
||||
{
|
||||
ZipFile zf = zc.ZipFile;
|
||||
Stream s = zc.ReadStream;
|
||||
System.Text.Encoding defaultEncoding = zc.AlternateEncoding;
|
||||
ZipEntry entry = new ZipEntry();
|
||||
entry._Source = ZipEntrySource.ZipFile;
|
||||
entry._container = zc;
|
||||
entry._archiveStream = s;
|
||||
if (zf != null)
|
||||
zf.OnReadEntry(true, null);
|
||||
|
||||
if (first) HandlePK00Prefix(s);
|
||||
|
||||
// Read entry header, including any encryption header
|
||||
if (!ReadHeader(entry, defaultEncoding)) return null;
|
||||
|
||||
// Store the position in the stream for this entry
|
||||
// change for workitem 8098
|
||||
entry.__FileDataPosition = entry.ArchiveStream.Position;
|
||||
|
||||
// seek past the data without reading it. We will read on Extract()
|
||||
s.Seek(entry._CompressedFileDataSize + entry._LengthOfTrailer, SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
|
||||
|
||||
// ReadHeader moves the file pointer to the end of the entry header,
|
||||
// as well as any encryption header.
|
||||
|
||||
// CompressedFileDataSize includes:
|
||||
// the maybe compressed, maybe encrypted file data
|
||||
// the encryption trailer, if any
|
||||
// the bit 3 descriptor, if any
|
||||
|
||||
// workitem 5306
|
||||
// http://www.codeplex.com/DotNetZip/WorkItem/View.aspx?WorkItemId=5306
|
||||
HandleUnexpectedDataDescriptor(entry);
|
||||
|
||||
if (zf != null)
|
||||
{
|
||||
zf.OnReadBytes(entry);
|
||||
zf.OnReadEntry(false, entry);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
internal static void HandlePK00Prefix(Stream s)
|
||||
{
|
||||
// in some cases, the zip file begins with "PK00". This is a throwback and is rare,
|
||||
// but we handle it anyway. We do not change behavior based on it.
|
||||
uint datum = (uint)Ionic.Zip.SharedUtilities.ReadInt(s);
|
||||
if (datum != ZipConstants.PackedToRemovableMedia)
|
||||
{
|
||||
s.Seek(-4, SeekOrigin.Current); // unread the block
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static void HandleUnexpectedDataDescriptor(ZipEntry entry)
|
||||
{
|
||||
Stream s = entry.ArchiveStream;
|
||||
|
||||
// In some cases, the "data descriptor" is present, without a signature, even when
|
||||
// bit 3 of the BitField is NOT SET. This is the CRC, followed
|
||||
// by the compressed length and the uncompressed length (4 bytes for each
|
||||
// of those three elements). Need to check that here.
|
||||
//
|
||||
uint datum = (uint)Ionic.Zip.SharedUtilities.ReadInt(s);
|
||||
if (datum == entry._Crc32)
|
||||
{
|
||||
int sz = Ionic.Zip.SharedUtilities.ReadInt(s);
|
||||
if (sz == entry._CompressedSize)
|
||||
{
|
||||
sz = Ionic.Zip.SharedUtilities.ReadInt(s);
|
||||
if (sz == entry._UncompressedSize)
|
||||
{
|
||||
// ignore everything and discard it.
|
||||
}
|
||||
else
|
||||
{
|
||||
s.Seek(-12, SeekOrigin.Current); // unread the three blocks
|
||||
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s.Seek(-8, SeekOrigin.Current); // unread the two blocks
|
||||
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s.Seek(-4, SeekOrigin.Current); // unread the block
|
||||
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Finds a particular segment in the given extra field.
|
||||
/// This is used when modifying a previously-generated
|
||||
/// extra field, in particular when removing the AES crypto
|
||||
/// segment in the extra field.
|
||||
/// </summary>
|
||||
static internal int FindExtraFieldSegment(byte[] extra, int offx, UInt16 targetHeaderId)
|
||||
{
|
||||
int j = offx;
|
||||
while (j + 3 < extra.Length)
|
||||
{
|
||||
UInt16 headerId = (UInt16)(extra[j++] + extra[j++] * 256);
|
||||
if (headerId == targetHeaderId) return j-2;
|
||||
|
||||
// else advance to next segment
|
||||
Int16 dataSize = (short)(extra[j++] + extra[j++] * 256);
|
||||
j+= dataSize;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// At current cursor position in the stream, read the extra
|
||||
/// field, and set the properties on the ZipEntry instance
|
||||
/// appropriately. This can be called when processing the
|
||||
/// Extra field in the Central Directory, or in the local
|
||||
/// header.
|
||||
/// </summary>
|
||||
internal int ProcessExtraField(Stream s, Int16 extraFieldLength)
|
||||
{
|
||||
int additionalBytesRead = 0;
|
||||
if (extraFieldLength > 0)
|
||||
{
|
||||
byte[] buffer = this._Extra = new byte[extraFieldLength];
|
||||
additionalBytesRead = s.Read(buffer, 0, buffer.Length);
|
||||
long posn = s.Position - additionalBytesRead;
|
||||
int j = 0;
|
||||
while (j + 3 < buffer.Length)
|
||||
{
|
||||
int start = j;
|
||||
UInt16 headerId = (UInt16)(buffer[j++] + buffer[j++] * 256);
|
||||
Int16 dataSize = (short)(buffer[j++] + buffer[j++] * 256);
|
||||
|
||||
switch (headerId)
|
||||
{
|
||||
case 0x000a: // NTFS ctime, atime, mtime
|
||||
j = ProcessExtraFieldWindowsTimes(buffer, j, dataSize, posn);
|
||||
break;
|
||||
|
||||
case 0x5455: // Unix ctime, atime, mtime
|
||||
j = ProcessExtraFieldUnixTimes(buffer, j, dataSize, posn);
|
||||
break;
|
||||
|
||||
case 0x5855: // Info-zip Extra field (outdated)
|
||||
// This is outdated, so the field is supported on
|
||||
// read only.
|
||||
j = ProcessExtraFieldInfoZipTimes(buffer, j, dataSize, posn);
|
||||
break;
|
||||
|
||||
case 0x7855: // Unix uid/gid
|
||||
// ignored. DotNetZip does not handle this field.
|
||||
break;
|
||||
|
||||
case 0x7875: // ??
|
||||
// ignored. I could not find documentation on this field,
|
||||
// though it appears in some zip files.
|
||||
break;
|
||||
|
||||
case 0x0001: // ZIP64
|
||||
j = ProcessExtraFieldZip64(buffer, j, dataSize, posn);
|
||||
break;
|
||||
|
||||
#if AESCRYPTO
|
||||
case 0x9901: // WinZip AES encryption is in use. (workitem 6834)
|
||||
// we will handle this extra field only if compressionmethod is 0x63
|
||||
j = ProcessExtraFieldWinZipAes(buffer, j, dataSize, posn);
|
||||
break;
|
||||
#endif
|
||||
case 0x0017: // workitem 7968: handle PKWare Strong encryption header
|
||||
j = ProcessExtraFieldPkwareStrongEncryption(buffer, j);
|
||||
break;
|
||||
}
|
||||
|
||||
// move to the next Header in the extra field
|
||||
j = start + dataSize + 4;
|
||||
}
|
||||
}
|
||||
return additionalBytesRead;
|
||||
}
|
||||
|
||||
private int ProcessExtraFieldPkwareStrongEncryption(byte[] Buffer, int j)
|
||||
{
|
||||
// Value Size Description
|
||||
// ----- ---- -----------
|
||||
// 0x0017 2 bytes Tag for this "extra" block type
|
||||
// TSize 2 bytes Size of data that follows
|
||||
// Format 2 bytes Format definition for this record
|
||||
// AlgID 2 bytes Encryption algorithm identifier
|
||||
// Bitlen 2 bytes Bit length of encryption key
|
||||
// Flags 2 bytes Processing flags
|
||||
// CertData TSize-8 Certificate decryption extra field data
|
||||
// (refer to the explanation for CertData
|
||||
// in the section describing the
|
||||
// Certificate Processing Method under
|
||||
// the Strong Encryption Specification)
|
||||
|
||||
j += 2;
|
||||
_UnsupportedAlgorithmId = (UInt16)(Buffer[j++] + Buffer[j++] * 256);
|
||||
_Encryption_FromZipFile = _Encryption = EncryptionAlgorithm.Unsupported;
|
||||
|
||||
// DotNetZip doesn't support this algorithm, but we don't need to throw
|
||||
// here. we might just be reading the archive, which is fine. We'll
|
||||
// need to throw if Extract() is called.
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
|
||||
#if AESCRYPTO
|
||||
private int ProcessExtraFieldWinZipAes(byte[] buffer, int j, Int16 dataSize, long posn)
|
||||
{
|
||||
if (this._CompressionMethod == 0x0063)
|
||||
{
|
||||
if ((this._BitField & 0x01) != 0x01)
|
||||
throw new BadReadException(String.Format(" Inconsistent metadata at position 0x{0:X16}", posn));
|
||||
|
||||
this._sourceIsEncrypted = true;
|
||||
|
||||
//this._aesCrypto = new WinZipAesCrypto(this);
|
||||
// see spec at http://www.winzip.com/aes_info.htm
|
||||
if (dataSize != 7)
|
||||
throw new BadReadException(String.Format(" Inconsistent size (0x{0:X4}) in WinZip AES field at position 0x{1:X16}", dataSize, posn));
|
||||
|
||||
this._WinZipAesMethod = BitConverter.ToInt16(buffer, j);
|
||||
j += 2;
|
||||
if (this._WinZipAesMethod != 0x01 && this._WinZipAesMethod != 0x02)
|
||||
throw new BadReadException(String.Format(" Unexpected vendor version number (0x{0:X4}) for WinZip AES metadata at position 0x{1:X16}",
|
||||
this._WinZipAesMethod, posn));
|
||||
|
||||
Int16 vendorId = BitConverter.ToInt16(buffer, j);
|
||||
j += 2;
|
||||
if (vendorId != 0x4541)
|
||||
throw new BadReadException(String.Format(" Unexpected vendor ID (0x{0:X4}) for WinZip AES metadata at position 0x{1:X16}", vendorId, posn));
|
||||
|
||||
int keystrength = (buffer[j] == 1) ? 128 : (buffer[j] == 3) ? 256 : -1;
|
||||
if (keystrength < 0)
|
||||
throw new BadReadException(String.Format("Invalid key strength ({0})", keystrength));
|
||||
|
||||
_Encryption_FromZipFile = this._Encryption = (keystrength == 128)
|
||||
? EncryptionAlgorithm.WinZipAes128
|
||||
: EncryptionAlgorithm.WinZipAes256;
|
||||
|
||||
j++;
|
||||
|
||||
// set the actual compression method
|
||||
this._CompressionMethod_FromZipFile =
|
||||
this._CompressionMethod = BitConverter.ToInt16(buffer, j);
|
||||
j += 2; // for the next segment of the extra field
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
private delegate T Func<T>();
|
||||
|
||||
private int ProcessExtraFieldZip64(byte[] buffer, int j, Int16 dataSize, long posn)
|
||||
{
|
||||
// The PKWare spec says that any of {UncompressedSize, CompressedSize,
|
||||
// RelativeOffset} exceeding 0xFFFFFFFF can lead to the ZIP64 header,
|
||||
// and the ZIP64 header may contain one or more of those. If the
|
||||
// values are present, they will be found in the prescribed order.
|
||||
// There may also be a 4-byte "disk start number."
|
||||
// This means that the DataSize must be 28 bytes or less.
|
||||
|
||||
this._InputUsesZip64 = true;
|
||||
|
||||
// workitem 7941: check datasize before reading.
|
||||
if (dataSize > 28)
|
||||
throw new BadReadException(String.Format(" Inconsistent size (0x{0:X4}) for ZIP64 extra field at position 0x{1:X16}",
|
||||
dataSize, posn));
|
||||
int remainingData = dataSize;
|
||||
|
||||
var slurp = new Func<Int64>( () => {
|
||||
if (remainingData < 8)
|
||||
throw new BadReadException(String.Format(" Missing data for ZIP64 extra field, position 0x{0:X16}", posn));
|
||||
var x = BitConverter.ToInt64(buffer, j);
|
||||
j+= 8;
|
||||
remainingData -= 8;
|
||||
return x;
|
||||
});
|
||||
|
||||
if (this._UncompressedSize == 0xFFFFFFFF)
|
||||
this._UncompressedSize = slurp();
|
||||
|
||||
if (this._CompressedSize == 0xFFFFFFFF)
|
||||
this._CompressedSize = slurp();
|
||||
|
||||
if (this._RelativeOffsetOfLocalHeader == 0xFFFFFFFF)
|
||||
this._RelativeOffsetOfLocalHeader = slurp();
|
||||
|
||||
// Ignore anything else. Potentially there are 4 more bytes for the
|
||||
// disk start number. DotNetZip currently doesn't handle multi-disk
|
||||
// archives.
|
||||
return j;
|
||||
}
|
||||
|
||||
|
||||
private int ProcessExtraFieldInfoZipTimes(byte[] buffer, int j, Int16 dataSize, long posn)
|
||||
{
|
||||
if (dataSize != 12 && dataSize != 8)
|
||||
throw new BadReadException(String.Format(" Unexpected size (0x{0:X4}) for InfoZip v1 extra field at position 0x{1:X16}", dataSize, posn));
|
||||
|
||||
Int32 timet = BitConverter.ToInt32(buffer, j);
|
||||
this._Mtime = _unixEpoch.AddSeconds(timet);
|
||||
j += 4;
|
||||
|
||||
timet = BitConverter.ToInt32(buffer, j);
|
||||
this._Atime = _unixEpoch.AddSeconds(timet);
|
||||
j += 4;
|
||||
|
||||
this._Ctime = DateTime.UtcNow;
|
||||
|
||||
_ntfsTimesAreSet = true;
|
||||
_timestamp |= ZipEntryTimestamp.InfoZip1; return j;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private int ProcessExtraFieldUnixTimes(byte[] buffer, int j, Int16 dataSize, long posn)
|
||||
{
|
||||
// The Unix filetimes are 32-bit unsigned integers,
|
||||
// storing seconds since Unix epoch.
|
||||
|
||||
if (dataSize != 13 && dataSize != 9 && dataSize != 5)
|
||||
throw new BadReadException(String.Format(" Unexpected size (0x{0:X4}) for Extended Timestamp extra field at position 0x{1:X16}", dataSize, posn));
|
||||
|
||||
int remainingData = dataSize;
|
||||
|
||||
var slurp = new Func<DateTime>( () => {
|
||||
Int32 timet = BitConverter.ToInt32(buffer, j);
|
||||
j += 4;
|
||||
remainingData -= 4;
|
||||
return _unixEpoch.AddSeconds(timet);
|
||||
});
|
||||
|
||||
if (dataSize == 13 || _readExtraDepth > 0)
|
||||
{
|
||||
byte flag = buffer[j++];
|
||||
remainingData--;
|
||||
|
||||
if ((flag & 0x0001) != 0 && remainingData >= 4)
|
||||
this._Mtime = slurp();
|
||||
|
||||
this._Atime = ((flag & 0x0002) != 0 && remainingData >= 4)
|
||||
? slurp()
|
||||
: DateTime.UtcNow;
|
||||
|
||||
this._Ctime = ((flag & 0x0004) != 0 && remainingData >= 4)
|
||||
? slurp()
|
||||
:DateTime.UtcNow;
|
||||
|
||||
_timestamp |= ZipEntryTimestamp.Unix;
|
||||
_ntfsTimesAreSet = true;
|
||||
_emitUnixTimes = true;
|
||||
}
|
||||
else
|
||||
ReadExtraField(); // will recurse
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
|
||||
private int ProcessExtraFieldWindowsTimes(byte[] buffer, int j, Int16 dataSize, long posn)
|
||||
{
|
||||
// The NTFS filetimes are 64-bit unsigned integers, stored in Intel
|
||||
// (least significant byte first) byte order. They are expressed as the
|
||||
// number of 1.0E-07 seconds (1/10th microseconds!) past WinNT "epoch",
|
||||
// which is "01-Jan-1601 00:00:00 UTC".
|
||||
//
|
||||
// HeaderId 2 bytes 0x000a == NTFS stuff
|
||||
// Datasize 2 bytes ?? (usually 32)
|
||||
// reserved 4 bytes ??
|
||||
// timetag 2 bytes 0x0001 == time
|
||||
// size 2 bytes 24 == 8 bytes each for ctime, mtime, atime
|
||||
// mtime 8 bytes win32 ticks since win32epoch
|
||||
// atime 8 bytes win32 ticks since win32epoch
|
||||
// ctime 8 bytes win32 ticks since win32epoch
|
||||
|
||||
if (dataSize != 32)
|
||||
throw new BadReadException(String.Format(" Unexpected size (0x{0:X4}) for NTFS times extra field at position 0x{1:X16}", dataSize, posn));
|
||||
|
||||
j += 4; // reserved
|
||||
Int16 timetag = (Int16)(buffer[j] + buffer[j + 1] * 256);
|
||||
Int16 addlsize = (Int16)(buffer[j + 2] + buffer[j + 3] * 256);
|
||||
j += 4; // tag and size
|
||||
|
||||
if (timetag == 0x0001 && addlsize == 24)
|
||||
{
|
||||
Int64 z = BitConverter.ToInt64(buffer, j);
|
||||
this._Mtime = DateTime.FromFileTimeUtc(z);
|
||||
j += 8;
|
||||
|
||||
// At this point the library *could* set the LastModified value
|
||||
// to coincide with the Mtime value. In theory, they refer to
|
||||
// the same property of the file, and should be the same anyway,
|
||||
// allowing for differences in precision. But they are
|
||||
// independent quantities in the zip archive, and this library
|
||||
// will keep them separate in the object model. There is no ill
|
||||
// effect from this, because as files are extracted, the
|
||||
// higher-precision value (Mtime) is used if it is present.
|
||||
// Apps may wish to compare the Mtime versus LastModified
|
||||
// values, but any difference when both are present is not
|
||||
// germaine to the correctness of the library. but note: when
|
||||
// explicitly setting either value, both are set. See the setter
|
||||
// for LastModified or the SetNtfsTimes() method.
|
||||
|
||||
z = BitConverter.ToInt64(buffer, j);
|
||||
this._Atime = DateTime.FromFileTimeUtc(z);
|
||||
j += 8;
|
||||
|
||||
z = BitConverter.ToInt64(buffer, j);
|
||||
this._Ctime = DateTime.FromFileTimeUtc(z);
|
||||
j += 8;
|
||||
|
||||
_ntfsTimesAreSet = true;
|
||||
_timestamp |= ZipEntryTimestamp.Windows;
|
||||
_emitNtfsTimes = true;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,69 @@
|
||||
// ZipEntrySource.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-November-19 11:18:42>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
/// <summary>
|
||||
/// An enum that specifies the source of the ZipEntry.
|
||||
/// </summary>
|
||||
public enum ZipEntrySource
|
||||
{
|
||||
/// <summary>
|
||||
/// Default value. Invalid on a bonafide ZipEntry.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The entry was instantiated by calling AddFile() or another method that
|
||||
/// added an entry from the filesystem.
|
||||
/// </summary>
|
||||
FileSystem,
|
||||
|
||||
/// <summary>
|
||||
/// The entry was instantiated via <see cref="Ionic.Zip.ZipFile.AddEntry(string,string)"/> or
|
||||
/// <see cref="Ionic.Zip.ZipFile.AddEntry(string,System.IO.Stream)"/> .
|
||||
/// </summary>
|
||||
Stream,
|
||||
|
||||
/// <summary>
|
||||
/// The ZipEntry was instantiated by reading a zipfile.
|
||||
/// </summary>
|
||||
ZipFile,
|
||||
|
||||
/// <summary>
|
||||
/// The content for the ZipEntry will be or was provided by the WriteDelegate.
|
||||
/// </summary>
|
||||
WriteDelegate,
|
||||
|
||||
/// <summary>
|
||||
/// The content for the ZipEntry will be obtained from the stream dispensed by the <c>OpenDelegate</c>.
|
||||
/// The entry was instantiated via <see cref="Ionic.Zip.ZipFile.AddEntry(string,OpenDelegate,CloseDelegate)"/>.
|
||||
/// </summary>
|
||||
JitStream,
|
||||
|
||||
/// <summary>
|
||||
/// The content for the ZipEntry will be or was obtained from a <c>ZipOutputStream</c>.
|
||||
/// </summary>
|
||||
ZipOutputStream,
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// ZipErrorAction.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-September-01 18:43:20>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the ZipErrorAction enum, which provides
|
||||
// an action to take when errors occur when opening or reading
|
||||
// files to be added to a zip file.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
/// <summary>
|
||||
/// An enum providing the options when an error occurs during opening or reading
|
||||
/// of a file or directory that is being saved to a zip file.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This enum describes the actions that the library can take when an error occurs
|
||||
/// opening or reading a file, as it is being saved into a Zip archive.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// In some cases an error will occur when DotNetZip tries to open a file to be
|
||||
/// added to the zip archive. In other cases, an error might occur after the
|
||||
/// file has been successfully opened, while DotNetZip is reading the file.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The first problem might occur when calling AddDirectory() on a directory
|
||||
/// that contains a Clipper .dbf file; the file is locked by Clipper and
|
||||
/// cannot be opened by another process. An example of the second problem is
|
||||
/// the ERROR_LOCK_VIOLATION that results when a file is opened by another
|
||||
/// process, but not locked, and a range lock has been taken on the file.
|
||||
/// Microsoft Outlook takes range locks on .PST files.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public enum ZipErrorAction
|
||||
{
|
||||
/// <summary>
|
||||
/// Throw an exception when an error occurs while zipping. This is the default
|
||||
/// behavior. (For COM clients, this is a 0 (zero).)
|
||||
/// </summary>
|
||||
Throw,
|
||||
|
||||
/// <summary>
|
||||
/// When an error occurs during zipping, for example a file cannot be opened,
|
||||
/// skip the file causing the error, and continue zipping. (For COM clients,
|
||||
/// this is a 1.)
|
||||
/// </summary>
|
||||
Skip,
|
||||
|
||||
/// <summary>
|
||||
/// When an error occurs during zipping, for example a file cannot be opened,
|
||||
/// retry the operation that caused the error. Be careful with this option. If
|
||||
/// the error is not temporary, the library will retry forever. (For COM
|
||||
/// clients, this is a 2.)
|
||||
/// </summary>
|
||||
Retry,
|
||||
|
||||
/// <summary>
|
||||
/// When an error occurs, invoke the zipError event. The event type used is
|
||||
/// <see cref="ZipProgressEventType.Error_Saving"/>. A typical use of this option:
|
||||
/// a GUI application may wish to pop up a dialog to allow the user to view the
|
||||
/// error that occurred, and choose an appropriate action. After your
|
||||
/// processing in the error event, if you want to skip the file, set <see
|
||||
/// cref="ZipEntry.ZipErrorAction"/> on the
|
||||
/// <c>ZipProgressEventArgs.CurrentEntry</c> to <c>Skip</c>. If you want the
|
||||
/// exception to be thrown, set <c>ZipErrorAction</c> on the <c>CurrentEntry</c>
|
||||
/// to <c>Throw</c>. If you want to cancel the zip, set
|
||||
/// <c>ZipProgressEventArgs.Cancel</c> to true. Cancelling differs from using
|
||||
/// Skip in that a cancel will not save any further entries, if there are any.
|
||||
/// (For COM clients, the value of this enum is a 3.)
|
||||
/// </summary>
|
||||
InvokeErrorEvent,
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,352 @@
|
||||
// ZipFile.Check.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-31 14:40:50>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the methods for doing Checks on zip files.
|
||||
// These are not necessary to include in the Reduced or CF
|
||||
// version of the library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
public partial class ZipFile
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks a zip file to see if its directory is consistent.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// In cases of data error, the directory within a zip file can get out
|
||||
/// of synch with the entries in the zip file. This method checks the
|
||||
/// given zip file and returns true if this has occurred.
|
||||
/// </para>
|
||||
///
|
||||
/// <para> This method may take a long time to run for large zip files. </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This method is not supported in the Reduced or Compact Framework
|
||||
/// versions of DotNetZip.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Developers using COM can use the <see
|
||||
/// cref="ComHelper.CheckZip(String)">ComHelper.CheckZip(String)</see>
|
||||
/// method.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="zipFileName">The filename to of the zip file to check.</param>
|
||||
///
|
||||
/// <returns>true if the named zip file checks OK. Otherwise, false. </returns>
|
||||
///
|
||||
/// <seealso cref="FixZipDirectory(string)"/>
|
||||
/// <seealso cref="CheckZip(string,bool,System.IO.TextWriter)"/>
|
||||
public static bool CheckZip(string zipFileName)
|
||||
{
|
||||
return CheckZip(zipFileName, false, null);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks a zip file to see if its directory is consistent,
|
||||
/// and optionally fixes the directory if necessary.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// In cases of data error, the directory within a zip file can get out of
|
||||
/// synch with the entries in the zip file. This method checks the given
|
||||
/// zip file, and returns true if this has occurred. It also optionally
|
||||
/// fixes the zipfile, saving the fixed copy in <em>Name</em>_Fixed.zip.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This method may take a long time to run for large zip files. It
|
||||
/// will take even longer if the file actually needs to be fixed, and if
|
||||
/// <c>fixIfNecessary</c> is true.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This method is not supported in the Reduced or Compact
|
||||
/// Framework versions of DotNetZip.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="zipFileName">The filename to of the zip file to check.</param>
|
||||
///
|
||||
/// <param name="fixIfNecessary">If true, the method will fix the zip file if
|
||||
/// necessary.</param>
|
||||
///
|
||||
/// <param name="writer">
|
||||
/// a TextWriter in which messages generated while checking will be written.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>true if the named zip is OK; false if the file needs to be fixed.</returns>
|
||||
///
|
||||
/// <seealso cref="CheckZip(string)"/>
|
||||
/// <seealso cref="FixZipDirectory(string)"/>
|
||||
public static bool CheckZip(string zipFileName, bool fixIfNecessary,
|
||||
TextWriter writer)
|
||||
|
||||
{
|
||||
ZipFile zip1 = null, zip2 = null;
|
||||
bool isOk = true;
|
||||
try
|
||||
{
|
||||
zip1 = new ZipFile();
|
||||
zip1.FullScan = true;
|
||||
zip1.Initialize(zipFileName);
|
||||
|
||||
zip2 = ZipFile.Read(zipFileName);
|
||||
|
||||
foreach (var e1 in zip1)
|
||||
{
|
||||
foreach (var e2 in zip2)
|
||||
{
|
||||
if (e1.FileName == e2.FileName)
|
||||
{
|
||||
if (e1._RelativeOffsetOfLocalHeader != e2._RelativeOffsetOfLocalHeader)
|
||||
{
|
||||
isOk = false;
|
||||
if (writer != null)
|
||||
writer.WriteLine("{0}: mismatch in RelativeOffsetOfLocalHeader (0x{1:X16} != 0x{2:X16})",
|
||||
e1.FileName, e1._RelativeOffsetOfLocalHeader,
|
||||
e2._RelativeOffsetOfLocalHeader);
|
||||
}
|
||||
if (e1._CompressedSize != e2._CompressedSize)
|
||||
{
|
||||
isOk = false;
|
||||
if (writer != null)
|
||||
writer.WriteLine("{0}: mismatch in CompressedSize (0x{1:X16} != 0x{2:X16})",
|
||||
e1.FileName, e1._CompressedSize,
|
||||
e2._CompressedSize);
|
||||
}
|
||||
if (e1._UncompressedSize != e2._UncompressedSize)
|
||||
{
|
||||
isOk = false;
|
||||
if (writer != null)
|
||||
writer.WriteLine("{0}: mismatch in UncompressedSize (0x{1:X16} != 0x{2:X16})",
|
||||
e1.FileName, e1._UncompressedSize,
|
||||
e2._UncompressedSize);
|
||||
}
|
||||
if (e1.CompressionMethod != e2.CompressionMethod)
|
||||
{
|
||||
isOk = false;
|
||||
if (writer != null)
|
||||
writer.WriteLine("{0}: mismatch in CompressionMethod (0x{1:X4} != 0x{2:X4})",
|
||||
e1.FileName, e1.CompressionMethod,
|
||||
e2.CompressionMethod);
|
||||
}
|
||||
if (e1.Crc != e2.Crc)
|
||||
{
|
||||
isOk = false;
|
||||
if (writer != null)
|
||||
writer.WriteLine("{0}: mismatch in Crc32 (0x{1:X4} != 0x{2:X4})",
|
||||
e1.FileName, e1.Crc,
|
||||
e2.Crc);
|
||||
}
|
||||
|
||||
// found a match, so stop the inside loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zip2.Dispose();
|
||||
zip2 = null;
|
||||
|
||||
if (!isOk && fixIfNecessary)
|
||||
{
|
||||
string newFileName = Path.GetFileNameWithoutExtension(zipFileName);
|
||||
newFileName = System.String.Format("{0}_fixed.zip", newFileName);
|
||||
zip1.Save(newFileName);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (zip1 != null) zip1.Dispose();
|
||||
if (zip2 != null) zip2.Dispose();
|
||||
}
|
||||
return isOk;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Rewrite the directory within a zipfile.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// In cases of data error, the directory in a zip file can get out of
|
||||
/// synch with the entries in the zip file. This method attempts to fix
|
||||
/// the zip file if this has occurred.
|
||||
/// </para>
|
||||
///
|
||||
/// <para> This can take a long time for large zip files. </para>
|
||||
///
|
||||
/// <para> This won't work if the zip file uses a non-standard
|
||||
/// code page - neither IBM437 nor UTF-8. </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This method is not supported in the Reduced or Compact Framework
|
||||
/// versions of DotNetZip.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Developers using COM can use the <see
|
||||
/// cref="ComHelper.FixZipDirectory(String)">ComHelper.FixZipDirectory(String)</see>
|
||||
/// method.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="zipFileName">The filename to of the zip file to fix.</param>
|
||||
///
|
||||
/// <seealso cref="CheckZip(string)"/>
|
||||
/// <seealso cref="CheckZip(string,bool,System.IO.TextWriter)"/>
|
||||
public static void FixZipDirectory(string zipFileName)
|
||||
{
|
||||
using (var zip = new ZipFile())
|
||||
{
|
||||
zip.FullScan = true;
|
||||
zip.Initialize(zipFileName);
|
||||
zip.Save(zipFileName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Verify the password on a zip file.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Keep in mind that passwords in zipfiles are applied to
|
||||
/// zip entries, not to the entire zip file. So testing a
|
||||
/// zipfile for a particular password doesn't work in the
|
||||
/// general case. On the other hand, it's often the case
|
||||
/// that a single password will be used on all entries in a
|
||||
/// zip file. This method works for that case.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// There is no way to check a password without doing the
|
||||
/// decryption. So this code decrypts and extracts the given
|
||||
/// zipfile into <see cref="System.IO.Stream.Null"/>
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="zipFileName">The filename to of the zip file to fix.</param>
|
||||
///
|
||||
/// <param name="password">The password to check.</param>
|
||||
///
|
||||
/// <returns>a bool indicating whether the password matches.</returns>
|
||||
public static bool CheckZipPassword(string zipFileName, string password)
|
||||
{
|
||||
// workitem 13664
|
||||
bool success = false;
|
||||
try
|
||||
{
|
||||
using (ZipFile zip1 = ZipFile.Read(zipFileName))
|
||||
{
|
||||
foreach (var e in zip1)
|
||||
{
|
||||
if (!e.IsDirectory && e.UsesEncryption)
|
||||
{
|
||||
e.ExtractWithPassword(System.IO.Stream.Null, password);
|
||||
}
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
catch(Ionic.Zip.BadPasswordException) { }
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides a human-readable string with information about the ZipFile.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The information string contains 10 lines or so, about each ZipEntry,
|
||||
/// describing whether encryption is in use, the compressed and uncompressed
|
||||
/// length of the entry, the offset of the entry, and so on. As a result the
|
||||
/// information string can be very long for zip files that contain many
|
||||
/// entries.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This information is mostly useful for diagnostic purposes.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public string Info
|
||||
{
|
||||
get
|
||||
{
|
||||
var builder = new System.Text.StringBuilder();
|
||||
builder.Append(string.Format(" ZipFile: {0}\n", this.Name));
|
||||
if (!string.IsNullOrEmpty(this._Comment))
|
||||
{
|
||||
builder.Append(string.Format(" Comment: {0}\n", this._Comment));
|
||||
}
|
||||
if (this._versionMadeBy != 0)
|
||||
{
|
||||
builder.Append(string.Format(" version made by: 0x{0:X4}\n", this._versionMadeBy));
|
||||
}
|
||||
if (this._versionNeededToExtract != 0)
|
||||
{
|
||||
builder.Append(string.Format("needed to extract: 0x{0:X4}\n", this._versionNeededToExtract));
|
||||
}
|
||||
|
||||
builder.Append(string.Format(" uses ZIP64: {0}\n", this.InputUsesZip64));
|
||||
|
||||
builder.Append(string.Format(" disk with CD: {0}\n", this._diskNumberWithCd));
|
||||
if (this._OffsetOfCentralDirectory == 0xFFFFFFFF)
|
||||
builder.Append(string.Format(" CD64 offset: 0x{0:X16}\n", this._OffsetOfCentralDirectory64));
|
||||
else
|
||||
builder.Append(string.Format(" CD offset: 0x{0:X8}\n", this._OffsetOfCentralDirectory));
|
||||
builder.Append("\n");
|
||||
foreach (ZipEntry entry in this._entries.Values)
|
||||
{
|
||||
builder.Append(entry.Info);
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,298 @@
|
||||
// ZipFile.Extract.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-31 14:45:18>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the methods for Extract operations on zip files.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
|
||||
public partial class ZipFile
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Extracts all of the items in the zip archive, to the specified path in the
|
||||
/// filesystem. The path can be relative or fully-qualified.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method will extract all entries in the <c>ZipFile</c> to the
|
||||
/// specified path.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// If an extraction of a file from the zip archive would overwrite an
|
||||
/// existing file in the filesystem, the action taken is dictated by the
|
||||
/// ExtractExistingFile property, which overrides any setting you may have
|
||||
/// made on individual ZipEntry instances. By default, if you have not
|
||||
/// set that property on the <c>ZipFile</c> instance, the entry will not
|
||||
/// be extracted, the existing file will not be overwritten and an
|
||||
/// exception will be thrown. To change this, set the property, or use the
|
||||
/// <see cref="ZipFile.ExtractAll(string,
|
||||
/// Ionic.Zip.ExtractExistingFileAction)" /> overload that allows you to
|
||||
/// specify an ExtractExistingFileAction parameter.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The action to take when an extract would overwrite an existing file
|
||||
/// applies to all entries. If you want to set this on a per-entry basis,
|
||||
/// then you must use one of the <see
|
||||
/// cref="ZipEntry.Extract()">ZipEntry.Extract</see> methods.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This method will send verbose output messages to the <see
|
||||
/// cref="StatusMessageTextWriter"/>, if it is set on the <c>ZipFile</c>
|
||||
/// instance.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// You may wish to take advantage of the <c>ExtractProgress</c> event.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// About timestamps: When extracting a file entry from a zip archive, the
|
||||
/// extracted file gets the last modified time of the entry as stored in
|
||||
/// the archive. The archive may also store extended file timestamp
|
||||
/// information, including last accessed and created times. If these are
|
||||
/// present in the <c>ZipEntry</c>, then the extracted file will also get
|
||||
/// these times.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A Directory entry is somewhat different. It will get the times as
|
||||
/// described for a file entry, but, if there are file entries in the zip
|
||||
/// archive that, when extracted, appear in the just-created directory,
|
||||
/// then when those file entries are extracted, the last modified and last
|
||||
/// accessed times of the directory will change, as a side effect. The
|
||||
/// result is that after an extraction of a directory and a number of
|
||||
/// files within the directory, the last modified and last accessed
|
||||
/// timestamps on the directory will reflect the time that the last file
|
||||
/// was extracted into the directory, rather than the time stored in the
|
||||
/// zip archive for the directory.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// To compensate, when extracting an archive with <c>ExtractAll</c>,
|
||||
/// DotNetZip will extract all the file and directory entries as described
|
||||
/// above, but it will then make a second pass on the directories, and
|
||||
/// reset the times on the directories to reflect what is stored in the
|
||||
/// zip archive.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This compensation is performed only within the context of an
|
||||
/// <c>ExtractAll</c>. If you call <c>ZipEntry.Extract</c> on a directory
|
||||
/// entry, the timestamps on directory in the filesystem will reflect the
|
||||
/// times stored in the zip. If you then call <c>ZipEntry.Extract</c> on
|
||||
/// a file entry, which is extracted into the directory, the timestamps on
|
||||
/// the directory will be updated to the current time.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
/// This example extracts all the entries in a zip archive file, to the
|
||||
/// specified target directory. The extraction will overwrite any
|
||||
/// existing files silently.
|
||||
///
|
||||
/// <code>
|
||||
/// String TargetDirectory= "unpack";
|
||||
/// using(ZipFile zip= ZipFile.Read(ZipFileToExtract))
|
||||
/// {
|
||||
/// zip.ExtractExistingFile= ExtractExistingFileAction.OverwriteSilently;
|
||||
/// zip.ExtractAll(TargetDirectory);
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Dim TargetDirectory As String = "unpack"
|
||||
/// Using zip As ZipFile = ZipFile.Read(ZipFileToExtract)
|
||||
/// zip.ExtractExistingFile= ExtractExistingFileAction.OverwriteSilently
|
||||
/// zip.ExtractAll(TargetDirectory)
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// <seealso cref="Ionic.Zip.ZipFile.ExtractProgress"/>
|
||||
/// <seealso cref="Ionic.Zip.ZipFile.ExtractExistingFile"/>
|
||||
///
|
||||
/// <param name="path">
|
||||
/// The path to which the contents of the zipfile will be extracted.
|
||||
/// The path can be relative or fully-qualified.
|
||||
/// </param>
|
||||
///
|
||||
public void ExtractAll(string path)
|
||||
{
|
||||
_InternalExtractAll(path, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Extracts all of the items in the zip archive, to the specified path in the
|
||||
/// filesystem, using the specified behavior when extraction would overwrite an
|
||||
/// existing file.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// This method will extract all entries in the <c>ZipFile</c> to the specified
|
||||
/// path. For an extraction that would overwrite an existing file, the behavior
|
||||
/// is dictated by <paramref name="extractExistingFile"/>, which overrides any
|
||||
/// setting you may have made on individual ZipEntry instances.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The action to take when an extract would overwrite an existing file
|
||||
/// applies to all entries. If you want to set this on a per-entry basis,
|
||||
/// then you must use <see cref="ZipEntry.Extract(String,
|
||||
/// ExtractExistingFileAction)" /> or one of the similar methods.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Calling this method is equivalent to setting the <see
|
||||
/// cref="ExtractExistingFile"/> property and then calling <see
|
||||
/// cref="ExtractAll(String)"/>.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This method will send verbose output messages to the
|
||||
/// <see cref="StatusMessageTextWriter"/>, if it is set on the <c>ZipFile</c> instance.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
/// This example extracts all the entries in a zip archive file, to the
|
||||
/// specified target directory. It does not overwrite any existing files.
|
||||
/// <code>
|
||||
/// String TargetDirectory= "c:\\unpack";
|
||||
/// using(ZipFile zip= ZipFile.Read(ZipFileToExtract))
|
||||
/// {
|
||||
/// zip.ExtractAll(TargetDirectory, ExtractExistingFileAction.DontOverwrite);
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Dim TargetDirectory As String = "c:\unpack"
|
||||
/// Using zip As ZipFile = ZipFile.Read(ZipFileToExtract)
|
||||
/// zip.ExtractAll(TargetDirectory, ExtractExistingFileAction.DontOverwrite)
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// <param name="path">
|
||||
/// The path to which the contents of the zipfile will be extracted.
|
||||
/// The path can be relative or fully-qualified.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="extractExistingFile">
|
||||
/// The action to take if extraction would overwrite an existing file.
|
||||
/// </param>
|
||||
/// <seealso cref="ExtractSelectedEntries(String,ExtractExistingFileAction)"/>
|
||||
public void ExtractAll(string path, ExtractExistingFileAction extractExistingFile)
|
||||
{
|
||||
ExtractExistingFile = extractExistingFile;
|
||||
_InternalExtractAll(path, true);
|
||||
}
|
||||
|
||||
|
||||
private void _InternalExtractAll(string path, bool overrideExtractExistingProperty)
|
||||
{
|
||||
bool header = Verbose;
|
||||
_inExtractAll = true;
|
||||
try
|
||||
{
|
||||
OnExtractAllStarted(path);
|
||||
|
||||
int n = 0;
|
||||
foreach (ZipEntry e in _entries.Values)
|
||||
{
|
||||
if (header)
|
||||
{
|
||||
StatusMessageTextWriter.WriteLine("\n{1,-22} {2,-8} {3,4} {4,-8} {0}",
|
||||
"Name", "Modified", "Size", "Ratio", "Packed");
|
||||
StatusMessageTextWriter.WriteLine(new System.String('-', 72));
|
||||
header = false;
|
||||
}
|
||||
if (Verbose)
|
||||
{
|
||||
StatusMessageTextWriter.WriteLine("{1,-22} {2,-8} {3,4:F0}% {4,-8} {0}",
|
||||
e.FileName,
|
||||
e.LastModified.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
e.UncompressedSize,
|
||||
e.CompressionRatio,
|
||||
e.CompressedSize);
|
||||
if (!String.IsNullOrEmpty(e.Comment))
|
||||
StatusMessageTextWriter.WriteLine(" Comment: {0}", e.Comment);
|
||||
}
|
||||
e.Password = _Password; // this may be null
|
||||
OnExtractEntry(n, true, e, path);
|
||||
if (overrideExtractExistingProperty)
|
||||
e.ExtractExistingFile = this.ExtractExistingFile;
|
||||
e.Extract(path);
|
||||
n++;
|
||||
OnExtractEntry(n, false, e, path);
|
||||
if (_extractOperationCanceled)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!_extractOperationCanceled)
|
||||
{
|
||||
// workitem 8264:
|
||||
// now, set times on directory entries, again.
|
||||
// The problem is, extracting a file changes the times on the parent
|
||||
// directory. So after all files have been extracted, we have to
|
||||
// run through the directories again.
|
||||
foreach (ZipEntry e in _entries.Values)
|
||||
{
|
||||
// check if it is a directory
|
||||
if ((e.IsDirectory) || (e.FileName.EndsWith("/")))
|
||||
{
|
||||
string outputFile = (e.FileName.StartsWith("/"))
|
||||
? Path.Combine(path, e.FileName.Substring(1))
|
||||
: Path.Combine(path, e.FileName);
|
||||
|
||||
e._SetTimes(outputFile, false);
|
||||
}
|
||||
}
|
||||
OnExtractAllCompleted(path);
|
||||
}
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
_inExtractAll = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,964 @@
|
||||
// ZipFile.Save.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-August-05 13:31:23>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the methods for Save operations on zip files.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
|
||||
public partial class ZipFile
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Delete file with retry on UnauthorizedAccessException.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// When calling File.Delete() on a file that has been "recently"
|
||||
/// created, the call sometimes fails with
|
||||
/// UnauthorizedAccessException. This method simply retries the Delete 3
|
||||
/// times with a sleep between tries.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name='filename'>the name of the file to be deleted</param>
|
||||
private void DeleteFileWithRetry(string filename)
|
||||
{
|
||||
bool done = false;
|
||||
int nRetries = 3;
|
||||
for (int i=0; i < nRetries && !done; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(filename);
|
||||
done = true;
|
||||
}
|
||||
catch (System.UnauthorizedAccessException)
|
||||
{
|
||||
Console.WriteLine("************************************************** Retry delete.");
|
||||
System.Threading.Thread.Sleep(200+i*200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Saves the Zip archive to a file, specified by the Name property of the
|
||||
/// <c>ZipFile</c>.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The <c>ZipFile</c> instance is written to storage, typically a zip file
|
||||
/// in a filesystem, only when the caller calls <c>Save</c>. In the typical
|
||||
/// case, the Save operation writes the zip content to a temporary file, and
|
||||
/// then renames the temporary file to the desired name. If necessary, this
|
||||
/// method will delete a pre-existing file before the rename.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The <see cref="ZipFile.Name"/> property is specified either explicitly,
|
||||
/// or implicitly using one of the parameterized ZipFile constructors. For
|
||||
/// COM Automation clients, the <c>Name</c> property must be set explicitly,
|
||||
/// because COM Automation clients cannot call parameterized constructors.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// When using a filesystem file for the Zip output, it is possible to call
|
||||
/// <c>Save</c> multiple times on the <c>ZipFile</c> instance. With each
|
||||
/// call the zip content is re-written to the same output file.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Data for entries that have been added to the <c>ZipFile</c> instance is
|
||||
/// written to the output when the <c>Save</c> method is called. This means
|
||||
/// that the input streams for those entries must be available at the time
|
||||
/// the application calls <c>Save</c>. If, for example, the application
|
||||
/// adds entries with <c>AddEntry</c> using a dynamically-allocated
|
||||
/// <c>MemoryStream</c>, the memory stream must not have been disposed
|
||||
/// before the call to <c>Save</c>. See the <see
|
||||
/// cref="ZipEntry.InputStream"/> property for more discussion of the
|
||||
/// availability requirements of the input stream for an entry, and an
|
||||
/// approach for providing just-in-time stream lifecycle management.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <seealso cref="Ionic.Zip.ZipFile.AddEntry(String, System.IO.Stream)"/>
|
||||
///
|
||||
/// <exception cref="Ionic.Zip.BadStateException">
|
||||
/// Thrown if you haven't specified a location or stream for saving the zip,
|
||||
/// either in the constructor or by setting the Name property, or if you try
|
||||
/// to save a regular zip archive to a filename with a .exe extension.
|
||||
/// </exception>
|
||||
///
|
||||
/// <exception cref="System.OverflowException">
|
||||
/// Thrown if <see cref="MaxOutputSegmentSize"/> is non-zero, and the number
|
||||
/// of segments that would be generated for the spanned zip file during the
|
||||
/// save operation exceeds 99. If this happens, you need to increase the
|
||||
/// segment size.
|
||||
/// </exception>
|
||||
///
|
||||
public void Save()
|
||||
{
|
||||
try
|
||||
{
|
||||
bool thisSaveUsedZip64 = false;
|
||||
_saveOperationCanceled = false;
|
||||
_numberOfSegmentsForMostRecentSave = 0;
|
||||
OnSaveStarted();
|
||||
|
||||
if (WriteStream == null)
|
||||
throw new BadStateException("You haven't specified where to save the zip.");
|
||||
|
||||
if (_name != null && _name.EndsWith(".exe") && !_SavingSfx)
|
||||
throw new BadStateException("You specified an EXE for a plain zip file.");
|
||||
|
||||
// check if modified, before saving.
|
||||
if (!_contentsChanged)
|
||||
{
|
||||
OnSaveCompleted();
|
||||
if (Verbose) StatusMessageTextWriter.WriteLine("No save is necessary....");
|
||||
return;
|
||||
}
|
||||
|
||||
Reset(true);
|
||||
|
||||
if (Verbose) StatusMessageTextWriter.WriteLine("saving....");
|
||||
|
||||
// validate the number of entries
|
||||
if (_entries.Count >= 0xFFFF && _zip64 == Zip64Option.Never)
|
||||
throw new ZipException("The number of entries is 65535 or greater. Consider setting the UseZip64WhenSaving property on the ZipFile instance.");
|
||||
|
||||
|
||||
// write an entry in the zip for each file
|
||||
int n = 0;
|
||||
// workitem 9831
|
||||
ICollection<ZipEntry> c = (SortEntriesBeforeSaving) ? EntriesSorted : Entries;
|
||||
foreach (ZipEntry e in c) // _entries.Values
|
||||
{
|
||||
OnSaveEntry(n, e, true);
|
||||
e.Write(WriteStream);
|
||||
if (_saveOperationCanceled)
|
||||
break;
|
||||
|
||||
n++;
|
||||
OnSaveEntry(n, e, false);
|
||||
if (_saveOperationCanceled)
|
||||
break;
|
||||
|
||||
// Some entries can be skipped during the save.
|
||||
if (e.IncludedInMostRecentSave)
|
||||
thisSaveUsedZip64 |= e.OutputUsedZip64.Value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (_saveOperationCanceled)
|
||||
return;
|
||||
|
||||
var zss = WriteStream as ZipSegmentedStream;
|
||||
|
||||
_numberOfSegmentsForMostRecentSave = (zss!=null)
|
||||
? zss.CurrentSegment
|
||||
: 1;
|
||||
|
||||
bool directoryNeededZip64 =
|
||||
ZipOutput.WriteCentralDirectoryStructure
|
||||
(WriteStream,
|
||||
c,
|
||||
_numberOfSegmentsForMostRecentSave,
|
||||
_zip64,
|
||||
Comment,
|
||||
new ZipContainer(this));
|
||||
|
||||
OnSaveEvent(ZipProgressEventType.Saving_AfterSaveTempArchive);
|
||||
|
||||
_hasBeenSaved = true;
|
||||
_contentsChanged = false;
|
||||
|
||||
thisSaveUsedZip64 |= directoryNeededZip64;
|
||||
_OutputUsesZip64 = new Nullable<bool>(thisSaveUsedZip64);
|
||||
|
||||
|
||||
// do the rename as necessary
|
||||
if (_name != null &&
|
||||
(_temporaryFileName!=null || zss != null))
|
||||
{
|
||||
// _temporaryFileName may remain null if we are writing to a stream.
|
||||
// only close the stream if there is a file behind it.
|
||||
#if NETCF
|
||||
WriteStream.Close();
|
||||
#else
|
||||
WriteStream.Dispose();
|
||||
#endif
|
||||
if (_saveOperationCanceled)
|
||||
return;
|
||||
|
||||
if (_fileAlreadyExists && this._readstream != null)
|
||||
{
|
||||
// This means we opened and read a zip file.
|
||||
// If we are now saving to the same file, we need to close the
|
||||
// orig file, first.
|
||||
this._readstream.Close();
|
||||
this._readstream = null;
|
||||
// the archiveStream for each entry needs to be null
|
||||
foreach (var e in c)
|
||||
{
|
||||
var zss1 = e._archiveStream as ZipSegmentedStream;
|
||||
if (zss1 != null)
|
||||
#if NETCF
|
||||
zss1.Close();
|
||||
#else
|
||||
zss1.Dispose();
|
||||
#endif
|
||||
e._archiveStream = null;
|
||||
}
|
||||
}
|
||||
|
||||
string tmpName = null;
|
||||
if (File.Exists(_name))
|
||||
{
|
||||
// the steps:
|
||||
//
|
||||
// 1. Delete tmpName
|
||||
// 2. move existing zip to tmpName
|
||||
// 3. rename (File.Move) working file to name of existing zip
|
||||
// 4. delete tmpName
|
||||
//
|
||||
// This series of steps avoids the exception,
|
||||
// System.IO.IOException:
|
||||
// "Cannot create a file when that file already exists."
|
||||
//
|
||||
// Cannot just call File.Replace() here because
|
||||
// there is a possibility that the TEMP volume is different
|
||||
// that the volume for the final file (c:\ vs d:\).
|
||||
// So we need to do a Delete+Move pair.
|
||||
//
|
||||
// But, when doing the delete, Windows allows a process to
|
||||
// delete the file, even though it is held open by, say, a
|
||||
// virus scanner. It gets internally marked as "delete
|
||||
// pending". The file does not actually get removed from the
|
||||
// file system, it is still there after the File.Delete
|
||||
// call.
|
||||
//
|
||||
// Therefore, we need to move the existing zip, which may be
|
||||
// held open, to some other name. Then rename our working
|
||||
// file to the desired name, then delete (possibly delete
|
||||
// pending) the "other name".
|
||||
//
|
||||
// Ideally this would be transactional. It's possible that the
|
||||
// delete succeeds and the move fails. Lacking transactions, if
|
||||
// this kind of failure happens, we're hosed, and this logic will
|
||||
// throw on the next File.Move().
|
||||
//
|
||||
//File.Delete(_name);
|
||||
// workitem 10447
|
||||
#if NETCF || SILVERLIGHT
|
||||
tmpName = _name + "." + SharedUtilities.GenerateRandomStringImpl(8,0) + ".tmp";
|
||||
#else
|
||||
tmpName = _name + "." + Path.GetRandomFileName();
|
||||
#endif
|
||||
if (File.Exists(tmpName))
|
||||
DeleteFileWithRetry(tmpName);
|
||||
File.Move(_name, tmpName);
|
||||
}
|
||||
|
||||
OnSaveEvent(ZipProgressEventType.Saving_BeforeRenameTempArchive);
|
||||
File.Move((zss != null) ? zss.CurrentTempName : _temporaryFileName,
|
||||
_name);
|
||||
|
||||
OnSaveEvent(ZipProgressEventType.Saving_AfterRenameTempArchive);
|
||||
|
||||
if (tmpName != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// not critical
|
||||
if (File.Exists(tmpName))
|
||||
File.Delete(tmpName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// don't care about exceptions here.
|
||||
}
|
||||
|
||||
}
|
||||
_fileAlreadyExists = true;
|
||||
}
|
||||
|
||||
NotifyEntriesSaveComplete(c);
|
||||
OnSaveCompleted();
|
||||
_JustSaved = true;
|
||||
}
|
||||
|
||||
// workitem 5043
|
||||
finally
|
||||
{
|
||||
CleanupAfterSaveOperation();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static void NotifyEntriesSaveComplete(ICollection<ZipEntry> c)
|
||||
{
|
||||
foreach (ZipEntry e in c)
|
||||
{
|
||||
e.NotifySaveComplete();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void RemoveTempFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(_temporaryFileName))
|
||||
{
|
||||
File.Delete(_temporaryFileName);
|
||||
}
|
||||
}
|
||||
catch (IOException ex1)
|
||||
{
|
||||
if (Verbose)
|
||||
StatusMessageTextWriter.WriteLine("ZipFile::Save: could not delete temp file: {0}.", ex1.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void CleanupAfterSaveOperation()
|
||||
{
|
||||
if (_name != null)
|
||||
{
|
||||
// close the stream if there is a file behind it.
|
||||
if (_writestream != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// workitem 7704
|
||||
#if NETCF
|
||||
_writestream.Close();
|
||||
#else
|
||||
_writestream.Dispose();
|
||||
#endif
|
||||
}
|
||||
catch (System.IO.IOException) { }
|
||||
}
|
||||
_writestream = null;
|
||||
|
||||
if (_temporaryFileName != null)
|
||||
{
|
||||
RemoveTempFile();
|
||||
_temporaryFileName = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Save the file to a new zipfile, with the given name.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method allows the application to explicitly specify the name of the zip
|
||||
/// file when saving. Use this when creating a new zip file, or when
|
||||
/// updating a zip archive.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// An application can also save a zip archive in several places by calling this
|
||||
/// method multiple times in succession, with different filenames.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The <c>ZipFile</c> instance is written to storage, typically a zip file in a
|
||||
/// filesystem, only when the caller calls <c>Save</c>. The Save operation writes
|
||||
/// the zip content to a temporary file, and then renames the temporary file
|
||||
/// to the desired name. If necessary, this method will delete a pre-existing file
|
||||
/// before the rename.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <exception cref="System.ArgumentException">
|
||||
/// Thrown if you specify a directory for the filename.
|
||||
/// </exception>
|
||||
///
|
||||
/// <param name="fileName">
|
||||
/// The name of the zip archive to save to. Existing files will
|
||||
/// be overwritten with great prejudice.
|
||||
/// </param>
|
||||
///
|
||||
/// <example>
|
||||
/// This example shows how to create and Save a zip file.
|
||||
/// <code>
|
||||
/// using (ZipFile zip = new ZipFile())
|
||||
/// {
|
||||
/// zip.AddDirectory(@"c:\reports\January");
|
||||
/// zip.Save("January.zip");
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Using zip As New ZipFile()
|
||||
/// zip.AddDirectory("c:\reports\January")
|
||||
/// zip.Save("January.zip")
|
||||
/// End Using
|
||||
/// </code>
|
||||
///
|
||||
/// </example>
|
||||
///
|
||||
/// <example>
|
||||
/// This example shows how to update a zip file.
|
||||
/// <code>
|
||||
/// using (ZipFile zip = ZipFile.Read("ExistingArchive.zip"))
|
||||
/// {
|
||||
/// zip.AddFile("NewData.csv");
|
||||
/// zip.Save("UpdatedArchive.zip");
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Using zip As ZipFile = ZipFile.Read("ExistingArchive.zip")
|
||||
/// zip.AddFile("NewData.csv")
|
||||
/// zip.Save("UpdatedArchive.zip")
|
||||
/// End Using
|
||||
/// </code>
|
||||
///
|
||||
/// </example>
|
||||
public void Save(String fileName)
|
||||
{
|
||||
// Check for the case where we are re-saving a zip archive
|
||||
// that was originally instantiated with a stream. In that case,
|
||||
// the _name will be null. If so, we set _writestream to null,
|
||||
// which insures that we'll cons up a new WriteStream (with a filesystem
|
||||
// file backing it) in the Save() method.
|
||||
if (_name == null)
|
||||
_writestream = null;
|
||||
|
||||
else _readName = _name; // workitem 13915
|
||||
|
||||
_name = fileName;
|
||||
if (Directory.Exists(_name))
|
||||
throw new ZipException("Bad Directory", new System.ArgumentException("That name specifies an existing directory. Please specify a filename.", "fileName"));
|
||||
_contentsChanged = true;
|
||||
_fileAlreadyExists = File.Exists(_name);
|
||||
Save();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Save the zip archive to the specified stream.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The <c>ZipFile</c> instance is written to storage - typically a zip file
|
||||
/// in a filesystem, but using this overload, the storage can be anything
|
||||
/// accessible via a writable stream - only when the caller calls <c>Save</c>.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Use this method to save the zip content to a stream directly. A common
|
||||
/// scenario is an ASP.NET application that dynamically generates a zip file
|
||||
/// and allows the browser to download it. The application can call
|
||||
/// <c>Save(Response.OutputStream)</c> to write a zipfile directly to the
|
||||
/// output stream, without creating a zip file on the disk on the ASP.NET
|
||||
/// server.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Be careful when saving a file to a non-seekable stream, including
|
||||
/// <c>Response.OutputStream</c>. When DotNetZip writes to a non-seekable
|
||||
/// stream, the zip archive is formatted in such a way that may not be
|
||||
/// compatible with all zip tools on all platforms. It's a perfectly legal
|
||||
/// and compliant zip file, but some people have reported problems opening
|
||||
/// files produced this way using the Mac OS archive utility.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example saves the zipfile content into a MemoryStream, and
|
||||
/// then gets the array of bytes from that MemoryStream.
|
||||
///
|
||||
/// <code lang="C#">
|
||||
/// using (var zip = new Ionic.Zip.ZipFile())
|
||||
/// {
|
||||
/// zip.CompressionLevel= Ionic.Zlib.CompressionLevel.BestCompression;
|
||||
/// zip.Password = "VerySecret.";
|
||||
/// zip.Encryption = EncryptionAlgorithm.WinZipAes128;
|
||||
/// zip.AddFile(sourceFileName);
|
||||
/// MemoryStream output = new MemoryStream();
|
||||
/// zip.Save(output);
|
||||
///
|
||||
/// byte[] zipbytes = output.ToArray();
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// <example>
|
||||
/// <para>
|
||||
/// This example shows a pitfall you should avoid. DO NOT read
|
||||
/// from a stream, then try to save to the same stream. DO
|
||||
/// NOT DO THIS:
|
||||
/// </para>
|
||||
///
|
||||
/// <code lang="C#">
|
||||
/// using (var fs = new FileSteeam(filename, FileMode.Open))
|
||||
/// {
|
||||
/// using (var zip = Ionic.Zip.ZipFile.Read(inputStream))
|
||||
/// {
|
||||
/// zip.AddEntry("Name1.txt", "this is the content");
|
||||
/// zip.Save(inputStream); // NO NO NO!!
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <para>
|
||||
/// Better like this:
|
||||
/// </para>
|
||||
///
|
||||
/// <code lang="C#">
|
||||
/// using (var zip = Ionic.Zip.ZipFile.Read(filename))
|
||||
/// {
|
||||
/// zip.AddEntry("Name1.txt", "this is the content");
|
||||
/// zip.Save(); // YES!
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// </example>
|
||||
///
|
||||
/// <param name="outputStream">
|
||||
/// The <c>System.IO.Stream</c> to write to. It must be
|
||||
/// writable. If you created the ZipFile instanct by calling
|
||||
/// ZipFile.Read(), this stream must not be the same stream
|
||||
/// you passed to ZipFile.Read().
|
||||
/// </param>
|
||||
public void Save(Stream outputStream)
|
||||
{
|
||||
if (outputStream == null)
|
||||
throw new ArgumentNullException("outputStream");
|
||||
if (!outputStream.CanWrite)
|
||||
throw new ArgumentException("Must be a writable stream.", "outputStream");
|
||||
|
||||
// if we had a filename to save to, we are now obliterating it.
|
||||
_name = null;
|
||||
|
||||
_writestream = new CountingStream(outputStream);
|
||||
|
||||
_contentsChanged = true;
|
||||
_fileAlreadyExists = false;
|
||||
Save();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal static class ZipOutput
|
||||
{
|
||||
public static bool WriteCentralDirectoryStructure(Stream s,
|
||||
ICollection<ZipEntry> entries,
|
||||
uint numSegments,
|
||||
Zip64Option zip64,
|
||||
String comment,
|
||||
ZipContainer container)
|
||||
{
|
||||
var zss = s as ZipSegmentedStream;
|
||||
if (zss != null)
|
||||
zss.ContiguousWrite = true;
|
||||
|
||||
// write to a memory stream in order to keep the
|
||||
// CDR contiguous
|
||||
Int64 aLength = 0;
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
foreach (ZipEntry e in entries)
|
||||
{
|
||||
if (e.IncludedInMostRecentSave)
|
||||
{
|
||||
// this writes a ZipDirEntry corresponding to the ZipEntry
|
||||
e.WriteCentralDirectoryEntry(ms);
|
||||
}
|
||||
}
|
||||
var a = ms.ToArray();
|
||||
s.Write(a, 0, a.Length);
|
||||
aLength = a.Length;
|
||||
}
|
||||
|
||||
|
||||
// We need to keep track of the start and
|
||||
// Finish of the Central Directory Structure.
|
||||
|
||||
// Cannot always use WriteStream.Length or Position; some streams do
|
||||
// not support these. (eg, ASP.NET Response.OutputStream) In those
|
||||
// cases we have a CountingStream.
|
||||
|
||||
// Also, we cannot just set Start as s.Position bfore the write, and Finish
|
||||
// as s.Position after the write. In a split zip, the write may actually
|
||||
// flip to the next segment. In that case, Start will be zero. But we
|
||||
// don't know that til after we know the size of the thing to write. So the
|
||||
// answer is to compute the directory, then ask the ZipSegmentedStream which
|
||||
// segment that directory would fall in, it it were written. Then, include
|
||||
// that data into the directory, and finally, write the directory to the
|
||||
// output stream.
|
||||
|
||||
var output = s as CountingStream;
|
||||
long Finish = (output != null) ? output.ComputedPosition : s.Position; // BytesWritten
|
||||
long Start = Finish - aLength;
|
||||
|
||||
// need to know which segment the EOCD record starts in
|
||||
UInt32 startSegment = (zss != null)
|
||||
? zss.CurrentSegment
|
||||
: 0;
|
||||
|
||||
Int64 SizeOfCentralDirectory = Finish - Start;
|
||||
|
||||
int countOfEntries = CountEntries(entries);
|
||||
|
||||
bool needZip64CentralDirectory =
|
||||
zip64 == Zip64Option.Always ||
|
||||
countOfEntries >= 0xFFFF ||
|
||||
SizeOfCentralDirectory > 0xFFFFFFFF ||
|
||||
Start > 0xFFFFFFFF;
|
||||
|
||||
byte[] a2 = null;
|
||||
|
||||
// emit ZIP64 extensions as required
|
||||
if (needZip64CentralDirectory)
|
||||
{
|
||||
if (zip64 == Zip64Option.Never)
|
||||
{
|
||||
#if NETCF
|
||||
throw new ZipException("The archive requires a ZIP64 Central Directory. Consider enabling ZIP64 extensions.");
|
||||
#else
|
||||
System.Diagnostics.StackFrame sf = new System.Diagnostics.StackFrame(1);
|
||||
if (sf.GetMethod().DeclaringType == typeof(ZipFile))
|
||||
throw new ZipException("The archive requires a ZIP64 Central Directory. Consider setting the ZipFile.UseZip64WhenSaving property.");
|
||||
else
|
||||
throw new ZipException("The archive requires a ZIP64 Central Directory. Consider setting the ZipOutputStream.EnableZip64 property.");
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
var a = GenZip64EndOfCentralDirectory(Start, Finish, countOfEntries, numSegments);
|
||||
a2 = GenCentralDirectoryFooter(Start, Finish, zip64, countOfEntries, comment, container);
|
||||
if (startSegment != 0)
|
||||
{
|
||||
UInt32 thisSegment = zss.ComputeSegment(a.Length + a2.Length);
|
||||
int i = 16;
|
||||
// number of this disk
|
||||
Array.Copy(BitConverter.GetBytes(thisSegment), 0, a, i, 4);
|
||||
i += 4;
|
||||
// number of the disk with the start of the central directory
|
||||
//Array.Copy(BitConverter.GetBytes(startSegment), 0, a, i, 4);
|
||||
Array.Copy(BitConverter.GetBytes(thisSegment), 0, a, i, 4);
|
||||
|
||||
i = 60;
|
||||
// offset 60
|
||||
// number of the disk with the start of the zip64 eocd
|
||||
Array.Copy(BitConverter.GetBytes(thisSegment), 0, a, i, 4);
|
||||
i += 4;
|
||||
i += 8;
|
||||
|
||||
// offset 72
|
||||
// total number of disks
|
||||
Array.Copy(BitConverter.GetBytes(thisSegment), 0, a, i, 4);
|
||||
}
|
||||
s.Write(a, 0, a.Length);
|
||||
}
|
||||
else
|
||||
a2 = GenCentralDirectoryFooter(Start, Finish, zip64, countOfEntries, comment, container);
|
||||
|
||||
|
||||
// now, the regular footer
|
||||
if (startSegment != 0)
|
||||
{
|
||||
// The assumption is the central directory is never split across
|
||||
// segment boundaries.
|
||||
|
||||
UInt16 thisSegment = (UInt16) zss.ComputeSegment(a2.Length);
|
||||
int i = 4;
|
||||
// number of this disk
|
||||
Array.Copy(BitConverter.GetBytes(thisSegment), 0, a2, i, 2);
|
||||
i += 2;
|
||||
// number of the disk with the start of the central directory
|
||||
//Array.Copy(BitConverter.GetBytes((UInt16)startSegment), 0, a2, i, 2);
|
||||
Array.Copy(BitConverter.GetBytes(thisSegment), 0, a2, i, 2);
|
||||
i += 2;
|
||||
}
|
||||
|
||||
s.Write(a2, 0, a2.Length);
|
||||
|
||||
// reset the contiguous write property if necessary
|
||||
if (zss != null)
|
||||
zss.ContiguousWrite = false;
|
||||
|
||||
return needZip64CentralDirectory;
|
||||
}
|
||||
|
||||
|
||||
private static System.Text.Encoding GetEncoding(ZipContainer container, string t)
|
||||
{
|
||||
switch (container.AlternateEncodingUsage)
|
||||
{
|
||||
case ZipOption.Always:
|
||||
return container.AlternateEncoding;
|
||||
case ZipOption.Never:
|
||||
return container.DefaultEncoding;
|
||||
}
|
||||
|
||||
// AsNecessary is in force
|
||||
var e = container.DefaultEncoding;
|
||||
if (t == null) return e;
|
||||
|
||||
var bytes = e.GetBytes(t);
|
||||
var t2 = e.GetString(bytes,0,bytes.Length);
|
||||
if (t2.Equals(t)) return e;
|
||||
return container.AlternateEncoding;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static byte[] GenCentralDirectoryFooter(long StartOfCentralDirectory,
|
||||
long EndOfCentralDirectory,
|
||||
Zip64Option zip64,
|
||||
int entryCount,
|
||||
string comment,
|
||||
ZipContainer container)
|
||||
{
|
||||
System.Text.Encoding encoding = GetEncoding(container, comment);
|
||||
int j = 0;
|
||||
int bufferLength = 22;
|
||||
byte[] block = null;
|
||||
Int16 commentLength = 0;
|
||||
if ((comment != null) && (comment.Length != 0))
|
||||
{
|
||||
block = encoding.GetBytes(comment);
|
||||
commentLength = (Int16)block.Length;
|
||||
}
|
||||
bufferLength += commentLength;
|
||||
byte[] bytes = new byte[bufferLength];
|
||||
|
||||
int i = 0;
|
||||
// signature
|
||||
byte[] sig = BitConverter.GetBytes(ZipConstants.EndOfCentralDirectorySignature);
|
||||
Array.Copy(sig, 0, bytes, i, 4);
|
||||
i+=4;
|
||||
|
||||
// number of this disk
|
||||
// (this number may change later)
|
||||
bytes[i++] = 0;
|
||||
bytes[i++] = 0;
|
||||
|
||||
// number of the disk with the start of the central directory
|
||||
// (this number may change later)
|
||||
bytes[i++] = 0;
|
||||
bytes[i++] = 0;
|
||||
|
||||
// handle ZIP64 extensions for the end-of-central-directory
|
||||
if (entryCount >= 0xFFFF || zip64 == Zip64Option.Always)
|
||||
{
|
||||
// the ZIP64 version.
|
||||
for (j = 0; j < 4; j++)
|
||||
bytes[i++] = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the standard version.
|
||||
// total number of entries in the central dir on this disk
|
||||
bytes[i++] = (byte)(entryCount & 0x00FF);
|
||||
bytes[i++] = (byte)((entryCount & 0xFF00) >> 8);
|
||||
|
||||
// total number of entries in the central directory
|
||||
bytes[i++] = (byte)(entryCount & 0x00FF);
|
||||
bytes[i++] = (byte)((entryCount & 0xFF00) >> 8);
|
||||
}
|
||||
|
||||
// size of the central directory
|
||||
Int64 SizeOfCentralDirectory = EndOfCentralDirectory - StartOfCentralDirectory;
|
||||
|
||||
if (SizeOfCentralDirectory >= 0xFFFFFFFF || StartOfCentralDirectory >= 0xFFFFFFFF)
|
||||
{
|
||||
// The actual data is in the ZIP64 central directory structure
|
||||
for (j = 0; j < 8; j++)
|
||||
bytes[i++] = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
// size of the central directory (we just get the low 4 bytes)
|
||||
bytes[i++] = (byte)(SizeOfCentralDirectory & 0x000000FF);
|
||||
bytes[i++] = (byte)((SizeOfCentralDirectory & 0x0000FF00) >> 8);
|
||||
bytes[i++] = (byte)((SizeOfCentralDirectory & 0x00FF0000) >> 16);
|
||||
bytes[i++] = (byte)((SizeOfCentralDirectory & 0xFF000000) >> 24);
|
||||
|
||||
// offset of the start of the central directory (we just get the low 4 bytes)
|
||||
bytes[i++] = (byte)(StartOfCentralDirectory & 0x000000FF);
|
||||
bytes[i++] = (byte)((StartOfCentralDirectory & 0x0000FF00) >> 8);
|
||||
bytes[i++] = (byte)((StartOfCentralDirectory & 0x00FF0000) >> 16);
|
||||
bytes[i++] = (byte)((StartOfCentralDirectory & 0xFF000000) >> 24);
|
||||
}
|
||||
|
||||
|
||||
// zip archive comment
|
||||
if ((comment == null) || (comment.Length == 0))
|
||||
{
|
||||
// no comment!
|
||||
bytes[i++] = (byte)0;
|
||||
bytes[i++] = (byte)0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the size of our buffer defines the max length of the comment we can write
|
||||
if (commentLength + i + 2 > bytes.Length) commentLength = (Int16)(bytes.Length - i - 2);
|
||||
bytes[i++] = (byte)(commentLength & 0x00FF);
|
||||
bytes[i++] = (byte)((commentLength & 0xFF00) >> 8);
|
||||
|
||||
if (commentLength != 0)
|
||||
{
|
||||
// now actually write the comment itself into the byte buffer
|
||||
for (j = 0; (j < commentLength) && (i + j < bytes.Length); j++)
|
||||
{
|
||||
bytes[i + j] = block[j];
|
||||
}
|
||||
i += j;
|
||||
}
|
||||
}
|
||||
|
||||
// s.Write(bytes, 0, i);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static byte[] GenZip64EndOfCentralDirectory(long StartOfCentralDirectory,
|
||||
long EndOfCentralDirectory,
|
||||
int entryCount,
|
||||
uint numSegments)
|
||||
{
|
||||
const int bufferLength = 12 + 44 + 20;
|
||||
|
||||
byte[] bytes = new byte[bufferLength];
|
||||
|
||||
int i = 0;
|
||||
// signature
|
||||
byte[] sig = BitConverter.GetBytes(ZipConstants.Zip64EndOfCentralDirectoryRecordSignature);
|
||||
Array.Copy(sig, 0, bytes, i, 4);
|
||||
i+=4;
|
||||
|
||||
// There is a possibility to include "Extensible" data in the zip64
|
||||
// end-of-central-dir record. I cannot figure out what it might be used to
|
||||
// store, so the size of this record is always fixed. Maybe it is used for
|
||||
// strong encryption data? That is for another day.
|
||||
long DataSize = 44;
|
||||
Array.Copy(BitConverter.GetBytes(DataSize), 0, bytes, i, 8);
|
||||
i += 8;
|
||||
|
||||
// offset 12
|
||||
// VersionMadeBy = 45;
|
||||
bytes[i++] = 45;
|
||||
bytes[i++] = 0x00;
|
||||
|
||||
// VersionNeededToExtract = 45;
|
||||
bytes[i++] = 45;
|
||||
bytes[i++] = 0x00;
|
||||
|
||||
// offset 16
|
||||
// number of the disk, and the disk with the start of the central dir.
|
||||
// (this may change later)
|
||||
for (int j = 0; j < 8; j++)
|
||||
bytes[i++] = 0x00;
|
||||
|
||||
// offset 24
|
||||
long numberOfEntries = entryCount;
|
||||
Array.Copy(BitConverter.GetBytes(numberOfEntries), 0, bytes, i, 8);
|
||||
i += 8;
|
||||
Array.Copy(BitConverter.GetBytes(numberOfEntries), 0, bytes, i, 8);
|
||||
i += 8;
|
||||
|
||||
// offset 40
|
||||
Int64 SizeofCentraldirectory = EndOfCentralDirectory - StartOfCentralDirectory;
|
||||
Array.Copy(BitConverter.GetBytes(SizeofCentraldirectory), 0, bytes, i, 8);
|
||||
i += 8;
|
||||
Array.Copy(BitConverter.GetBytes(StartOfCentralDirectory), 0, bytes, i, 8);
|
||||
i += 8;
|
||||
|
||||
// offset 56
|
||||
// now, the locator
|
||||
// signature
|
||||
sig = BitConverter.GetBytes(ZipConstants.Zip64EndOfCentralDirectoryLocatorSignature);
|
||||
Array.Copy(sig, 0, bytes, i, 4);
|
||||
i+=4;
|
||||
|
||||
// offset 60
|
||||
// number of the disk with the start of the zip64 eocd
|
||||
// (this will change later) (it will?)
|
||||
uint x2 = (numSegments==0)?0:(uint)(numSegments-1);
|
||||
Array.Copy(BitConverter.GetBytes(x2), 0, bytes, i, 4);
|
||||
i+=4;
|
||||
|
||||
// offset 64
|
||||
// relative offset of the zip64 eocd
|
||||
Array.Copy(BitConverter.GetBytes(EndOfCentralDirectory), 0, bytes, i, 8);
|
||||
i += 8;
|
||||
|
||||
// offset 72
|
||||
// total number of disks
|
||||
// (this will change later)
|
||||
Array.Copy(BitConverter.GetBytes(numSegments), 0, bytes, i, 4);
|
||||
i+=4;
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static int CountEntries(ICollection<ZipEntry> _entries)
|
||||
{
|
||||
// Cannot just emit _entries.Count, because some of the entries
|
||||
// may have been skipped.
|
||||
int count = 0;
|
||||
foreach (var entry in _entries)
|
||||
if (entry.IncludedInMostRecentSave) count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,154 @@
|
||||
// ZipFile.x-IEnumerable.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2006, 2007, 2008, 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-December-26 15:13:26>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines smoe methods for IEnumerable support. It is
|
||||
// particularly important for COM to have these things in a separate module.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
|
||||
// For some weird reason, the method with the DispId(-4) attribute, which is used as
|
||||
// the _NewEnum() method, and which is required to get enumeration to work from COM
|
||||
// environments like VBScript and Javascript (etc) must be the LAST MEMBER in the
|
||||
// source. In the event of Partial classes, it needs to be the last member defined
|
||||
// in the last source module. The source modules are ordered alphabetically by
|
||||
// filename. Not sure why this is true. In any case, we put the enumeration stuff
|
||||
// here in this oddly-named module, for this reason.
|
||||
//
|
||||
|
||||
|
||||
|
||||
public partial class ZipFile
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Generic IEnumerator support, for use of a ZipFile in an enumeration.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// You probably do not want to call <c>GetEnumerator</c> explicitly. Instead
|
||||
/// it is implicitly called when you use a <see langword="foreach"/> loop in C#, or a
|
||||
/// <c>For Each</c> loop in VB.NET.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
/// This example reads a zipfile of a given name, then enumerates the
|
||||
/// entries in that zip file, and displays the information about each
|
||||
/// entry on the Console.
|
||||
/// <code>
|
||||
/// using (ZipFile zip = ZipFile.Read(zipfile))
|
||||
/// {
|
||||
/// bool header = true;
|
||||
/// foreach (ZipEntry e in zip)
|
||||
/// {
|
||||
/// if (header)
|
||||
/// {
|
||||
/// System.Console.WriteLine("Zipfile: {0}", zip.Name);
|
||||
/// System.Console.WriteLine("Version Needed: 0x{0:X2}", e.VersionNeeded);
|
||||
/// System.Console.WriteLine("BitField: 0x{0:X2}", e.BitField);
|
||||
/// System.Console.WriteLine("Compression Method: 0x{0:X2}", e.CompressionMethod);
|
||||
/// System.Console.WriteLine("\n{1,-22} {2,-6} {3,4} {4,-8} {0}",
|
||||
/// "Filename", "Modified", "Size", "Ratio", "Packed");
|
||||
/// System.Console.WriteLine(new System.String('-', 72));
|
||||
/// header = false;
|
||||
/// }
|
||||
///
|
||||
/// System.Console.WriteLine("{1,-22} {2,-6} {3,4:F0}% {4,-8} {0}",
|
||||
/// e.FileName,
|
||||
/// e.LastModified.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
/// e.UncompressedSize,
|
||||
/// e.CompressionRatio,
|
||||
/// e.CompressedSize);
|
||||
///
|
||||
/// e.Extract();
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Dim ZipFileToExtract As String = "c:\foo.zip"
|
||||
/// Using zip As ZipFile = ZipFile.Read(ZipFileToExtract)
|
||||
/// Dim header As Boolean = True
|
||||
/// Dim e As ZipEntry
|
||||
/// For Each e In zip
|
||||
/// If header Then
|
||||
/// Console.WriteLine("Zipfile: {0}", zip.Name)
|
||||
/// Console.WriteLine("Version Needed: 0x{0:X2}", e.VersionNeeded)
|
||||
/// Console.WriteLine("BitField: 0x{0:X2}", e.BitField)
|
||||
/// Console.WriteLine("Compression Method: 0x{0:X2}", e.CompressionMethod)
|
||||
/// Console.WriteLine(ChrW(10) & "{1,-22} {2,-6} {3,4} {4,-8} {0}", _
|
||||
/// "Filename", "Modified", "Size", "Ratio", "Packed" )
|
||||
/// Console.WriteLine(New String("-"c, 72))
|
||||
/// header = False
|
||||
/// End If
|
||||
/// Console.WriteLine("{1,-22} {2,-6} {3,4:F0}% {4,-8} {0}", _
|
||||
/// e.FileName, _
|
||||
/// e.LastModified.ToString("yyyy-MM-dd HH:mm:ss"), _
|
||||
/// e.UncompressedSize, _
|
||||
/// e.CompressionRatio, _
|
||||
/// e.CompressedSize )
|
||||
/// e.Extract
|
||||
/// Next
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// <returns>A generic enumerator suitable for use within a foreach loop.</returns>
|
||||
public System.Collections.Generic.IEnumerator<ZipEntry> GetEnumerator()
|
||||
{
|
||||
foreach (ZipEntry e in _entries.Values)
|
||||
yield return e;
|
||||
}
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An IEnumerator, for use of a ZipFile in a foreach construct.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// This method is included for COM support. An application generally does not call
|
||||
/// this method directly. It is called implicitly by COM clients when enumerating
|
||||
/// the entries in the ZipFile instance. In VBScript, this is done with a <c>For Each</c>
|
||||
/// statement. In Javascript, this is done with <c>new Enumerator(zipfile)</c>.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <returns>
|
||||
/// The IEnumerator over the entries in the ZipFile.
|
||||
/// </returns>
|
||||
[System.Runtime.InteropServices.DispId(-4)]
|
||||
public System.Collections.IEnumerator GetNewEnum() // the name of this method is not significant
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,827 @@
|
||||
// ZipInputStream.cs
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2010 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-31 14:48:30>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the ZipInputStream class, which is a stream metaphor for
|
||||
// reading zip files. This class does not depend on Ionic.Zip.ZipFile, but rather
|
||||
// stands alongside it as an alternative "container" for ZipEntry, when reading zips.
|
||||
//
|
||||
// It adds one interesting method to the normal "stream" interface: GetNextEntry.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Ionic.Zip;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a stream metaphor for reading zip files.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This class provides an alternative programming model for reading zip files to
|
||||
/// the one enabled by the <see cref="ZipFile"/> class. Use this when reading zip
|
||||
/// files, as an alternative to the <see cref="ZipFile"/> class, when you would
|
||||
/// like to use a Stream class to read the file.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Some application designs require a readable stream for input. This stream can
|
||||
/// be used to read a zip file, and extract entries.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Both the <c>ZipInputStream</c> class and the <c>ZipFile</c> class can be used
|
||||
/// to read and extract zip files. Both of them support many of the common zip
|
||||
/// features, including Unicode, different compression levels, and ZIP64. The
|
||||
/// programming models differ. For example, when extracting entries via calls to
|
||||
/// the <c>GetNextEntry()</c> and <c>Read()</c> methods on the
|
||||
/// <c>ZipInputStream</c> class, the caller is responsible for creating the file,
|
||||
/// writing the bytes into the file, setting the attributes on the file, and
|
||||
/// setting the created, last modified, and last accessed timestamps on the
|
||||
/// file. All of these things are done automatically by a call to <see
|
||||
/// cref="ZipEntry.Extract()">ZipEntry.Extract()</see>. For this reason, the
|
||||
/// <c>ZipInputStream</c> is generally recommended for when your application wants
|
||||
/// to extract the data, without storing that data into a file.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Aside from the obvious differences in programming model, there are some
|
||||
/// differences in capability between the <c>ZipFile</c> class and the
|
||||
/// <c>ZipInputStream</c> class.
|
||||
/// </para>
|
||||
///
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <c>ZipFile</c> can be used to create or update zip files, or read and
|
||||
/// extract zip files. <c>ZipInputStream</c> can be used only to read and
|
||||
/// extract zip files. If you want to use a stream to create zip files, check
|
||||
/// out the <see cref="ZipOutputStream"/>.
|
||||
/// </item>
|
||||
///
|
||||
/// <item>
|
||||
/// <c>ZipInputStream</c> cannot read segmented or spanned
|
||||
/// zip files.
|
||||
/// </item>
|
||||
///
|
||||
/// <item>
|
||||
/// <c>ZipInputStream</c> will not read Zip file comments.
|
||||
/// </item>
|
||||
///
|
||||
/// <item>
|
||||
/// When reading larger files, <c>ZipInputStream</c> will always underperform
|
||||
/// <c>ZipFile</c>. This is because the <c>ZipInputStream</c> does a full scan on the
|
||||
/// zip file, while the <c>ZipFile</c> class reads the central directory of the
|
||||
/// zip file.
|
||||
/// </item>
|
||||
///
|
||||
/// </list>
|
||||
///
|
||||
/// </remarks>
|
||||
public class ZipInputStream : Stream
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a <c>ZipInputStream</c>, wrapping it around an existing stream.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// While the <see cref="ZipFile"/> class is generally easier
|
||||
/// to use, this class provides an alternative to those
|
||||
/// applications that want to read from a zipfile directly,
|
||||
/// using a <see cref="System.IO.Stream"/>.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Both the <c>ZipInputStream</c> class and the <c>ZipFile</c> class can be used
|
||||
/// to read and extract zip files. Both of them support many of the common zip
|
||||
/// features, including Unicode, different compression levels, and ZIP64. The
|
||||
/// programming models differ. For example, when extracting entries via calls to
|
||||
/// the <c>GetNextEntry()</c> and <c>Read()</c> methods on the
|
||||
/// <c>ZipInputStream</c> class, the caller is responsible for creating the file,
|
||||
/// writing the bytes into the file, setting the attributes on the file, and
|
||||
/// setting the created, last modified, and last accessed timestamps on the
|
||||
/// file. All of these things are done automatically by a call to <see
|
||||
/// cref="ZipEntry.Extract()">ZipEntry.Extract()</see>. For this reason, the
|
||||
/// <c>ZipInputStream</c> is generally recommended for when your application wants
|
||||
/// to extract the data, without storing that data into a file.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Aside from the obvious differences in programming model, there are some
|
||||
/// differences in capability between the <c>ZipFile</c> class and the
|
||||
/// <c>ZipInputStream</c> class.
|
||||
/// </para>
|
||||
///
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <c>ZipFile</c> can be used to create or update zip files, or read and extract
|
||||
/// zip files. <c>ZipInputStream</c> can be used only to read and extract zip
|
||||
/// files. If you want to use a stream to create zip files, check out the <see
|
||||
/// cref="ZipOutputStream"/>.
|
||||
/// </item>
|
||||
///
|
||||
/// <item>
|
||||
/// <c>ZipInputStream</c> cannot read segmented or spanned
|
||||
/// zip files.
|
||||
/// </item>
|
||||
///
|
||||
/// <item>
|
||||
/// <c>ZipInputStream</c> will not read Zip file comments.
|
||||
/// </item>
|
||||
///
|
||||
/// <item>
|
||||
/// When reading larger files, <c>ZipInputStream</c> will always underperform
|
||||
/// <c>ZipFile</c>. This is because the <c>ZipInputStream</c> does a full scan on the
|
||||
/// zip file, while the <c>ZipFile</c> class reads the central directory of the
|
||||
/// zip file.
|
||||
/// </item>
|
||||
///
|
||||
/// </list>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="stream">
|
||||
/// The stream to read. It must be readable. This stream will be closed at
|
||||
/// the time the <c>ZipInputStream</c> is closed.
|
||||
/// </param>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example shows how to read a zip file, and extract entries, using the
|
||||
/// <c>ZipInputStream</c> class.
|
||||
///
|
||||
/// <code lang="C#">
|
||||
/// private void Unzip()
|
||||
/// {
|
||||
/// byte[] buffer= new byte[2048];
|
||||
/// int n;
|
||||
/// using (var raw = File.Open(inputFileName, FileMode.Open, FileAccess.Read))
|
||||
/// {
|
||||
/// using (var input= new ZipInputStream(raw))
|
||||
/// {
|
||||
/// ZipEntry e;
|
||||
/// while (( e = input.GetNextEntry()) != null)
|
||||
/// {
|
||||
/// if (e.IsDirectory) continue;
|
||||
/// string outputPath = Path.Combine(extractDir, e.FileName);
|
||||
/// using (var output = File.Open(outputPath, FileMode.Create, FileAccess.ReadWrite))
|
||||
/// {
|
||||
/// while ((n= input.Read(buffer, 0, buffer.Length)) > 0)
|
||||
/// {
|
||||
/// output.Write(buffer,0,n);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Private Sub UnZip()
|
||||
/// Dim inputFileName As String = "MyArchive.zip"
|
||||
/// Dim extractDir As String = "extract"
|
||||
/// Dim buffer As Byte() = New Byte(2048) {}
|
||||
/// Using raw As FileStream = File.Open(inputFileName, FileMode.Open, FileAccess.Read)
|
||||
/// Using input As ZipInputStream = New ZipInputStream(raw)
|
||||
/// Dim e As ZipEntry
|
||||
/// Do While (Not e = input.GetNextEntry Is Nothing)
|
||||
/// If Not e.IsDirectory Then
|
||||
/// Using output As FileStream = File.Open(Path.Combine(extractDir, e.FileName), _
|
||||
/// FileMode.Create, FileAccess.ReadWrite)
|
||||
/// Dim n As Integer
|
||||
/// Do While (n = input.Read(buffer, 0, buffer.Length) > 0)
|
||||
/// output.Write(buffer, 0, n)
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End If
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// End Sub
|
||||
/// </code>
|
||||
/// </example>
|
||||
public ZipInputStream(Stream stream) : this (stream, false) { }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a <c>ZipInputStream</c>, given the name of an existing zip file.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// This constructor opens a <c>FileStream</c> for the given zipfile, and
|
||||
/// wraps a <c>ZipInputStream</c> around that. See the documentation for the
|
||||
/// <see cref="ZipInputStream(Stream)"/> constructor for full details.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// While the <see cref="ZipFile"/> class is generally easier
|
||||
/// to use, this class provides an alternative to those
|
||||
/// applications that want to read from a zipfile directly,
|
||||
/// using a <see cref="System.IO.Stream"/>.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="fileName">
|
||||
/// The name of the filesystem file to read.
|
||||
/// </param>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example shows how to read a zip file, and extract entries, using the
|
||||
/// <c>ZipInputStream</c> class.
|
||||
///
|
||||
/// <code lang="C#">
|
||||
/// private void Unzip()
|
||||
/// {
|
||||
/// byte[] buffer= new byte[2048];
|
||||
/// int n;
|
||||
/// using (var input= new ZipInputStream(inputFileName))
|
||||
/// {
|
||||
/// ZipEntry e;
|
||||
/// while (( e = input.GetNextEntry()) != null)
|
||||
/// {
|
||||
/// if (e.IsDirectory) continue;
|
||||
/// string outputPath = Path.Combine(extractDir, e.FileName);
|
||||
/// using (var output = File.Open(outputPath, FileMode.Create, FileAccess.ReadWrite))
|
||||
/// {
|
||||
/// while ((n= input.Read(buffer, 0, buffer.Length)) > 0)
|
||||
/// {
|
||||
/// output.Write(buffer,0,n);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Private Sub UnZip()
|
||||
/// Dim inputFileName As String = "MyArchive.zip"
|
||||
/// Dim extractDir As String = "extract"
|
||||
/// Dim buffer As Byte() = New Byte(2048) {}
|
||||
/// Using input As ZipInputStream = New ZipInputStream(inputFileName)
|
||||
/// Dim e As ZipEntry
|
||||
/// Do While (Not e = input.GetNextEntry Is Nothing)
|
||||
/// If Not e.IsDirectory Then
|
||||
/// Using output As FileStream = File.Open(Path.Combine(extractDir, e.FileName), _
|
||||
/// FileMode.Create, FileAccess.ReadWrite)
|
||||
/// Dim n As Integer
|
||||
/// Do While (n = input.Read(buffer, 0, buffer.Length) > 0)
|
||||
/// output.Write(buffer, 0, n)
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End If
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End Sub
|
||||
/// </code>
|
||||
/// </example>
|
||||
public ZipInputStream(String fileName)
|
||||
{
|
||||
Stream stream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read );
|
||||
_Init(stream, false, fileName);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a <c>ZipInputStream</c>, explicitly specifying whether to
|
||||
/// keep the underlying stream open.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// See the documentation for the <see
|
||||
/// cref="ZipInputStream(Stream)">ZipInputStream(Stream)</see>
|
||||
/// constructor for a discussion of the class, and an example of how to use the class.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="stream">
|
||||
/// The stream to read from. It must be readable.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="leaveOpen">
|
||||
/// true if the application would like the stream
|
||||
/// to remain open after the <c>ZipInputStream</c> has been closed.
|
||||
/// </param>
|
||||
public ZipInputStream(Stream stream, bool leaveOpen)
|
||||
{
|
||||
_Init(stream, leaveOpen, null);
|
||||
}
|
||||
|
||||
private void _Init(Stream stream, bool leaveOpen, string name)
|
||||
{
|
||||
_inputStream = stream;
|
||||
if (!_inputStream.CanRead)
|
||||
throw new ZipException("The stream must be readable.");
|
||||
_container= new ZipContainer(this);
|
||||
_provisionalAlternateEncoding = System.Text.Encoding.GetEncoding("IBM437");
|
||||
_leaveUnderlyingStreamOpen = leaveOpen;
|
||||
_findRequired= true;
|
||||
_name = name ?? "(stream)";
|
||||
}
|
||||
|
||||
|
||||
/// <summary>Provides a string representation of the instance.</summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This can be useful for debugging purposes.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <returns>a string representation of the instance.</returns>
|
||||
public override String ToString()
|
||||
{
|
||||
return String.Format ("ZipInputStream::{0}(leaveOpen({1})))", _name, _leaveUnderlyingStreamOpen);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The text encoding to use when reading entries into the zip archive, for
|
||||
/// those entries whose filenames or comments cannot be encoded with the
|
||||
/// default (IBM437) encoding.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// In <see href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">its
|
||||
/// zip specification</see>, PKWare describes two options for encoding
|
||||
/// filenames and comments: using IBM437 or UTF-8. But, some archiving tools
|
||||
/// or libraries do not follow the specification, and instead encode
|
||||
/// characters using the system default code page. For example, WinRAR when
|
||||
/// run on a machine in Shanghai may encode filenames with the Big-5 Chinese
|
||||
/// (950) code page. This behavior is contrary to the Zip specification, but
|
||||
/// it occurs anyway.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// When using DotNetZip to read zip archives that use something other than
|
||||
/// UTF-8 or IBM437, set this property to specify the code page to use when
|
||||
/// reading encoded filenames and comments for each <c>ZipEntry</c> in the zip
|
||||
/// file.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This property is "provisional". When the entry in the zip archive is not
|
||||
/// explicitly marked as using UTF-8, then IBM437 is used to decode filenames
|
||||
/// and comments. If a loss of data would result from using IBM436 -
|
||||
/// specifically when encoding and decoding is not reflexive - the codepage
|
||||
/// specified here is used. It is possible, therefore, to have a given entry
|
||||
/// with a <c>Comment</c> encoded in IBM437 and a <c>FileName</c> encoded with
|
||||
/// the specified "provisional" codepage.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// When a zip file uses an arbitrary, non-UTF8 code page for encoding, there
|
||||
/// is no standard way for the reader application - whether DotNetZip, WinZip,
|
||||
/// WinRar, or something else - to know which codepage has been used for the
|
||||
/// entries. Readers of zip files are not able to inspect the zip file and
|
||||
/// determine the codepage that was used for the entries contained within it.
|
||||
/// It is left to the application or user to determine the necessary codepage
|
||||
/// when reading zip files encoded this way. If you use an incorrect codepage
|
||||
/// when reading a zipfile, you will get entries with filenames that are
|
||||
/// incorrect, and the incorrect filenames may even contain characters that
|
||||
/// are not legal for use within filenames in Windows. Extracting entries with
|
||||
/// illegal characters in the filenames will lead to exceptions. It's too bad,
|
||||
/// but this is just the way things are with code pages in zip files. Caveat
|
||||
/// Emptor.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
public System.Text.Encoding ProvisionalAlternateEncoding
|
||||
{
|
||||
get
|
||||
{
|
||||
return _provisionalAlternateEncoding;
|
||||
}
|
||||
set
|
||||
{
|
||||
_provisionalAlternateEncoding = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Size of the work buffer to use for the ZLIB codec during decompression.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Setting this affects the performance and memory efficiency of compression
|
||||
/// and decompression. For larger files, setting this to a larger size may
|
||||
/// improve performance, but the exact numbers vary depending on available
|
||||
/// memory, and a bunch of other variables. I don't have good firm
|
||||
/// recommendations on how to set it. You'll have to test it yourself. Or
|
||||
/// just leave it alone and accept the default.
|
||||
/// </remarks>
|
||||
public int CodecBufferSize
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the password to be used on the <c>ZipInputStream</c> instance.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// When reading a zip archive, this password is used to read and decrypt the
|
||||
/// entries that are encrypted within the zip file. When entries within a zip
|
||||
/// file use different passwords, set the appropriate password for the entry
|
||||
/// before the first call to <c>Read()</c> for each entry.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// When reading an entry that is not encrypted, the value of this property is
|
||||
/// ignored.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example uses the ZipInputStream to read and extract entries from a
|
||||
/// zip file, using a potentially different password for each entry.
|
||||
///
|
||||
/// <code lang="C#">
|
||||
/// byte[] buffer= new byte[2048];
|
||||
/// int n;
|
||||
/// using (var raw = File.Open(_inputFileName, FileMode.Open, FileAccess.Read ))
|
||||
/// {
|
||||
/// using (var input= new ZipInputStream(raw))
|
||||
/// {
|
||||
/// ZipEntry e;
|
||||
/// while (( e = input.GetNextEntry()) != null)
|
||||
/// {
|
||||
/// input.Password = PasswordForEntry(e.FileName);
|
||||
/// if (e.IsDirectory) continue;
|
||||
/// string outputPath = Path.Combine(_extractDir, e.FileName);
|
||||
/// using (var output = File.Open(outputPath, FileMode.Create, FileAccess.ReadWrite))
|
||||
/// {
|
||||
/// while ((n= input.Read(buffer,0,buffer.Length)) > 0)
|
||||
/// {
|
||||
/// output.Write(buffer,0,n);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// </code>
|
||||
/// </example>
|
||||
public String Password
|
||||
{
|
||||
set
|
||||
{
|
||||
if (_closed)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new System.InvalidOperationException("The stream has been closed.");
|
||||
}
|
||||
_Password = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void SetupStream()
|
||||
{
|
||||
// Seek to the correct posn in the file, and open a
|
||||
// stream that can be read.
|
||||
_crcStream= _currentEntry.InternalOpenReader(_Password);
|
||||
_LeftToRead = _crcStream.Length;
|
||||
_needSetup = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal Stream ReadStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return _inputStream;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Read the data from the stream into the buffer.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The data for the zipentry will be decrypted and uncompressed, as
|
||||
/// necessary, before being copied into the buffer.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// You must set the <see cref="Password"/> property before calling
|
||||
/// <c>Read()</c> the first time for an encrypted entry. To determine if an
|
||||
/// entry is encrypted and requires a password, check the <see
|
||||
/// cref="ZipEntry.Encryption">ZipEntry.Encryption</see> property.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="buffer">The buffer to hold the data read from the stream.</param>
|
||||
/// <param name="offset">the offset within the buffer to copy the first byte read.</param>
|
||||
/// <param name="count">the number of bytes to read.</param>
|
||||
/// <returns>the number of bytes read, after decryption and decompression.</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_closed)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new System.InvalidOperationException("The stream has been closed.");
|
||||
}
|
||||
|
||||
if (_needSetup)
|
||||
SetupStream();
|
||||
|
||||
if (_LeftToRead == 0) return 0;
|
||||
|
||||
int len = (_LeftToRead > count) ? count : (int)_LeftToRead;
|
||||
int n = _crcStream.Read(buffer, offset, len);
|
||||
|
||||
_LeftToRead -= n;
|
||||
|
||||
if (_LeftToRead == 0)
|
||||
{
|
||||
int CrcResult = _crcStream.Crc;
|
||||
_currentEntry.VerifyCrcAfterExtract(CrcResult);
|
||||
_inputStream.Seek(_endOfEntry, SeekOrigin.Begin);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_inputStream);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Read the next entry from the zip file.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Call this method just before calling <see cref="Read(byte[], int, int)"/>,
|
||||
/// to position the pointer in the zip file to the next entry that can be
|
||||
/// read. Subsequent calls to <c>Read()</c>, will decrypt and decompress the
|
||||
/// data in the zip file, until <c>Read()</c> returns 0.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Each time you call <c>GetNextEntry()</c>, the pointer in the wrapped
|
||||
/// stream is moved to the next entry in the zip file. If you call <see
|
||||
/// cref="Seek(long, SeekOrigin)"/>, and thus re-position the pointer within
|
||||
/// the file, you will need to call <c>GetNextEntry()</c> again, to insure
|
||||
/// that the file pointer is positioned at the beginning of a zip entry.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This method returns the <c>ZipEntry</c>. Using a stream approach, you will
|
||||
/// read the raw bytes for an entry in a zip file via calls to <c>Read()</c>.
|
||||
/// Alternatively, you can extract an entry into a file, or a stream, by
|
||||
/// calling <see cref="ZipEntry.Extract()"/>, or one of its siblings.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <returns>
|
||||
/// The <c>ZipEntry</c> read. Returns null (or Nothing in VB) if there are no more
|
||||
/// entries in the zip file.
|
||||
/// </returns>
|
||||
///
|
||||
public ZipEntry GetNextEntry()
|
||||
{
|
||||
if (_findRequired)
|
||||
{
|
||||
// find the next signature
|
||||
long d = SharedUtilities.FindSignature(_inputStream, ZipConstants.ZipEntrySignature);
|
||||
if (d == -1) return null;
|
||||
// back up 4 bytes: ReadEntry assumes the file pointer is positioned before the entry signature
|
||||
_inputStream.Seek(-4, SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_inputStream);
|
||||
}
|
||||
// workitem 10923
|
||||
else if (_firstEntry)
|
||||
{
|
||||
// we've already read one entry.
|
||||
// Seek to the end of it.
|
||||
_inputStream.Seek(_endOfEntry, SeekOrigin.Begin);
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_inputStream);
|
||||
}
|
||||
|
||||
_currentEntry = ZipEntry.ReadEntry(_container, !_firstEntry);
|
||||
// ReadEntry leaves the file position after all the entry
|
||||
// data and the optional bit-3 data descriptpr. This is
|
||||
// where the next entry would normally start.
|
||||
_endOfEntry = _inputStream.Position;
|
||||
_firstEntry = true;
|
||||
_needSetup = true;
|
||||
_findRequired= false;
|
||||
return _currentEntry;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Dispose the stream.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method disposes the ZipInputStream. It may also close the
|
||||
/// underlying stream, depending on which constructor was used.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Typically the application will call <c>Dispose()</c> implicitly, via
|
||||
/// a <c>using</c> statement in C#, or a <c>Using</c> statement in VB.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Application code won't call this code directly. This method may
|
||||
/// be invoked in two distinct scenarios. If disposing == true, the
|
||||
/// method has been called directly or indirectly by a user's code,
|
||||
/// for example via the public Dispose() method. In this case, both
|
||||
/// managed and unmanaged resources can be referenced and disposed.
|
||||
/// If disposing == false, the method has been called by the runtime
|
||||
/// from inside the object finalizer and this method should not
|
||||
/// reference other objects; in that case only unmanaged resources
|
||||
/// must be referenced or disposed.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="disposing">
|
||||
/// true if the Dispose method was invoked by user code.
|
||||
/// </param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_closed) return;
|
||||
|
||||
if (disposing) // not called from finalizer
|
||||
{
|
||||
// When ZipInputStream is used within a using clause, and an
|
||||
// exception is thrown, Close() is invoked. But we don't want to
|
||||
// try to write anything in that case. Eventually the exception
|
||||
// will be propagated to the application.
|
||||
if (_exceptionPending) return;
|
||||
|
||||
if (!_leaveUnderlyingStreamOpen)
|
||||
{
|
||||
#if NETCF
|
||||
_inputStream.Close();
|
||||
#else
|
||||
_inputStream.Dispose();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
_closed= true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Always returns true.
|
||||
/// </summary>
|
||||
public override bool CanRead { get { return true; }}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of <c>CanSeek</c> for the underlying (wrapped) stream.
|
||||
/// </summary>
|
||||
public override bool CanSeek { get { return _inputStream.CanSeek; } }
|
||||
|
||||
/// <summary>
|
||||
/// Always returns false.
|
||||
/// </summary>
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the length of the underlying stream.
|
||||
/// </summary>
|
||||
public override long Length { get { return _inputStream.Length; }}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position of the underlying stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Setting the position is equivalent to calling <c>Seek(value, SeekOrigin.Begin)</c>.
|
||||
/// </remarks>
|
||||
public override long Position
|
||||
{
|
||||
get { return _inputStream.Position;}
|
||||
set { Seek(value, SeekOrigin.Begin); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a no-op.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotSupportedException("Flush");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This method always throws a NotSupportedException.
|
||||
/// </summary>
|
||||
/// <param name="buffer">ignored</param>
|
||||
/// <param name="offset">ignored</param>
|
||||
/// <param name="count">ignored</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException("Write");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This method seeks in the underlying stream.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Call this method if you want to seek around within the zip file for random access.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Applications can intermix calls to <c>Seek()</c> with calls to <see
|
||||
/// cref="GetNextEntry()"/>. After a call to <c>Seek()</c>,
|
||||
/// <c>GetNextEntry()</c> will get the next <c>ZipEntry</c> that falls after
|
||||
/// the current position in the input stream. You're on your own for finding
|
||||
/// out just where to seek in the stream, to get to the various entries.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="offset">the offset point to seek to</param>
|
||||
/// <param name="origin">the reference point from which to seek</param>
|
||||
/// <returns>The new position</returns>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
_findRequired= true;
|
||||
var x = _inputStream.Seek(offset, origin);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_inputStream);
|
||||
return x;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method always throws a NotSupportedException.
|
||||
/// </summary>
|
||||
/// <param name="value">ignored</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
|
||||
private Stream _inputStream;
|
||||
private System.Text.Encoding _provisionalAlternateEncoding;
|
||||
private ZipEntry _currentEntry;
|
||||
private bool _firstEntry;
|
||||
private bool _needSetup;
|
||||
private ZipContainer _container;
|
||||
private Ionic.Crc.CrcCalculatorStream _crcStream;
|
||||
private Int64 _LeftToRead;
|
||||
internal String _Password;
|
||||
private Int64 _endOfEntry;
|
||||
private string _name;
|
||||
|
||||
private bool _leaveUnderlyingStreamOpen;
|
||||
private bool _closed;
|
||||
private bool _findRequired;
|
||||
private bool _exceptionPending;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,571 @@
|
||||
// ZipSegmentedStream.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-13 22:25:45>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines logic for zip streams that span disk files.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
internal class ZipSegmentedStream : System.IO.Stream
|
||||
{
|
||||
enum RwMode
|
||||
{
|
||||
None = 0,
|
||||
ReadOnly = 1,
|
||||
Write = 2,
|
||||
//Update = 3
|
||||
}
|
||||
|
||||
private RwMode rwMode;
|
||||
private bool _exceptionPending; // **see note below
|
||||
private string _baseName;
|
||||
private string _baseDir;
|
||||
//private bool _isDisposed;
|
||||
private string _currentName;
|
||||
private string _currentTempName;
|
||||
private uint _currentDiskNumber;
|
||||
private uint _maxDiskNumber;
|
||||
private int _maxSegmentSize;
|
||||
private System.IO.Stream _innerStream;
|
||||
|
||||
// **Note regarding exceptions:
|
||||
//
|
||||
// When ZipSegmentedStream is employed within a using clause,
|
||||
// which is the typical scenario, and an exception is thrown
|
||||
// within the scope of the using, Dispose() is invoked
|
||||
// implicitly before processing the initial exception. If that
|
||||
// happens, this class sets _exceptionPending to true, and then
|
||||
// within the Dispose(bool), takes special action as
|
||||
// appropriate. Need to be careful: any additional exceptions
|
||||
// will mask the original one.
|
||||
|
||||
private ZipSegmentedStream() : base()
|
||||
{
|
||||
_exceptionPending = false;
|
||||
}
|
||||
|
||||
public static ZipSegmentedStream ForReading(string name,
|
||||
uint initialDiskNumber,
|
||||
uint maxDiskNumber)
|
||||
{
|
||||
ZipSegmentedStream zss = new ZipSegmentedStream()
|
||||
{
|
||||
rwMode = RwMode.ReadOnly,
|
||||
CurrentSegment = initialDiskNumber,
|
||||
_maxDiskNumber = maxDiskNumber,
|
||||
_baseName = name,
|
||||
};
|
||||
|
||||
// Console.WriteLine("ZSS: ForReading ({0})",
|
||||
// Path.GetFileName(zss.CurrentName));
|
||||
|
||||
zss._SetReadStream();
|
||||
|
||||
return zss;
|
||||
}
|
||||
|
||||
|
||||
public static ZipSegmentedStream ForWriting(string name, int maxSegmentSize)
|
||||
{
|
||||
ZipSegmentedStream zss = new ZipSegmentedStream()
|
||||
{
|
||||
rwMode = RwMode.Write,
|
||||
CurrentSegment = 0,
|
||||
_baseName = name,
|
||||
_maxSegmentSize = maxSegmentSize,
|
||||
_baseDir = Path.GetDirectoryName(name)
|
||||
};
|
||||
|
||||
// workitem 9522
|
||||
if (zss._baseDir=="") zss._baseDir=".";
|
||||
|
||||
zss._SetWriteStream(0);
|
||||
|
||||
// Console.WriteLine("ZSS: ForWriting ({0})",
|
||||
// Path.GetFileName(zss.CurrentName));
|
||||
|
||||
return zss;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sort-of like a factory method, ForUpdate is used only when
|
||||
/// the application needs to update the zip entry metadata for
|
||||
/// a segmented zip file, when the starting segment is earlier
|
||||
/// than the ending segment, for a particular entry.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The update is always contiguous, never rolls over. As a
|
||||
/// result, this method doesn't need to return a ZSS; it can
|
||||
/// simply return a FileStream. That's why it's "sort of"
|
||||
/// like a Factory method.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Caller must Close/Dispose the stream object returned by
|
||||
/// this method.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static Stream ForUpdate(string name, uint diskNumber)
|
||||
{
|
||||
if (diskNumber >= 99)
|
||||
throw new ArgumentOutOfRangeException("diskNumber");
|
||||
|
||||
string fname =
|
||||
String.Format("{0}.z{1:D2}",
|
||||
Path.Combine(Path.GetDirectoryName(name),
|
||||
Path.GetFileNameWithoutExtension(name)),
|
||||
diskNumber + 1);
|
||||
|
||||
// Console.WriteLine("ZSS: ForUpdate ({0})",
|
||||
// Path.GetFileName(fname));
|
||||
|
||||
// This class assumes that the update will not expand the
|
||||
// size of the segment. Update is used only for an in-place
|
||||
// update of zip metadata. It never will try to write beyond
|
||||
// the end of a segment.
|
||||
|
||||
return File.Open(fname,
|
||||
FileMode.Open,
|
||||
FileAccess.ReadWrite,
|
||||
FileShare.None);
|
||||
}
|
||||
|
||||
public bool ContiguousWrite
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
public UInt32 CurrentSegment
|
||||
{
|
||||
get
|
||||
{
|
||||
return _currentDiskNumber;
|
||||
}
|
||||
private set
|
||||
{
|
||||
_currentDiskNumber = value;
|
||||
_currentName = null; // it will get updated next time referenced
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name of the filesystem file corresponding to the current segment.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The name is not always the name currently being used in the
|
||||
/// filesystem. When rwMode is RwMode.Write, the filesystem file has a
|
||||
/// temporary name until the stream is closed or until the next segment is
|
||||
/// started.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public String CurrentName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_currentName==null)
|
||||
_currentName = _NameForSegment(CurrentSegment);
|
||||
|
||||
return _currentName;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String CurrentTempName
|
||||
{
|
||||
get
|
||||
{
|
||||
return _currentTempName;
|
||||
}
|
||||
}
|
||||
|
||||
private string _NameForSegment(uint diskNumber)
|
||||
{
|
||||
if (diskNumber >= 99)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new OverflowException("The number of zip segments would exceed 99.");
|
||||
}
|
||||
|
||||
return String.Format("{0}.z{1:D2}",
|
||||
Path.Combine(Path.GetDirectoryName(_baseName),
|
||||
Path.GetFileNameWithoutExtension(_baseName)),
|
||||
diskNumber + 1);
|
||||
}
|
||||
|
||||
|
||||
// Returns the segment that WILL be current if writing
|
||||
// a block of the given length.
|
||||
// This isn't exactly true. It could roll over beyond
|
||||
// this number.
|
||||
public UInt32 ComputeSegment(int length)
|
||||
{
|
||||
if (_innerStream.Position + length > _maxSegmentSize)
|
||||
// the block will go AT LEAST into the next segment
|
||||
return CurrentSegment + 1;
|
||||
|
||||
// it will fit in the current segment
|
||||
return CurrentSegment;
|
||||
}
|
||||
|
||||
|
||||
public override String ToString()
|
||||
{
|
||||
return String.Format("{0}[{1}][{2}], pos=0x{3:X})",
|
||||
"ZipSegmentedStream", CurrentName,
|
||||
rwMode.ToString(),
|
||||
this.Position);
|
||||
}
|
||||
|
||||
|
||||
private void _SetReadStream()
|
||||
{
|
||||
if (_innerStream != null)
|
||||
{
|
||||
#if NETCF
|
||||
_innerStream.Close();
|
||||
#else
|
||||
_innerStream.Dispose();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (CurrentSegment + 1 == _maxDiskNumber)
|
||||
_currentName = _baseName;
|
||||
|
||||
// Console.WriteLine("ZSS: SRS ({0})",
|
||||
// Path.GetFileName(CurrentName));
|
||||
|
||||
_innerStream = File.OpenRead(CurrentName);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Read from the stream
|
||||
/// </summary>
|
||||
/// <param name="buffer">the buffer to read</param>
|
||||
/// <param name="offset">the offset at which to start</param>
|
||||
/// <param name="count">the number of bytes to read</param>
|
||||
/// <returns>the number of bytes actually read</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (rwMode != RwMode.ReadOnly)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new InvalidOperationException("Stream Error: Cannot Read.");
|
||||
}
|
||||
|
||||
int r = _innerStream.Read(buffer, offset, count);
|
||||
int r1 = r;
|
||||
|
||||
while (r1 != count)
|
||||
{
|
||||
if (_innerStream.Position != _innerStream.Length)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new ZipException(String.Format("Read error in file {0}", CurrentName));
|
||||
|
||||
}
|
||||
|
||||
if (CurrentSegment + 1 == _maxDiskNumber)
|
||||
return r; // no more to read
|
||||
|
||||
CurrentSegment++;
|
||||
_SetReadStream();
|
||||
offset += r1;
|
||||
count -= r1;
|
||||
r1 = _innerStream.Read(buffer, offset, count);
|
||||
r += r1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void _SetWriteStream(uint increment)
|
||||
{
|
||||
if (_innerStream != null)
|
||||
{
|
||||
#if NETCF
|
||||
_innerStream.Close();
|
||||
#else
|
||||
_innerStream.Dispose();
|
||||
#endif
|
||||
if (File.Exists(CurrentName))
|
||||
File.Delete(CurrentName);
|
||||
File.Move(_currentTempName, CurrentName);
|
||||
// Console.WriteLine("ZSS: SWS close ({0})",
|
||||
// Path.GetFileName(CurrentName));
|
||||
}
|
||||
|
||||
if (increment > 0)
|
||||
CurrentSegment += increment;
|
||||
|
||||
SharedUtilities.CreateAndOpenUniqueTempFile(_baseDir,
|
||||
out _innerStream,
|
||||
out _currentTempName);
|
||||
|
||||
// Console.WriteLine("ZSS: SWS open ({0})",
|
||||
// Path.GetFileName(_currentTempName));
|
||||
|
||||
if (CurrentSegment == 0)
|
||||
_innerStream.Write(BitConverter.GetBytes(ZipConstants.SplitArchiveSignature), 0, 4);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Write to the stream.
|
||||
/// </summary>
|
||||
/// <param name="buffer">the buffer from which to write</param>
|
||||
/// <param name="offset">the offset at which to start writing</param>
|
||||
/// <param name="count">the number of bytes to write</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (rwMode != RwMode.Write)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new InvalidOperationException("Stream Error: Cannot Write.");
|
||||
}
|
||||
|
||||
|
||||
if (ContiguousWrite)
|
||||
{
|
||||
// enough space for a contiguous write?
|
||||
if (_innerStream.Position + count > _maxSegmentSize)
|
||||
_SetWriteStream(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (_innerStream.Position + count > _maxSegmentSize)
|
||||
{
|
||||
int c = unchecked(_maxSegmentSize - (int)_innerStream.Position);
|
||||
_innerStream.Write(buffer, offset, c);
|
||||
_SetWriteStream(1);
|
||||
count -= c;
|
||||
offset += c;
|
||||
}
|
||||
}
|
||||
|
||||
_innerStream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
|
||||
public long TruncateBackward(uint diskNumber, long offset)
|
||||
{
|
||||
// Console.WriteLine("***ZSS.Trunc to disk {0}", diskNumber);
|
||||
// Console.WriteLine("***ZSS.Trunc: current disk {0}", CurrentSegment);
|
||||
if (diskNumber >= 99)
|
||||
throw new ArgumentOutOfRangeException("diskNumber");
|
||||
|
||||
if (rwMode != RwMode.Write)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new ZipException("bad state.");
|
||||
}
|
||||
|
||||
// Seek back in the segmented stream to a (maybe) prior segment.
|
||||
|
||||
// Check if it is the same segment. If it is, very simple.
|
||||
if (diskNumber == CurrentSegment)
|
||||
{
|
||||
var x =_innerStream.Seek(offset, SeekOrigin.Begin);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_innerStream);
|
||||
return x;
|
||||
}
|
||||
|
||||
// Seeking back to a prior segment.
|
||||
// The current segment and any intervening segments must be removed.
|
||||
// First, close the current segment, and then remove it.
|
||||
if (_innerStream != null)
|
||||
{
|
||||
#if NETCF
|
||||
_innerStream.Close();
|
||||
#else
|
||||
_innerStream.Dispose();
|
||||
#endif
|
||||
if (File.Exists(_currentTempName))
|
||||
File.Delete(_currentTempName);
|
||||
}
|
||||
|
||||
// Now, remove intervening segments.
|
||||
for (uint j= CurrentSegment-1; j > diskNumber; j--)
|
||||
{
|
||||
string s = _NameForSegment(j);
|
||||
// Console.WriteLine("***ZSS.Trunc: removing file {0}", s);
|
||||
if (File.Exists(s))
|
||||
File.Delete(s);
|
||||
}
|
||||
|
||||
// now, open the desired segment. It must exist.
|
||||
CurrentSegment = diskNumber;
|
||||
|
||||
// get a new temp file, try 3 times:
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
_currentTempName = SharedUtilities.InternalGetTempFileName();
|
||||
// move the .z0x file back to a temp name
|
||||
File.Move(CurrentName, _currentTempName);
|
||||
break; // workitem 12403
|
||||
}
|
||||
catch(IOException)
|
||||
{
|
||||
if (i == 2) throw;
|
||||
}
|
||||
}
|
||||
|
||||
// open it
|
||||
_innerStream = new FileStream(_currentTempName, FileMode.Open);
|
||||
|
||||
var r = _innerStream.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_innerStream);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return (rwMode == RwMode.ReadOnly &&
|
||||
(_innerStream != null) &&
|
||||
_innerStream.CanRead);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_innerStream != null) &&
|
||||
_innerStream.CanSeek;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return (rwMode == RwMode.Write) &&
|
||||
(_innerStream != null) &&
|
||||
_innerStream.CanWrite;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
_innerStream.Flush();
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return _innerStream.Length;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { return _innerStream.Position; }
|
||||
set { _innerStream.Position = value; }
|
||||
}
|
||||
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
var x = _innerStream.Seek(offset, origin);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_innerStream);
|
||||
return x;
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
if (rwMode != RwMode.Write)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
_innerStream.SetLength(value);
|
||||
}
|
||||
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
// this gets called by Stream.Close()
|
||||
|
||||
// if (_isDisposed) return;
|
||||
// _isDisposed = true;
|
||||
//Console.WriteLine("Dispose (mode={0})\n", rwMode.ToString());
|
||||
|
||||
try
|
||||
{
|
||||
if (_innerStream != null)
|
||||
{
|
||||
#if NETCF
|
||||
_innerStream.Close();
|
||||
#else
|
||||
_innerStream.Dispose();
|
||||
#endif
|
||||
//_innerStream = null;
|
||||
if (rwMode == RwMode.Write)
|
||||
{
|
||||
if (_exceptionPending)
|
||||
{
|
||||
// possibly could try to clean up all the
|
||||
// temp files created so far...
|
||||
}
|
||||
else
|
||||
{
|
||||
// // move the final temp file to the .zNN name
|
||||
// if (File.Exists(CurrentName))
|
||||
// File.Delete(CurrentName);
|
||||
// if (File.Exists(_currentTempName))
|
||||
// File.Move(_currentTempName, CurrentName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,740 @@
|
||||
// DeflateStream.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2010 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-31 14:48:11>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the DeflateStream class, which can be used as a replacement for
|
||||
// the System.IO.Compression.DeflateStream class in the .NET BCL.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
using System;
|
||||
|
||||
namespace Ionic.Zlib
|
||||
{
|
||||
/// <summary>
|
||||
/// A class for compressing and decompressing streams using the Deflate algorithm.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// The DeflateStream is a <see
|
||||
/// href="http://en.wikipedia.org/wiki/Decorator_pattern">Decorator</see> on a <see
|
||||
/// cref="System.IO.Stream"/>. It adds DEFLATE compression or decompression to any
|
||||
/// stream.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Using this stream, applications can compress or decompress data via stream
|
||||
/// <c>Read</c> and <c>Write</c> operations. Either compresssion or decompression
|
||||
/// can occur through either reading or writing. The compression format used is
|
||||
/// DEFLATE, which is documented in <see
|
||||
/// href="http://www.ietf.org/rfc/rfc1951.txt">IETF RFC 1951</see>, "DEFLATE
|
||||
/// Compressed Data Format Specification version 1.3.".
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This class is similar to <see cref="ZlibStream"/>, except that
|
||||
/// <c>ZlibStream</c> adds the <see href="http://www.ietf.org/rfc/rfc1950.txt">RFC
|
||||
/// 1950 - ZLIB</see> framing bytes to a compressed stream when compressing, or
|
||||
/// expects the RFC1950 framing bytes when decompressing. The <c>DeflateStream</c>
|
||||
/// does not.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <seealso cref="ZlibStream" />
|
||||
/// <seealso cref="GZipStream" />
|
||||
public class DeflateStream : System.IO.Stream
|
||||
{
|
||||
internal ZlibBaseStream _baseStream;
|
||||
internal System.IO.Stream _innerStream;
|
||||
bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Create a DeflateStream using the specified CompressionMode.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// When mode is <c>CompressionMode.Compress</c>, the DeflateStream will use
|
||||
/// the default compression level. The "captive" stream will be closed when
|
||||
/// the DeflateStream is closed.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
/// This example uses a DeflateStream to compress data from a file, and writes
|
||||
/// the compressed data to another file.
|
||||
/// <code>
|
||||
/// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
|
||||
/// {
|
||||
/// using (var raw = System.IO.File.Create(fileToCompress + ".deflated"))
|
||||
/// {
|
||||
/// using (Stream compressor = new DeflateStream(raw, CompressionMode.Compress))
|
||||
/// {
|
||||
/// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
|
||||
/// int n;
|
||||
/// while ((n= input.Read(buffer, 0, buffer.Length)) != 0)
|
||||
/// {
|
||||
/// compressor.Write(buffer, 0, n);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Using input As Stream = File.OpenRead(fileToCompress)
|
||||
/// Using raw As FileStream = File.Create(fileToCompress & ".deflated")
|
||||
/// Using compressor As Stream = New DeflateStream(raw, CompressionMode.Compress)
|
||||
/// Dim buffer As Byte() = New Byte(4096) {}
|
||||
/// Dim n As Integer = -1
|
||||
/// Do While (n <> 0)
|
||||
/// If (n > 0) Then
|
||||
/// compressor.Write(buffer, 0, n)
|
||||
/// End If
|
||||
/// n = input.Read(buffer, 0, buffer.Length)
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="stream">The stream which will be read or written.</param>
|
||||
/// <param name="mode">Indicates whether the DeflateStream will compress or decompress.</param>
|
||||
public DeflateStream(System.IO.Stream stream, CompressionMode mode)
|
||||
: this(stream, mode, CompressionLevel.Default, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a DeflateStream using the specified CompressionMode and the specified CompressionLevel.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// When mode is <c>CompressionMode.Decompress</c>, the level parameter is
|
||||
/// ignored. The "captive" stream will be closed when the DeflateStream is
|
||||
/// closed.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example uses a DeflateStream to compress data from a file, and writes
|
||||
/// the compressed data to another file.
|
||||
///
|
||||
/// <code>
|
||||
/// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
|
||||
/// {
|
||||
/// using (var raw = System.IO.File.Create(fileToCompress + ".deflated"))
|
||||
/// {
|
||||
/// using (Stream compressor = new DeflateStream(raw,
|
||||
/// CompressionMode.Compress,
|
||||
/// CompressionLevel.BestCompression))
|
||||
/// {
|
||||
/// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
|
||||
/// int n= -1;
|
||||
/// while (n != 0)
|
||||
/// {
|
||||
/// if (n > 0)
|
||||
/// compressor.Write(buffer, 0, n);
|
||||
/// n= input.Read(buffer, 0, buffer.Length);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Using input As Stream = File.OpenRead(fileToCompress)
|
||||
/// Using raw As FileStream = File.Create(fileToCompress & ".deflated")
|
||||
/// Using compressor As Stream = New DeflateStream(raw, CompressionMode.Compress, CompressionLevel.BestCompression)
|
||||
/// Dim buffer As Byte() = New Byte(4096) {}
|
||||
/// Dim n As Integer = -1
|
||||
/// Do While (n <> 0)
|
||||
/// If (n > 0) Then
|
||||
/// compressor.Write(buffer, 0, n)
|
||||
/// End If
|
||||
/// n = input.Read(buffer, 0, buffer.Length)
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="stream">The stream to be read or written while deflating or inflating.</param>
|
||||
/// <param name="mode">Indicates whether the <c>DeflateStream</c> will compress or decompress.</param>
|
||||
/// <param name="level">A tuning knob to trade speed for effectiveness.</param>
|
||||
public DeflateStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level)
|
||||
: this(stream, mode, level, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <c>DeflateStream</c> using the specified
|
||||
/// <c>CompressionMode</c>, and explicitly specify whether the
|
||||
/// stream should be left open after Deflation or Inflation.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// This constructor allows the application to request that the captive stream
|
||||
/// remain open after the deflation or inflation occurs. By default, after
|
||||
/// <c>Close()</c> is called on the stream, the captive stream is also
|
||||
/// closed. In some cases this is not desired, for example if the stream is a
|
||||
/// memory stream that will be re-read after compression. Specify true for
|
||||
/// the <paramref name="leaveOpen"/> parameter to leave the stream open.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The <c>DeflateStream</c> will use the default compression level.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// See the other overloads of this constructor for example code.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="stream">
|
||||
/// The stream which will be read or written. This is called the
|
||||
/// "captive" stream in other places in this documentation.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="mode">
|
||||
/// Indicates whether the <c>DeflateStream</c> will compress or decompress.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="leaveOpen">true if the application would like the stream to
|
||||
/// remain open after inflation/deflation.</param>
|
||||
public DeflateStream(System.IO.Stream stream, CompressionMode mode, bool leaveOpen)
|
||||
: this(stream, mode, CompressionLevel.Default, leaveOpen)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <c>DeflateStream</c> using the specified <c>CompressionMode</c>
|
||||
/// and the specified <c>CompressionLevel</c>, and explicitly specify whether
|
||||
/// the stream should be left open after Deflation or Inflation.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// When mode is <c>CompressionMode.Decompress</c>, the level parameter is ignored.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This constructor allows the application to request that the captive stream
|
||||
/// remain open after the deflation or inflation occurs. By default, after
|
||||
/// <c>Close()</c> is called on the stream, the captive stream is also
|
||||
/// closed. In some cases this is not desired, for example if the stream is a
|
||||
/// <see cref="System.IO.MemoryStream"/> that will be re-read after
|
||||
/// compression. Specify true for the <paramref name="leaveOpen"/> parameter
|
||||
/// to leave the stream open.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example shows how to use a <c>DeflateStream</c> to compress data from
|
||||
/// a file, and store the compressed data into another file.
|
||||
///
|
||||
/// <code>
|
||||
/// using (var output = System.IO.File.Create(fileToCompress + ".deflated"))
|
||||
/// {
|
||||
/// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
|
||||
/// {
|
||||
/// using (Stream compressor = new DeflateStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, true))
|
||||
/// {
|
||||
/// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
|
||||
/// int n= -1;
|
||||
/// while (n != 0)
|
||||
/// {
|
||||
/// if (n > 0)
|
||||
/// compressor.Write(buffer, 0, n);
|
||||
/// n= input.Read(buffer, 0, buffer.Length);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// // can write additional data to the output stream here
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Using output As FileStream = File.Create(fileToCompress & ".deflated")
|
||||
/// Using input As Stream = File.OpenRead(fileToCompress)
|
||||
/// Using compressor As Stream = New DeflateStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, True)
|
||||
/// Dim buffer As Byte() = New Byte(4096) {}
|
||||
/// Dim n As Integer = -1
|
||||
/// Do While (n <> 0)
|
||||
/// If (n > 0) Then
|
||||
/// compressor.Write(buffer, 0, n)
|
||||
/// End If
|
||||
/// n = input.Read(buffer, 0, buffer.Length)
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// ' can write additional data to the output stream here.
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="stream">The stream which will be read or written.</param>
|
||||
/// <param name="mode">Indicates whether the DeflateStream will compress or decompress.</param>
|
||||
/// <param name="leaveOpen">true if the application would like the stream to remain open after inflation/deflation.</param>
|
||||
/// <param name="level">A tuning knob to trade speed for effectiveness.</param>
|
||||
public DeflateStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen)
|
||||
{
|
||||
_innerStream = stream;
|
||||
_baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.DEFLATE, leaveOpen);
|
||||
}
|
||||
|
||||
#region Zlib properties
|
||||
|
||||
/// <summary>
|
||||
/// This property sets the flush behavior on the stream.
|
||||
/// </summary>
|
||||
/// <remarks> See the ZLIB documentation for the meaning of the flush behavior.
|
||||
/// </remarks>
|
||||
virtual public FlushType FlushMode
|
||||
{
|
||||
get { return (this._baseStream._flushMode); }
|
||||
set
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
this._baseStream._flushMode = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The size of the working buffer for the compression codec.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The working buffer is used for all stream operations. The default size is
|
||||
/// 1024 bytes. The minimum size is 128 bytes. You may get better performance
|
||||
/// with a larger buffer. Then again, you might not. You would have to test
|
||||
/// it.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Set this before the first call to <c>Read()</c> or <c>Write()</c> on the
|
||||
/// stream. If you try to set it afterwards, it will throw.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public int BufferSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._baseStream._bufferSize;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
if (this._baseStream._workingBuffer != null)
|
||||
throw new ZlibException("The working buffer is already set.");
|
||||
if (value < ZlibConstants.WorkingBufferSizeMin)
|
||||
throw new ZlibException(String.Format("Don't be silly. {0} bytes?? Use a bigger buffer, at least {1}.", value, ZlibConstants.WorkingBufferSizeMin));
|
||||
this._baseStream._bufferSize = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ZLIB strategy to be used during compression.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// By tweaking this parameter, you may be able to optimize the compression for
|
||||
/// data with particular characteristics.
|
||||
/// </remarks>
|
||||
public CompressionStrategy Strategy
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._baseStream.Strategy;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
this._baseStream.Strategy = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Returns the total number of bytes input so far.</summary>
|
||||
virtual public long TotalIn
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._baseStream._z.TotalBytesIn;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Returns the total number of bytes output so far.</summary>
|
||||
virtual public long TotalOut
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._baseStream._z.TotalBytesOut;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.IO.Stream methods
|
||||
/// <summary>
|
||||
/// Dispose the stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This may or may not result in a <c>Close()</c> call on the captive
|
||||
/// stream. See the constructors that have a <c>leaveOpen</c> parameter
|
||||
/// for more information.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Application code won't call this code directly. This method may be
|
||||
/// invoked in two distinct scenarios. If disposing == true, the method
|
||||
/// has been called directly or indirectly by a user's code, for example
|
||||
/// via the public Dispose() method. In this case, both managed and
|
||||
/// unmanaged resources can be referenced and disposed. If disposing ==
|
||||
/// false, the method has been called by the runtime from inside the
|
||||
/// object finalizer and this method should not reference other objects;
|
||||
/// in that case only unmanaged resources must be referenced or
|
||||
/// disposed.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="disposing">
|
||||
/// true if the Dispose method was invoked by user code.
|
||||
/// </param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing && (this._baseStream != null))
|
||||
this._baseStream.Close();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream can be read.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The return value depends on whether the captive stream supports reading.
|
||||
/// </remarks>
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
return _baseStream._stream.CanRead;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream supports Seek operations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Always returns false.
|
||||
/// </remarks>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream can be written.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The return value depends on whether the captive stream supports writing.
|
||||
/// </remarks>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
return _baseStream._stream.CanWrite;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flush the stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
_baseStream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reading this property always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The position of the stream pointer.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Setting this property always throws a <see
|
||||
/// cref="NotImplementedException"/>. Reading will return the total bytes
|
||||
/// written out, if used in writing, or the total bytes read in, if used in
|
||||
/// reading. The count may refer to compressed bytes or uncompressed bytes,
|
||||
/// depending on how you've used the stream.
|
||||
/// </remarks>
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this._baseStream._streamMode == Ionic.Zlib.ZlibBaseStream.StreamMode.Writer)
|
||||
return this._baseStream._z.TotalBytesOut;
|
||||
if (this._baseStream._streamMode == Ionic.Zlib.ZlibBaseStream.StreamMode.Reader)
|
||||
return this._baseStream._z.TotalBytesIn;
|
||||
return 0;
|
||||
}
|
||||
set { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read data from the stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// If you wish to use the <c>DeflateStream</c> to compress data while
|
||||
/// reading, you can create a <c>DeflateStream</c> with
|
||||
/// <c>CompressionMode.Compress</c>, providing an uncompressed data stream.
|
||||
/// Then call Read() on that <c>DeflateStream</c>, and the data read will be
|
||||
/// compressed as you read. If you wish to use the <c>DeflateStream</c> to
|
||||
/// decompress data while reading, you can create a <c>DeflateStream</c> with
|
||||
/// <c>CompressionMode.Decompress</c>, providing a readable compressed data
|
||||
/// stream. Then call Read() on that <c>DeflateStream</c>, and the data read
|
||||
/// will be decompressed as you read.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A <c>DeflateStream</c> can be used for <c>Read()</c> or <c>Write()</c>, but not both.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="buffer">The buffer into which the read data should be placed.</param>
|
||||
/// <param name="offset">the offset within that data array to put the first byte read.</param>
|
||||
/// <param name="count">the number of bytes to read.</param>
|
||||
/// <returns>the number of bytes actually read</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
return _baseStream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calling this method always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
/// <param name="offset">this is irrelevant, since it will always throw!</param>
|
||||
/// <param name="origin">this is irrelevant, since it will always throw!</param>
|
||||
/// <returns>irrelevant!</returns>
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calling this method always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">this is irrelevant, since it will always throw!</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write data to the stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// If you wish to use the <c>DeflateStream</c> to compress data while
|
||||
/// writing, you can create a <c>DeflateStream</c> with
|
||||
/// <c>CompressionMode.Compress</c>, and a writable output stream. Then call
|
||||
/// <c>Write()</c> on that <c>DeflateStream</c>, providing uncompressed data
|
||||
/// as input. The data sent to the output stream will be the compressed form
|
||||
/// of the data written. If you wish to use the <c>DeflateStream</c> to
|
||||
/// decompress data while writing, you can create a <c>DeflateStream</c> with
|
||||
/// <c>CompressionMode.Decompress</c>, and a writable output stream. Then
|
||||
/// call <c>Write()</c> on that stream, providing previously compressed
|
||||
/// data. The data sent to the output stream will be the decompressed form of
|
||||
/// the data written.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A <c>DeflateStream</c> can be used for <c>Read()</c> or <c>Write()</c>,
|
||||
/// but not both.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="buffer">The buffer holding data to write to the stream.</param>
|
||||
/// <param name="offset">the offset within that data array to find the first byte to write.</param>
|
||||
/// <param name="count">the number of bytes to write.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
_baseStream.Write(buffer, offset, count);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compress a string into a byte array using DEFLATE (RFC 1951).
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Uncompress it with <see cref="DeflateStream.UncompressString(byte[])"/>.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <seealso cref="DeflateStream.UncompressString(byte[])">DeflateStream.UncompressString(byte[])</seealso>
|
||||
/// <seealso cref="DeflateStream.CompressBuffer(byte[])">DeflateStream.CompressBuffer(byte[])</seealso>
|
||||
/// <seealso cref="GZipStream.CompressString(string)">GZipStream.CompressString(string)</seealso>
|
||||
/// <seealso cref="ZlibStream.CompressString(string)">ZlibStream.CompressString(string)</seealso>
|
||||
///
|
||||
/// <param name="s">
|
||||
/// A string to compress. The string will first be encoded
|
||||
/// using UTF8, then compressed.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The string in compressed form</returns>
|
||||
public static byte[] CompressString(String s)
|
||||
{
|
||||
using (var ms = new System.IO.MemoryStream())
|
||||
{
|
||||
System.IO.Stream compressor =
|
||||
new DeflateStream(ms, CompressionMode.Compress, CompressionLevel.BestCompression);
|
||||
ZlibBaseStream.CompressString(s, compressor);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compress a byte array into a new byte array using DEFLATE.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Uncompress it with <see cref="DeflateStream.UncompressBuffer(byte[])"/>.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <seealso cref="DeflateStream.CompressString(string)">DeflateStream.CompressString(string)</seealso>
|
||||
/// <seealso cref="DeflateStream.UncompressBuffer(byte[])">DeflateStream.UncompressBuffer(byte[])</seealso>
|
||||
/// <seealso cref="GZipStream.CompressBuffer(byte[])">GZipStream.CompressBuffer(byte[])</seealso>
|
||||
/// <seealso cref="ZlibStream.CompressBuffer(byte[])">ZlibStream.CompressBuffer(byte[])</seealso>
|
||||
///
|
||||
/// <param name="b">
|
||||
/// A buffer to compress.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The data in compressed form</returns>
|
||||
public static byte[] CompressBuffer(byte[] b)
|
||||
{
|
||||
using (var ms = new System.IO.MemoryStream())
|
||||
{
|
||||
System.IO.Stream compressor =
|
||||
new DeflateStream( ms, CompressionMode.Compress, CompressionLevel.BestCompression );
|
||||
|
||||
ZlibBaseStream.CompressBuffer(b, compressor);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Uncompress a DEFLATE'd byte array into a single string.
|
||||
/// </summary>
|
||||
///
|
||||
/// <seealso cref="DeflateStream.CompressString(String)">DeflateStream.CompressString(String)</seealso>
|
||||
/// <seealso cref="DeflateStream.UncompressBuffer(byte[])">DeflateStream.UncompressBuffer(byte[])</seealso>
|
||||
/// <seealso cref="GZipStream.UncompressString(byte[])">GZipStream.UncompressString(byte[])</seealso>
|
||||
/// <seealso cref="ZlibStream.UncompressString(byte[])">ZlibStream.UncompressString(byte[])</seealso>
|
||||
///
|
||||
/// <param name="compressed">
|
||||
/// A buffer containing DEFLATE-compressed data.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The uncompressed string</returns>
|
||||
public static String UncompressString(byte[] compressed)
|
||||
{
|
||||
using (var input = new System.IO.MemoryStream(compressed))
|
||||
{
|
||||
System.IO.Stream decompressor =
|
||||
new DeflateStream(input, CompressionMode.Decompress);
|
||||
|
||||
return ZlibBaseStream.UncompressString(compressed, decompressor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Uncompress a DEFLATE'd byte array into a byte array.
|
||||
/// </summary>
|
||||
///
|
||||
/// <seealso cref="DeflateStream.CompressBuffer(byte[])">DeflateStream.CompressBuffer(byte[])</seealso>
|
||||
/// <seealso cref="DeflateStream.UncompressString(byte[])">DeflateStream.UncompressString(byte[])</seealso>
|
||||
/// <seealso cref="GZipStream.UncompressBuffer(byte[])">GZipStream.UncompressBuffer(byte[])</seealso>
|
||||
/// <seealso cref="ZlibStream.UncompressBuffer(byte[])">ZlibStream.UncompressBuffer(byte[])</seealso>
|
||||
///
|
||||
/// <param name="compressed">
|
||||
/// A buffer containing data that has been compressed with DEFLATE.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The data in uncompressed form</returns>
|
||||
public static byte[] UncompressBuffer(byte[] compressed)
|
||||
{
|
||||
using (var input = new System.IO.MemoryStream(compressed))
|
||||
{
|
||||
System.IO.Stream decompressor =
|
||||
new DeflateStream( input, CompressionMode.Decompress );
|
||||
|
||||
return ZlibBaseStream.UncompressBuffer(compressed, decompressor);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,436 @@
|
||||
// Inftree.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-October-28 12:43:54>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines classes used in decompression. This code is derived
|
||||
// from the jzlib implementation of zlib. In keeping with the license for jzlib,
|
||||
// the copyright to that code is below.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// This program is based on zlib-1.1.3; credit to authors
|
||||
// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
|
||||
// and contributors of zlib.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
using System;
|
||||
namespace Ionic.Zlib
|
||||
{
|
||||
|
||||
sealed class InfTree
|
||||
{
|
||||
|
||||
private const int MANY = 1440;
|
||||
|
||||
private const int Z_OK = 0;
|
||||
private const int Z_STREAM_END = 1;
|
||||
private const int Z_NEED_DICT = 2;
|
||||
private const int Z_ERRNO = - 1;
|
||||
private const int Z_STREAM_ERROR = - 2;
|
||||
private const int Z_DATA_ERROR = - 3;
|
||||
private const int Z_MEM_ERROR = - 4;
|
||||
private const int Z_BUF_ERROR = - 5;
|
||||
private const int Z_VERSION_ERROR = - 6;
|
||||
|
||||
internal const int fixed_bl = 9;
|
||||
internal const int fixed_bd = 5;
|
||||
|
||||
//UPGRADE_NOTE: Final was removed from the declaration of 'fixed_tl'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
|
||||
internal static readonly int[] fixed_tl = new int[]{96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9, 192, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 160, 0, 8, 0, 0, 8, 128, 0, 8, 64, 0, 9, 224, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 144, 83, 7, 59, 0, 8, 120, 0, 8, 56, 0, 9, 208, 81, 7, 17, 0, 8, 104, 0, 8, 40, 0, 9, 176, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 240, 80, 7, 4, 0, 8, 84, 0, 8, 20, 85, 8, 227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 200, 81, 7, 13, 0, 8, 100, 0, 8, 36, 0, 9, 168, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 232, 80, 7, 8, 0, 8, 92, 0, 8, 28, 0, 9, 152, 84, 7, 83, 0, 8, 124, 0, 8, 60, 0, 9, 216, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, 9, 184, 0, 8, 12, 0, 8, 140, 0, 8, 76, 0, 9, 248, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, 83, 7, 35, 0, 8, 114, 0, 8, 50, 0, 9, 196, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 164, 0, 8, 2, 0, 8, 130, 0, 8, 66, 0, 9, 228, 80, 7, 7, 0, 8, 90, 0, 8, 26, 0, 9, 148, 84, 7, 67, 0, 8, 122, 0, 8, 58, 0, 9, 212, 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9, 180, 0, 8, 10, 0, 8, 138, 0, 8, 74, 0, 9, 244, 80, 7, 5, 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 204, 81, 7, 15, 0, 8, 102, 0, 8, 38, 0, 9, 172, 0, 8, 6, 0, 8, 134, 0, 8, 70, 0, 9, 236, 80, 7, 9, 0, 8, 94, 0, 8, 30, 0, 9, 156, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9, 220, 82, 7, 27, 0, 8, 110, 0, 8, 46, 0, 9, 188, 0, 8, 14, 0, 8, 142, 0, 8, 78, 0, 9, 252, 96, 7, 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, 82, 7, 31, 0, 8, 113, 0, 8, 49, 0, 9, 194, 80, 7, 10, 0, 8, 97, 0, 8, 33, 0, 9, 162, 0, 8, 1, 0, 8, 129, 0, 8, 65, 0, 9, 226, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9, 146, 83, 7, 59, 0, 8, 121, 0, 8, 57, 0, 9, 210, 81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 178, 0, 8, 9, 0, 8, 137, 0, 8, 73, 0, 9, 242, 80, 7, 4, 0, 8, 85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, 117, 0, 8, 53, 0, 9, 202, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 170, 0, 8, 5, 0, 8, 133, 0, 8, 69, 0, 9, 234, 80, 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 154, 84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, 218, 82, 7, 23, 0, 8, 109, 0, 8, 45, 0, 9, 186,
|
||||
0, 8, 13, 0, 8, 141, 0, 8, 77, 0, 9, 250, 80, 7, 3, 0, 8, 83, 0, 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 198, 81, 7, 11, 0, 8, 99, 0, 8, 35, 0, 9, 166, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 230, 80, 7, 7, 0, 8, 91, 0, 8, 27, 0, 9, 150, 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 214, 82, 7, 19, 0, 8, 107, 0, 8, 43, 0, 9, 182, 0, 8, 11, 0, 8, 139, 0, 8, 75, 0, 9, 246, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, 7, 51, 0, 8, 119, 0, 8, 55, 0, 9, 206, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 174, 0, 8, 7, 0, 8, 135, 0, 8, 71, 0, 9, 238, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 158, 84, 7, 99, 0, 8, 127, 0, 8, 63, 0, 9, 222, 82, 7, 27, 0, 8, 111, 0, 8, 47, 0, 9, 190, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 254, 96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9, 193, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 161, 0, 8, 0, 0, 8, 128, 0, 8, 64, 0, 9, 225, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 145, 83, 7, 59, 0, 8, 120, 0, 8, 56, 0, 9, 209, 81, 7, 17, 0, 8, 104, 0, 8, 40, 0, 9, 177, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 241, 80, 7, 4, 0, 8, 84, 0, 8, 20, 85, 8, 227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 201, 81, 7, 13, 0, 8, 100, 0, 8, 36, 0, 9, 169, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 233, 80, 7, 8, 0, 8, 92, 0, 8, 28, 0, 9, 153, 84, 7, 83, 0, 8, 124, 0, 8, 60, 0, 9, 217, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, 9, 185, 0, 8, 12, 0, 8, 140, 0, 8, 76, 0, 9, 249, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, 83, 7, 35, 0, 8, 114, 0, 8, 50, 0, 9, 197, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 165, 0, 8, 2, 0, 8, 130, 0, 8, 66, 0, 9, 229, 80, 7, 7, 0, 8, 90, 0, 8, 26, 0, 9, 149, 84, 7, 67, 0, 8, 122, 0, 8, 58, 0, 9, 213, 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9, 181, 0, 8, 10, 0, 8, 138, 0, 8, 74, 0, 9, 245, 80, 7, 5, 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 205, 81, 7, 15, 0, 8, 102, 0, 8, 38, 0, 9, 173, 0, 8, 6, 0, 8, 134, 0, 8, 70, 0, 9, 237, 80, 7, 9, 0, 8, 94, 0, 8, 30, 0, 9, 157, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9, 221, 82, 7, 27, 0, 8, 110, 0, 8, 46, 0, 9, 189, 0, 8,
|
||||
14, 0, 8, 142, 0, 8, 78, 0, 9, 253, 96, 7, 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, 82, 7, 31, 0, 8, 113, 0, 8, 49, 0, 9, 195, 80, 7, 10, 0, 8, 97, 0, 8, 33, 0, 9, 163, 0, 8, 1, 0, 8, 129, 0, 8, 65, 0, 9, 227, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9, 147, 83, 7, 59, 0, 8, 121, 0, 8, 57, 0, 9, 211, 81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 179, 0, 8, 9, 0, 8, 137, 0, 8, 73, 0, 9, 243, 80, 7, 4, 0, 8, 85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, 117, 0, 8, 53, 0, 9, 203, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 171, 0, 8, 5, 0, 8, 133, 0, 8, 69, 0, 9, 235, 80, 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 155, 84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, 219, 82, 7, 23, 0, 8, 109, 0, 8, 45, 0, 9, 187, 0, 8, 13, 0, 8, 141, 0, 8, 77, 0, 9, 251, 80, 7, 3, 0, 8, 83, 0, 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 199, 81, 7, 11, 0, 8, 99, 0, 8, 35, 0, 9, 167, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 231, 80, 7, 7, 0, 8, 91, 0, 8, 27, 0, 9, 151, 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 215, 82, 7, 19, 0, 8, 107, 0, 8, 43, 0, 9, 183, 0, 8, 11, 0, 8, 139, 0, 8, 75, 0, 9, 247, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, 7, 51, 0, 8, 119, 0, 8, 55, 0, 9, 207, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 175, 0, 8, 7, 0, 8, 135, 0, 8, 71, 0, 9, 239, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 159, 84, 7, 99, 0, 8, 127, 0, 8, 63, 0, 9, 223, 82, 7, 27, 0, 8, 111, 0, 8, 47, 0, 9, 191, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 255};
|
||||
//UPGRADE_NOTE: Final was removed from the declaration of 'fixed_td'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
|
||||
internal static readonly int[] fixed_td = new int[]{80, 5, 1, 87, 5, 257, 83, 5, 17, 91, 5, 4097, 81, 5, 5, 89, 5, 1025, 85, 5, 65, 93, 5, 16385, 80, 5, 3, 88, 5, 513, 84, 5, 33, 92, 5, 8193, 82, 5, 9, 90, 5, 2049, 86, 5, 129, 192, 5, 24577, 80, 5, 2, 87, 5, 385, 83, 5, 25, 91, 5, 6145, 81, 5, 7, 89, 5, 1537, 85, 5, 97, 93, 5, 24577, 80, 5, 4, 88, 5, 769, 84, 5, 49, 92, 5, 12289, 82, 5, 13, 90, 5, 3073, 86, 5, 193, 192, 5, 24577};
|
||||
|
||||
// Tables for deflate from PKZIP's appnote.txt.
|
||||
//UPGRADE_NOTE: Final was removed from the declaration of 'cplens'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
|
||||
internal static readonly int[] cplens = new int[]{3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
|
||||
|
||||
// see note #13 above about 258
|
||||
//UPGRADE_NOTE: Final was removed from the declaration of 'cplext'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
|
||||
internal static readonly int[] cplext = new int[]{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112};
|
||||
|
||||
//UPGRADE_NOTE: Final was removed from the declaration of 'cpdist'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
|
||||
internal static readonly int[] cpdist = new int[]{1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577};
|
||||
|
||||
//UPGRADE_NOTE: Final was removed from the declaration of 'cpdext'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
|
||||
internal static readonly int[] cpdext = new int[]{0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13};
|
||||
|
||||
// If BMAX needs to be larger than 16, then h and x[] should be uLong.
|
||||
internal const int BMAX = 15; // maximum bit length of any code
|
||||
|
||||
internal int[] hn = null; // hufts used in space
|
||||
internal int[] v = null; // work area for huft_build
|
||||
internal int[] c = null; // bit length count table
|
||||
internal int[] r = null; // table entry for structure assignment
|
||||
internal int[] u = null; // table stack
|
||||
internal int[] x = null; // bit offsets, then code stack
|
||||
|
||||
private int huft_build(int[] b, int bindex, int n, int s, int[] d, int[] e, int[] t, int[] m, int[] hp, int[] hn, int[] v)
|
||||
{
|
||||
// Given a list of code lengths and a maximum table size, make a set of
|
||||
// tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR
|
||||
// if the given code set is incomplete (the tables are still built in this
|
||||
// case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of
|
||||
// lengths), or Z_MEM_ERROR if not enough memory.
|
||||
|
||||
int a; // counter for codes of length k
|
||||
int f; // i repeats in table every f entries
|
||||
int g; // maximum code length
|
||||
int h; // table level
|
||||
int i; // counter, current code
|
||||
int j; // counter
|
||||
int k; // number of bits in current code
|
||||
int l; // bits per table (returned in m)
|
||||
int mask; // (1 << w) - 1, to avoid cc -O bug on HP
|
||||
int p; // pointer into c[], b[], or v[]
|
||||
int q; // points to current table
|
||||
int w; // bits before this table == (l * h)
|
||||
int xp; // pointer into x
|
||||
int y; // number of dummy codes added
|
||||
int z; // number of entries in current table
|
||||
|
||||
// Generate counts for each bit length
|
||||
|
||||
p = 0; i = n;
|
||||
do
|
||||
{
|
||||
c[b[bindex + p]]++; p++; i--; // assume all entries <= BMAX
|
||||
}
|
||||
while (i != 0);
|
||||
|
||||
if (c[0] == n)
|
||||
{
|
||||
// null input--all zero length codes
|
||||
t[0] = - 1;
|
||||
m[0] = 0;
|
||||
return Z_OK;
|
||||
}
|
||||
|
||||
// Find minimum and maximum length, bound *m by those
|
||||
l = m[0];
|
||||
for (j = 1; j <= BMAX; j++)
|
||||
if (c[j] != 0)
|
||||
break;
|
||||
k = j; // minimum code length
|
||||
if (l < j)
|
||||
{
|
||||
l = j;
|
||||
}
|
||||
for (i = BMAX; i != 0; i--)
|
||||
{
|
||||
if (c[i] != 0)
|
||||
break;
|
||||
}
|
||||
g = i; // maximum code length
|
||||
if (l > i)
|
||||
{
|
||||
l = i;
|
||||
}
|
||||
m[0] = l;
|
||||
|
||||
// Adjust last length count to fill out codes, if needed
|
||||
for (y = 1 << j; j < i; j++, y <<= 1)
|
||||
{
|
||||
if ((y -= c[j]) < 0)
|
||||
{
|
||||
return Z_DATA_ERROR;
|
||||
}
|
||||
}
|
||||
if ((y -= c[i]) < 0)
|
||||
{
|
||||
return Z_DATA_ERROR;
|
||||
}
|
||||
c[i] += y;
|
||||
|
||||
// Generate starting offsets into the value table for each length
|
||||
x[1] = j = 0;
|
||||
p = 1; xp = 2;
|
||||
while (--i != 0)
|
||||
{
|
||||
// note that i == g from above
|
||||
x[xp] = (j += c[p]);
|
||||
xp++;
|
||||
p++;
|
||||
}
|
||||
|
||||
// Make a table of values in order of bit lengths
|
||||
i = 0; p = 0;
|
||||
do
|
||||
{
|
||||
if ((j = b[bindex + p]) != 0)
|
||||
{
|
||||
v[x[j]++] = i;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
while (++i < n);
|
||||
n = x[g]; // set n to length of v
|
||||
|
||||
// Generate the Huffman codes and for each, make the table entries
|
||||
x[0] = i = 0; // first Huffman code is zero
|
||||
p = 0; // grab values in bit order
|
||||
h = - 1; // no tables yet--level -1
|
||||
w = - l; // bits decoded == (l * h)
|
||||
u[0] = 0; // just to keep compilers happy
|
||||
q = 0; // ditto
|
||||
z = 0; // ditto
|
||||
|
||||
// go through the bit lengths (k already is bits in shortest code)
|
||||
for (; k <= g; k++)
|
||||
{
|
||||
a = c[k];
|
||||
while (a-- != 0)
|
||||
{
|
||||
// here i is the Huffman code of length k bits for value *p
|
||||
// make tables up to required level
|
||||
while (k > w + l)
|
||||
{
|
||||
h++;
|
||||
w += l; // previous table always l bits
|
||||
// compute minimum size table less than or equal to l bits
|
||||
z = g - w;
|
||||
z = (z > l)?l:z; // table size upper limit
|
||||
if ((f = 1 << (j = k - w)) > a + 1)
|
||||
{
|
||||
// try a k-w bit table
|
||||
// too few codes for k-w bit table
|
||||
f -= (a + 1); // deduct codes from patterns left
|
||||
xp = k;
|
||||
if (j < z)
|
||||
{
|
||||
while (++j < z)
|
||||
{
|
||||
// try smaller tables up to z bits
|
||||
if ((f <<= 1) <= c[++xp])
|
||||
break; // enough codes to use up j bits
|
||||
f -= c[xp]; // else deduct codes from patterns
|
||||
}
|
||||
}
|
||||
}
|
||||
z = 1 << j; // table entries for j-bit table
|
||||
|
||||
// allocate new table
|
||||
if (hn[0] + z > MANY)
|
||||
{
|
||||
// (note: doesn't matter for fixed)
|
||||
return Z_DATA_ERROR; // overflow of MANY
|
||||
}
|
||||
u[h] = q = hn[0]; // DEBUG
|
||||
hn[0] += z;
|
||||
|
||||
// connect to last table, if there is one
|
||||
if (h != 0)
|
||||
{
|
||||
x[h] = i; // save pattern for backing up
|
||||
r[0] = (sbyte) j; // bits in this table
|
||||
r[1] = (sbyte) l; // bits to dump before this table
|
||||
j = SharedUtils.URShift(i, (w - l));
|
||||
r[2] = (int) (q - u[h - 1] - j); // offset to this table
|
||||
Array.Copy(r, 0, hp, (u[h - 1] + j) * 3, 3); // connect to last table
|
||||
}
|
||||
else
|
||||
{
|
||||
t[0] = q; // first table is returned result
|
||||
}
|
||||
}
|
||||
|
||||
// set up table entry in r
|
||||
r[1] = (sbyte) (k - w);
|
||||
if (p >= n)
|
||||
{
|
||||
r[0] = 128 + 64; // out of values--invalid code
|
||||
}
|
||||
else if (v[p] < s)
|
||||
{
|
||||
r[0] = (sbyte) (v[p] < 256?0:32 + 64); // 256 is end-of-block
|
||||
r[2] = v[p++]; // simple code is just the value
|
||||
}
|
||||
else
|
||||
{
|
||||
r[0] = (sbyte) (e[v[p] - s] + 16 + 64); // non-simple--look up in lists
|
||||
r[2] = d[v[p++] - s];
|
||||
}
|
||||
|
||||
// fill code-like entries with r
|
||||
f = 1 << (k - w);
|
||||
for (j = SharedUtils.URShift(i, w); j < z; j += f)
|
||||
{
|
||||
Array.Copy(r, 0, hp, (q + j) * 3, 3);
|
||||
}
|
||||
|
||||
// backwards increment the k-bit code i
|
||||
for (j = 1 << (k - 1); (i & j) != 0; j = SharedUtils.URShift(j, 1))
|
||||
{
|
||||
i ^= j;
|
||||
}
|
||||
i ^= j;
|
||||
|
||||
// backup over finished tables
|
||||
mask = (1 << w) - 1; // needed on HP, cc -O bug
|
||||
while ((i & mask) != x[h])
|
||||
{
|
||||
h--; // don't need to update q
|
||||
w -= l;
|
||||
mask = (1 << w) - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return Z_BUF_ERROR if we were given an incomplete table
|
||||
return y != 0 && g != 1?Z_BUF_ERROR:Z_OK;
|
||||
}
|
||||
|
||||
internal int inflate_trees_bits(int[] c, int[] bb, int[] tb, int[] hp, ZlibCodec z)
|
||||
{
|
||||
int result;
|
||||
initWorkArea(19);
|
||||
hn[0] = 0;
|
||||
result = huft_build(c, 0, 19, 19, null, null, tb, bb, hp, hn, v);
|
||||
|
||||
if (result == Z_DATA_ERROR)
|
||||
{
|
||||
z.Message = "oversubscribed dynamic bit lengths tree";
|
||||
}
|
||||
else if (result == Z_BUF_ERROR || bb[0] == 0)
|
||||
{
|
||||
z.Message = "incomplete dynamic bit lengths tree";
|
||||
result = Z_DATA_ERROR;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal int inflate_trees_dynamic(int nl, int nd, int[] c, int[] bl, int[] bd, int[] tl, int[] td, int[] hp, ZlibCodec z)
|
||||
{
|
||||
int result;
|
||||
|
||||
// build literal/length tree
|
||||
initWorkArea(288);
|
||||
hn[0] = 0;
|
||||
result = huft_build(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v);
|
||||
if (result != Z_OK || bl[0] == 0)
|
||||
{
|
||||
if (result == Z_DATA_ERROR)
|
||||
{
|
||||
z.Message = "oversubscribed literal/length tree";
|
||||
}
|
||||
else if (result != Z_MEM_ERROR)
|
||||
{
|
||||
z.Message = "incomplete literal/length tree";
|
||||
result = Z_DATA_ERROR;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// build distance tree
|
||||
initWorkArea(288);
|
||||
result = huft_build(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v);
|
||||
|
||||
if (result != Z_OK || (bd[0] == 0 && nl > 257))
|
||||
{
|
||||
if (result == Z_DATA_ERROR)
|
||||
{
|
||||
z.Message = "oversubscribed distance tree";
|
||||
}
|
||||
else if (result == Z_BUF_ERROR)
|
||||
{
|
||||
z.Message = "incomplete distance tree";
|
||||
result = Z_DATA_ERROR;
|
||||
}
|
||||
else if (result != Z_MEM_ERROR)
|
||||
{
|
||||
z.Message = "empty distance tree with lengths";
|
||||
result = Z_DATA_ERROR;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return Z_OK;
|
||||
}
|
||||
|
||||
internal static int inflate_trees_fixed(int[] bl, int[] bd, int[][] tl, int[][] td, ZlibCodec z)
|
||||
{
|
||||
bl[0] = fixed_bl;
|
||||
bd[0] = fixed_bd;
|
||||
tl[0] = fixed_tl;
|
||||
td[0] = fixed_td;
|
||||
return Z_OK;
|
||||
}
|
||||
|
||||
private void initWorkArea(int vsize)
|
||||
{
|
||||
if (hn == null)
|
||||
{
|
||||
hn = new int[1];
|
||||
v = new int[vsize];
|
||||
c = new int[BMAX + 1];
|
||||
r = new int[3];
|
||||
u = new int[BMAX];
|
||||
x = new int[BMAX + 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (v.Length < vsize)
|
||||
{
|
||||
v = new int[vsize];
|
||||
}
|
||||
Array.Clear(v,0,vsize);
|
||||
Array.Clear(c,0,BMAX+1);
|
||||
r[0]=0; r[1]=0; r[2]=0;
|
||||
// for(int i=0; i<BMAX; i++){u[i]=0;}
|
||||
//Array.Copy(c, 0, u, 0, BMAX);
|
||||
Array.Clear(u,0,BMAX);
|
||||
// for(int i=0; i<BMAX+1; i++){x[i]=0;}
|
||||
//Array.Copy(c, 0, x, 0, BMAX + 1);
|
||||
Array.Clear(x,0,BMAX+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,423 @@
|
||||
// Tree.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-October-28 13:29:50>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines classes for zlib compression and
|
||||
// decompression. This code is derived from the jzlib implementation of
|
||||
// zlib. In keeping with the license for jzlib, the copyright to that
|
||||
// code is below.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// This program is based on zlib-1.1.3; credit to authors
|
||||
// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
|
||||
// and contributors of zlib.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
|
||||
using System;
|
||||
|
||||
namespace Ionic.Zlib
|
||||
{
|
||||
sealed class Tree
|
||||
{
|
||||
private static readonly int HEAP_SIZE = (2 * InternalConstants.L_CODES + 1);
|
||||
|
||||
// extra bits for each length code
|
||||
internal static readonly int[] ExtraLengthBits = new int[]
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
|
||||
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
|
||||
};
|
||||
|
||||
// extra bits for each distance code
|
||||
internal static readonly int[] ExtraDistanceBits = new int[]
|
||||
{
|
||||
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
|
||||
7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
|
||||
};
|
||||
|
||||
// extra bits for each bit length code
|
||||
internal static readonly int[] extra_blbits = new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7};
|
||||
|
||||
internal static readonly sbyte[] bl_order = new sbyte[]{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
|
||||
|
||||
|
||||
// The lengths of the bit length codes are sent in order of decreasing
|
||||
// probability, to avoid transmitting the lengths for unused bit
|
||||
// length codes.
|
||||
|
||||
internal const int Buf_size = 8 * 2;
|
||||
|
||||
// see definition of array dist_code below
|
||||
//internal const int DIST_CODE_LEN = 512;
|
||||
|
||||
private static readonly sbyte[] _dist_code = new sbyte[]
|
||||
{
|
||||
0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,
|
||||
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
|
||||
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
|
||||
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
|
||||
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
|
||||
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
|
||||
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
|
||||
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
|
||||
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
|
||||
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
|
||||
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
|
||||
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
0, 0, 16, 17, 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
|
||||
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
|
||||
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
|
||||
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
|
||||
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
|
||||
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
|
||||
};
|
||||
|
||||
internal static readonly sbyte[] LengthCode = new sbyte[]
|
||||
{
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11,
|
||||
12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17,
|
||||
18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19,
|
||||
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
|
||||
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
|
||||
25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
|
||||
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
|
||||
};
|
||||
|
||||
|
||||
internal static readonly int[] LengthBase = new int[]
|
||||
{
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28,
|
||||
32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 0
|
||||
};
|
||||
|
||||
|
||||
internal static readonly int[] DistanceBase = new int[]
|
||||
{
|
||||
0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192,
|
||||
256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576
|
||||
};
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Map from a distance to a distance code.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// No side effects. _dist_code[256] and _dist_code[257] are never used.
|
||||
/// </remarks>
|
||||
internal static int DistanceCode(int dist)
|
||||
{
|
||||
return (dist < 256)
|
||||
? _dist_code[dist]
|
||||
: _dist_code[256 + SharedUtils.URShift(dist, 7)];
|
||||
}
|
||||
|
||||
internal short[] dyn_tree; // the dynamic tree
|
||||
internal int max_code; // largest code with non zero frequency
|
||||
internal StaticTree staticTree; // the corresponding static tree
|
||||
|
||||
// Compute the optimal bit lengths for a tree and update the total bit length
|
||||
// for the current block.
|
||||
// IN assertion: the fields freq and dad are set, heap[heap_max] and
|
||||
// above are the tree nodes sorted by increasing frequency.
|
||||
// OUT assertions: the field len is set to the optimal bit length, the
|
||||
// array bl_count contains the frequencies for each bit length.
|
||||
// The length opt_len is updated; static_len is also updated if stree is
|
||||
// not null.
|
||||
internal void gen_bitlen(DeflateManager s)
|
||||
{
|
||||
short[] tree = dyn_tree;
|
||||
short[] stree = staticTree.treeCodes;
|
||||
int[] extra = staticTree.extraBits;
|
||||
int base_Renamed = staticTree.extraBase;
|
||||
int max_length = staticTree.maxLength;
|
||||
int h; // heap index
|
||||
int n, m; // iterate over the tree elements
|
||||
int bits; // bit length
|
||||
int xbits; // extra bits
|
||||
short f; // frequency
|
||||
int overflow = 0; // number of elements with bit length too large
|
||||
|
||||
for (bits = 0; bits <= InternalConstants.MAX_BITS; bits++)
|
||||
s.bl_count[bits] = 0;
|
||||
|
||||
// In a first pass, compute the optimal bit lengths (which may
|
||||
// overflow in the case of the bit length tree).
|
||||
tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap
|
||||
|
||||
for (h = s.heap_max + 1; h < HEAP_SIZE; h++)
|
||||
{
|
||||
n = s.heap[h];
|
||||
bits = tree[tree[n * 2 + 1] * 2 + 1] + 1;
|
||||
if (bits > max_length)
|
||||
{
|
||||
bits = max_length; overflow++;
|
||||
}
|
||||
tree[n * 2 + 1] = (short) bits;
|
||||
// We overwrite tree[n*2+1] which is no longer needed
|
||||
|
||||
if (n > max_code)
|
||||
continue; // not a leaf node
|
||||
|
||||
s.bl_count[bits]++;
|
||||
xbits = 0;
|
||||
if (n >= base_Renamed)
|
||||
xbits = extra[n - base_Renamed];
|
||||
f = tree[n * 2];
|
||||
s.opt_len += f * (bits + xbits);
|
||||
if (stree != null)
|
||||
s.static_len += f * (stree[n * 2 + 1] + xbits);
|
||||
}
|
||||
if (overflow == 0)
|
||||
return ;
|
||||
|
||||
// This happens for example on obj2 and pic of the Calgary corpus
|
||||
// Find the first bit length which could increase:
|
||||
do
|
||||
{
|
||||
bits = max_length - 1;
|
||||
while (s.bl_count[bits] == 0)
|
||||
bits--;
|
||||
s.bl_count[bits]--; // move one leaf down the tree
|
||||
s.bl_count[bits + 1] = (short) (s.bl_count[bits + 1] + 2); // move one overflow item as its brother
|
||||
s.bl_count[max_length]--;
|
||||
// The brother of the overflow item also moves one step up,
|
||||
// but this does not affect bl_count[max_length]
|
||||
overflow -= 2;
|
||||
}
|
||||
while (overflow > 0);
|
||||
|
||||
for (bits = max_length; bits != 0; bits--)
|
||||
{
|
||||
n = s.bl_count[bits];
|
||||
while (n != 0)
|
||||
{
|
||||
m = s.heap[--h];
|
||||
if (m > max_code)
|
||||
continue;
|
||||
if (tree[m * 2 + 1] != bits)
|
||||
{
|
||||
s.opt_len = (int) (s.opt_len + ((long) bits - (long) tree[m * 2 + 1]) * (long) tree[m * 2]);
|
||||
tree[m * 2 + 1] = (short) bits;
|
||||
}
|
||||
n--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Construct one Huffman tree and assigns the code bit strings and lengths.
|
||||
// Update the total bit length for the current block.
|
||||
// IN assertion: the field freq is set for all tree elements.
|
||||
// OUT assertions: the fields len and code are set to the optimal bit length
|
||||
// and corresponding code. The length opt_len is updated; static_len is
|
||||
// also updated if stree is not null. The field max_code is set.
|
||||
internal void build_tree(DeflateManager s)
|
||||
{
|
||||
short[] tree = dyn_tree;
|
||||
short[] stree = staticTree.treeCodes;
|
||||
int elems = staticTree.elems;
|
||||
int n, m; // iterate over heap elements
|
||||
int max_code = -1; // largest code with non zero frequency
|
||||
int node; // new node being created
|
||||
|
||||
// Construct the initial heap, with least frequent element in
|
||||
// heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
|
||||
// heap[0] is not used.
|
||||
s.heap_len = 0;
|
||||
s.heap_max = HEAP_SIZE;
|
||||
|
||||
for (n = 0; n < elems; n++)
|
||||
{
|
||||
if (tree[n * 2] != 0)
|
||||
{
|
||||
s.heap[++s.heap_len] = max_code = n;
|
||||
s.depth[n] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
tree[n * 2 + 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// The pkzip format requires that at least one distance code exists,
|
||||
// and that at least one bit should be sent even if there is only one
|
||||
// possible code. So to avoid special checks later on we force at least
|
||||
// two codes of non zero frequency.
|
||||
while (s.heap_len < 2)
|
||||
{
|
||||
node = s.heap[++s.heap_len] = (max_code < 2?++max_code:0);
|
||||
tree[node * 2] = 1;
|
||||
s.depth[node] = 0;
|
||||
s.opt_len--;
|
||||
if (stree != null)
|
||||
s.static_len -= stree[node * 2 + 1];
|
||||
// node is 0 or 1 so it does not have extra bits
|
||||
}
|
||||
this.max_code = max_code;
|
||||
|
||||
// The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
|
||||
// establish sub-heaps of increasing lengths:
|
||||
|
||||
for (n = s.heap_len / 2; n >= 1; n--)
|
||||
s.pqdownheap(tree, n);
|
||||
|
||||
// Construct the Huffman tree by repeatedly combining the least two
|
||||
// frequent nodes.
|
||||
|
||||
node = elems; // next internal node of the tree
|
||||
do
|
||||
{
|
||||
// n = node of least frequency
|
||||
n = s.heap[1];
|
||||
s.heap[1] = s.heap[s.heap_len--];
|
||||
s.pqdownheap(tree, 1);
|
||||
m = s.heap[1]; // m = node of next least frequency
|
||||
|
||||
s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency
|
||||
s.heap[--s.heap_max] = m;
|
||||
|
||||
// Create a new node father of n and m
|
||||
tree[node * 2] = unchecked((short) (tree[n * 2] + tree[m * 2]));
|
||||
s.depth[node] = (sbyte) (System.Math.Max((byte) s.depth[n], (byte) s.depth[m]) + 1);
|
||||
tree[n * 2 + 1] = tree[m * 2 + 1] = (short) node;
|
||||
|
||||
// and insert the new node in the heap
|
||||
s.heap[1] = node++;
|
||||
s.pqdownheap(tree, 1);
|
||||
}
|
||||
while (s.heap_len >= 2);
|
||||
|
||||
s.heap[--s.heap_max] = s.heap[1];
|
||||
|
||||
// At this point, the fields freq and dad are set. We can now
|
||||
// generate the bit lengths.
|
||||
|
||||
gen_bitlen(s);
|
||||
|
||||
// The field len is now set, we can generate the bit codes
|
||||
gen_codes(tree, max_code, s.bl_count);
|
||||
}
|
||||
|
||||
// Generate the codes for a given tree and bit counts (which need not be
|
||||
// optimal).
|
||||
// IN assertion: the array bl_count contains the bit length statistics for
|
||||
// the given tree and the field len is set for all tree elements.
|
||||
// OUT assertion: the field code is set for all tree elements of non
|
||||
// zero code length.
|
||||
internal static void gen_codes(short[] tree, int max_code, short[] bl_count)
|
||||
{
|
||||
short[] next_code = new short[InternalConstants.MAX_BITS + 1]; // next code value for each bit length
|
||||
short code = 0; // running code value
|
||||
int bits; // bit index
|
||||
int n; // code index
|
||||
|
||||
// The distribution counts are first used to generate the code values
|
||||
// without bit reversal.
|
||||
for (bits = 1; bits <= InternalConstants.MAX_BITS; bits++)
|
||||
unchecked {
|
||||
next_code[bits] = code = (short) ((code + bl_count[bits - 1]) << 1);
|
||||
}
|
||||
|
||||
// Check that the bit counts in bl_count are consistent. The last code
|
||||
// must be all ones.
|
||||
//Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
|
||||
// "inconsistent bit counts");
|
||||
//Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
|
||||
|
||||
for (n = 0; n <= max_code; n++)
|
||||
{
|
||||
int len = tree[n * 2 + 1];
|
||||
if (len == 0)
|
||||
continue;
|
||||
// Now reverse the bits
|
||||
tree[n * 2] = unchecked((short) (bi_reverse(next_code[len]++, len)));
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse the first len bits of a code, using straightforward code (a faster
|
||||
// method would use a table)
|
||||
// IN assertion: 1 <= len <= 15
|
||||
internal static int bi_reverse(int code, int len)
|
||||
{
|
||||
int res = 0;
|
||||
do
|
||||
{
|
||||
res |= code & 1;
|
||||
code >>= 1; //SharedUtils.URShift(code, 1);
|
||||
res <<= 1;
|
||||
}
|
||||
while (--len > 0);
|
||||
return res >> 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,546 @@
|
||||
// Zlib.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2011 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-August-03 19:52:28>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines classes for ZLIB compression and
|
||||
// decompression. This code is derived from the jzlib implementation of
|
||||
// zlib, but significantly modified. The object model is not the same,
|
||||
// and many of the behaviors are new or different. Nonetheless, in
|
||||
// keeping with the license for jzlib, the copyright to that code is
|
||||
// included below.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// The following notice applies to jzlib:
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// jzlib is based on zlib-1.1.3.
|
||||
//
|
||||
// The following notice applies to zlib:
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler
|
||||
//
|
||||
// The ZLIB software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
// Jean-loup Gailly jloup@gzip.org
|
||||
// Mark Adler madler@alumni.caltech.edu
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
using System;
|
||||
using Interop=System.Runtime.InteropServices;
|
||||
|
||||
namespace Ionic.Zlib
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Describes how to flush the current deflate operation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The different FlushType values are useful when using a Deflate in a streaming application.
|
||||
/// </remarks>
|
||||
public enum FlushType
|
||||
{
|
||||
/// <summary>No flush at all.</summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>Closes the current block, but doesn't flush it to
|
||||
/// the output. Used internally only in hypothetical
|
||||
/// scenarios. This was supposed to be removed by Zlib, but it is
|
||||
/// still in use in some edge cases.
|
||||
/// </summary>
|
||||
Partial,
|
||||
|
||||
/// <summary>
|
||||
/// Use this during compression to specify that all pending output should be
|
||||
/// flushed to the output buffer and the output should be aligned on a byte
|
||||
/// boundary. You might use this in a streaming communication scenario, so that
|
||||
/// the decompressor can get all input data available so far. When using this
|
||||
/// with a ZlibCodec, <c>AvailableBytesIn</c> will be zero after the call if
|
||||
/// enough output space has been provided before the call. Flushing will
|
||||
/// degrade compression and so it should be used only when necessary.
|
||||
/// </summary>
|
||||
Sync,
|
||||
|
||||
/// <summary>
|
||||
/// Use this during compression to specify that all output should be flushed, as
|
||||
/// with <c>FlushType.Sync</c>, but also, the compression state should be reset
|
||||
/// so that decompression can restart from this point if previous compressed
|
||||
/// data has been damaged or if random access is desired. Using
|
||||
/// <c>FlushType.Full</c> too often can significantly degrade the compression.
|
||||
/// </summary>
|
||||
Full,
|
||||
|
||||
/// <summary>Signals the end of the compression/decompression stream.</summary>
|
||||
Finish,
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The compression level to be used when using a DeflateStream or ZlibStream with CompressionMode.Compress.
|
||||
/// </summary>
|
||||
public enum CompressionLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// None means that the data will be simply stored, with no change at all.
|
||||
/// If you are producing ZIPs for use on Mac OSX, be aware that archives produced with CompressionLevel.None
|
||||
/// cannot be opened with the default zip reader. Use a different CompressionLevel.
|
||||
/// </summary>
|
||||
None= 0,
|
||||
/// <summary>
|
||||
/// Same as None.
|
||||
/// </summary>
|
||||
Level0 = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The fastest but least effective compression.
|
||||
/// </summary>
|
||||
BestSpeed = 1,
|
||||
|
||||
/// <summary>
|
||||
/// A synonym for BestSpeed.
|
||||
/// </summary>
|
||||
Level1 = 1,
|
||||
|
||||
/// <summary>
|
||||
/// A little slower, but better, than level 1.
|
||||
/// </summary>
|
||||
Level2 = 2,
|
||||
|
||||
/// <summary>
|
||||
/// A little slower, but better, than level 2.
|
||||
/// </summary>
|
||||
Level3 = 3,
|
||||
|
||||
/// <summary>
|
||||
/// A little slower, but better, than level 3.
|
||||
/// </summary>
|
||||
Level4 = 4,
|
||||
|
||||
/// <summary>
|
||||
/// A little slower than level 4, but with better compression.
|
||||
/// </summary>
|
||||
Level5 = 5,
|
||||
|
||||
/// <summary>
|
||||
/// The default compression level, with a good balance of speed and compression efficiency.
|
||||
/// </summary>
|
||||
Default = 6,
|
||||
/// <summary>
|
||||
/// A synonym for Default.
|
||||
/// </summary>
|
||||
Level6 = 6,
|
||||
|
||||
/// <summary>
|
||||
/// Pretty good compression!
|
||||
/// </summary>
|
||||
Level7 = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Better compression than Level7!
|
||||
/// </summary>
|
||||
Level8 = 8,
|
||||
|
||||
/// <summary>
|
||||
/// The "best" compression, where best means greatest reduction in size of the input data stream.
|
||||
/// This is also the slowest compression.
|
||||
/// </summary>
|
||||
BestCompression = 9,
|
||||
|
||||
/// <summary>
|
||||
/// A synonym for BestCompression.
|
||||
/// </summary>
|
||||
Level9 = 9,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes options for how the compression algorithm is executed. Different strategies
|
||||
/// work better on different sorts of data. The strategy parameter can affect the compression
|
||||
/// ratio and the speed of compression but not the correctness of the compresssion.
|
||||
/// </summary>
|
||||
public enum CompressionStrategy
|
||||
{
|
||||
/// <summary>
|
||||
/// The default strategy is probably the best for normal data.
|
||||
/// </summary>
|
||||
Default = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The <c>Filtered</c> strategy is intended to be used most effectively with data produced by a
|
||||
/// filter or predictor. By this definition, filtered data consists mostly of small
|
||||
/// values with a somewhat random distribution. In this case, the compression algorithm
|
||||
/// is tuned to compress them better. The effect of <c>Filtered</c> is to force more Huffman
|
||||
/// coding and less string matching; it is a half-step between <c>Default</c> and <c>HuffmanOnly</c>.
|
||||
/// </summary>
|
||||
Filtered = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Using <c>HuffmanOnly</c> will force the compressor to do Huffman encoding only, with no
|
||||
/// string matching.
|
||||
/// </summary>
|
||||
HuffmanOnly = 2,
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An enum to specify the direction of transcoding - whether to compress or decompress.
|
||||
/// </summary>
|
||||
public enum CompressionMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to specify that the stream should compress the data.
|
||||
/// </summary>
|
||||
Compress= 0,
|
||||
/// <summary>
|
||||
/// Used to specify that the stream should decompress the data.
|
||||
/// </summary>
|
||||
Decompress = 1,
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A general purpose exception class for exceptions in the Zlib library.
|
||||
/// </summary>
|
||||
[Interop.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d0000E")]
|
||||
public class ZlibException : System.Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// The ZlibException class captures exception information generated
|
||||
/// by the Zlib library.
|
||||
/// </summary>
|
||||
public ZlibException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This ctor collects a message attached to the exception.
|
||||
/// </summary>
|
||||
/// <param name="s">the message for the exception.</param>
|
||||
public ZlibException(System.String s)
|
||||
: base(s)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class SharedUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Performs an unsigned bitwise right shift with the specified number
|
||||
/// </summary>
|
||||
/// <param name="number">Number to operate on</param>
|
||||
/// <param name="bits">Ammount of bits to shift</param>
|
||||
/// <returns>The resulting number from the shift operation</returns>
|
||||
public static int URShift(int number, int bits)
|
||||
{
|
||||
return (int)((uint)number >> bits);
|
||||
}
|
||||
|
||||
#if NOT
|
||||
/// <summary>
|
||||
/// Performs an unsigned bitwise right shift with the specified number
|
||||
/// </summary>
|
||||
/// <param name="number">Number to operate on</param>
|
||||
/// <param name="bits">Ammount of bits to shift</param>
|
||||
/// <returns>The resulting number from the shift operation</returns>
|
||||
public static long URShift(long number, int bits)
|
||||
{
|
||||
return (long) ((UInt64)number >> bits);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Reads a number of characters from the current source TextReader and writes
|
||||
/// the data to the target array at the specified index.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="sourceTextReader">The source TextReader to read from</param>
|
||||
/// <param name="target">Contains the array of characteres read from the source TextReader.</param>
|
||||
/// <param name="start">The starting index of the target array.</param>
|
||||
/// <param name="count">The maximum number of characters to read from the source TextReader.</param>
|
||||
///
|
||||
/// <returns>
|
||||
/// The number of characters read. The number will be less than or equal to
|
||||
/// count depending on the data available in the source TextReader. Returns -1
|
||||
/// if the end of the stream is reached.
|
||||
/// </returns>
|
||||
public static System.Int32 ReadInput(System.IO.TextReader sourceTextReader, byte[] target, int start, int count)
|
||||
{
|
||||
// Returns 0 bytes if not enough space in target
|
||||
if (target.Length == 0) return 0;
|
||||
|
||||
char[] charArray = new char[target.Length];
|
||||
int bytesRead = sourceTextReader.Read(charArray, start, count);
|
||||
|
||||
// Returns -1 if EOF
|
||||
if (bytesRead == 0) return -1;
|
||||
|
||||
for (int index = start; index < start + bytesRead; index++)
|
||||
target[index] = (byte)charArray[index];
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
|
||||
internal static byte[] ToByteArray(System.String sourceString)
|
||||
{
|
||||
return System.Text.UTF8Encoding.UTF8.GetBytes(sourceString);
|
||||
}
|
||||
|
||||
|
||||
internal static char[] ToCharArray(byte[] byteArray)
|
||||
{
|
||||
return System.Text.UTF8Encoding.UTF8.GetChars(byteArray);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class InternalConstants
|
||||
{
|
||||
internal static readonly int MAX_BITS = 15;
|
||||
internal static readonly int BL_CODES = 19;
|
||||
internal static readonly int D_CODES = 30;
|
||||
internal static readonly int LITERALS = 256;
|
||||
internal static readonly int LENGTH_CODES = 29;
|
||||
internal static readonly int L_CODES = (LITERALS + 1 + LENGTH_CODES);
|
||||
|
||||
// Bit length codes must not exceed MAX_BL_BITS bits
|
||||
internal static readonly int MAX_BL_BITS = 7;
|
||||
|
||||
// repeat previous bit length 3-6 times (2 bits of repeat count)
|
||||
internal static readonly int REP_3_6 = 16;
|
||||
|
||||
// repeat a zero length 3-10 times (3 bits of repeat count)
|
||||
internal static readonly int REPZ_3_10 = 17;
|
||||
|
||||
// repeat a zero length 11-138 times (7 bits of repeat count)
|
||||
internal static readonly int REPZ_11_138 = 18;
|
||||
|
||||
}
|
||||
|
||||
internal sealed class StaticTree
|
||||
{
|
||||
internal static readonly short[] lengthAndLiteralsTreeCodes = new short[] {
|
||||
12, 8, 140, 8, 76, 8, 204, 8, 44, 8, 172, 8, 108, 8, 236, 8,
|
||||
28, 8, 156, 8, 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, 252, 8,
|
||||
2, 8, 130, 8, 66, 8, 194, 8, 34, 8, 162, 8, 98, 8, 226, 8,
|
||||
18, 8, 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, 114, 8, 242, 8,
|
||||
10, 8, 138, 8, 74, 8, 202, 8, 42, 8, 170, 8, 106, 8, 234, 8,
|
||||
26, 8, 154, 8, 90, 8, 218, 8, 58, 8, 186, 8, 122, 8, 250, 8,
|
||||
6, 8, 134, 8, 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, 230, 8,
|
||||
22, 8, 150, 8, 86, 8, 214, 8, 54, 8, 182, 8, 118, 8, 246, 8,
|
||||
14, 8, 142, 8, 78, 8, 206, 8, 46, 8, 174, 8, 110, 8, 238, 8,
|
||||
30, 8, 158, 8, 94, 8, 222, 8, 62, 8, 190, 8, 126, 8, 254, 8,
|
||||
1, 8, 129, 8, 65, 8, 193, 8, 33, 8, 161, 8, 97, 8, 225, 8,
|
||||
17, 8, 145, 8, 81, 8, 209, 8, 49, 8, 177, 8, 113, 8, 241, 8,
|
||||
9, 8, 137, 8, 73, 8, 201, 8, 41, 8, 169, 8, 105, 8, 233, 8,
|
||||
25, 8, 153, 8, 89, 8, 217, 8, 57, 8, 185, 8, 121, 8, 249, 8,
|
||||
5, 8, 133, 8, 69, 8, 197, 8, 37, 8, 165, 8, 101, 8, 229, 8,
|
||||
21, 8, 149, 8, 85, 8, 213, 8, 53, 8, 181, 8, 117, 8, 245, 8,
|
||||
13, 8, 141, 8, 77, 8, 205, 8, 45, 8, 173, 8, 109, 8, 237, 8,
|
||||
29, 8, 157, 8, 93, 8, 221, 8, 61, 8, 189, 8, 125, 8, 253, 8,
|
||||
19, 9, 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, 211, 9, 467, 9,
|
||||
51, 9, 307, 9, 179, 9, 435, 9, 115, 9, 371, 9, 243, 9, 499, 9,
|
||||
11, 9, 267, 9, 139, 9, 395, 9, 75, 9, 331, 9, 203, 9, 459, 9,
|
||||
43, 9, 299, 9, 171, 9, 427, 9, 107, 9, 363, 9, 235, 9, 491, 9,
|
||||
27, 9, 283, 9, 155, 9, 411, 9, 91, 9, 347, 9, 219, 9, 475, 9,
|
||||
59, 9, 315, 9, 187, 9, 443, 9, 123, 9, 379, 9, 251, 9, 507, 9,
|
||||
7, 9, 263, 9, 135, 9, 391, 9, 71, 9, 327, 9, 199, 9, 455, 9,
|
||||
39, 9, 295, 9, 167, 9, 423, 9, 103, 9, 359, 9, 231, 9, 487, 9,
|
||||
23, 9, 279, 9, 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, 471, 9,
|
||||
55, 9, 311, 9, 183, 9, 439, 9, 119, 9, 375, 9, 247, 9, 503, 9,
|
||||
15, 9, 271, 9, 143, 9, 399, 9, 79, 9, 335, 9, 207, 9, 463, 9,
|
||||
47, 9, 303, 9, 175, 9, 431, 9, 111, 9, 367, 9, 239, 9, 495, 9,
|
||||
31, 9, 287, 9, 159, 9, 415, 9, 95, 9, 351, 9, 223, 9, 479, 9,
|
||||
63, 9, 319, 9, 191, 9, 447, 9, 127, 9, 383, 9, 255, 9, 511, 9,
|
||||
0, 7, 64, 7, 32, 7, 96, 7, 16, 7, 80, 7, 48, 7, 112, 7,
|
||||
8, 7, 72, 7, 40, 7, 104, 7, 24, 7, 88, 7, 56, 7, 120, 7,
|
||||
4, 7, 68, 7, 36, 7, 100, 7, 20, 7, 84, 7, 52, 7, 116, 7,
|
||||
3, 8, 131, 8, 67, 8, 195, 8, 35, 8, 163, 8, 99, 8, 227, 8
|
||||
};
|
||||
|
||||
internal static readonly short[] distTreeCodes = new short[] {
|
||||
0, 5, 16, 5, 8, 5, 24, 5, 4, 5, 20, 5, 12, 5, 28, 5,
|
||||
2, 5, 18, 5, 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, 30, 5,
|
||||
1, 5, 17, 5, 9, 5, 25, 5, 5, 5, 21, 5, 13, 5, 29, 5,
|
||||
3, 5, 19, 5, 11, 5, 27, 5, 7, 5, 23, 5 };
|
||||
|
||||
internal static readonly StaticTree Literals;
|
||||
internal static readonly StaticTree Distances;
|
||||
internal static readonly StaticTree BitLengths;
|
||||
|
||||
internal short[] treeCodes; // static tree or null
|
||||
internal int[] extraBits; // extra bits for each code or null
|
||||
internal int extraBase; // base index for extra_bits
|
||||
internal int elems; // max number of elements in the tree
|
||||
internal int maxLength; // max bit length for the codes
|
||||
|
||||
private StaticTree(short[] treeCodes, int[] extraBits, int extraBase, int elems, int maxLength)
|
||||
{
|
||||
this.treeCodes = treeCodes;
|
||||
this.extraBits = extraBits;
|
||||
this.extraBase = extraBase;
|
||||
this.elems = elems;
|
||||
this.maxLength = maxLength;
|
||||
}
|
||||
static StaticTree()
|
||||
{
|
||||
Literals = new StaticTree(lengthAndLiteralsTreeCodes, Tree.ExtraLengthBits, InternalConstants.LITERALS + 1, InternalConstants.L_CODES, InternalConstants.MAX_BITS);
|
||||
Distances = new StaticTree(distTreeCodes, Tree.ExtraDistanceBits, 0, InternalConstants.D_CODES, InternalConstants.MAX_BITS);
|
||||
BitLengths = new StaticTree(null, Tree.extra_blbits, 0, InternalConstants.BL_CODES, InternalConstants.MAX_BL_BITS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Computes an Adler-32 checksum.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The Adler checksum is similar to a CRC checksum, but faster to compute, though less
|
||||
/// reliable. It is used in producing RFC1950 compressed streams. The Adler checksum
|
||||
/// is a required part of the "ZLIB" standard. Applications will almost never need to
|
||||
/// use this class directly.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <exclude/>
|
||||
public sealed class Adler
|
||||
{
|
||||
// largest prime smaller than 65536
|
||||
private static readonly uint BASE = 65521;
|
||||
// NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
|
||||
private static readonly int NMAX = 5552;
|
||||
|
||||
|
||||
#pragma warning disable 3001
|
||||
#pragma warning disable 3002
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the Adler32 checksum.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This is used within ZLIB. You probably don't need to use this directly.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// To compute an Adler32 checksum on a byte array:
|
||||
/// <code>
|
||||
/// var adler = Adler.Adler32(0, null, 0, 0);
|
||||
/// adler = Adler.Adler32(adler, buffer, index, length);
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static uint Adler32(uint adler, byte[] buf, int index, int len)
|
||||
{
|
||||
if (buf == null)
|
||||
return 1;
|
||||
|
||||
uint s1 = (uint) (adler & 0xffff);
|
||||
uint s2 = (uint) ((adler >> 16) & 0xffff);
|
||||
|
||||
while (len > 0)
|
||||
{
|
||||
int k = len < NMAX ? len : NMAX;
|
||||
len -= k;
|
||||
while (k >= 16)
|
||||
{
|
||||
//s1 += (buf[index++] & 0xff); s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
s1 += buf[index++]; s2 += s1;
|
||||
k -= 16;
|
||||
}
|
||||
if (k != 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
s1 += buf[index++];
|
||||
s2 += s1;
|
||||
}
|
||||
while (--k != 0);
|
||||
}
|
||||
s1 %= BASE;
|
||||
s2 %= BASE;
|
||||
}
|
||||
return (uint)((s2 << 16) | s1);
|
||||
}
|
||||
#pragma warning restore 3001
|
||||
#pragma warning restore 3002
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,627 @@
|
||||
// ZlibBaseStream.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-August-06 21:22:38>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the ZlibBaseStream class, which is an intnernal
|
||||
// base class for DeflateStream, ZlibStream and GZipStream.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Ionic.Zlib
|
||||
{
|
||||
|
||||
internal enum ZlibStreamFlavor { ZLIB = 1950, DEFLATE = 1951, GZIP = 1952 }
|
||||
|
||||
internal class ZlibBaseStream : System.IO.Stream
|
||||
{
|
||||
protected internal ZlibCodec _z = null; // deferred init... new ZlibCodec();
|
||||
|
||||
protected internal StreamMode _streamMode = StreamMode.Undefined;
|
||||
protected internal FlushType _flushMode;
|
||||
protected internal ZlibStreamFlavor _flavor;
|
||||
protected internal CompressionMode _compressionMode;
|
||||
protected internal CompressionLevel _level;
|
||||
protected internal bool _leaveOpen;
|
||||
protected internal byte[] _workingBuffer;
|
||||
protected internal int _bufferSize = ZlibConstants.WorkingBufferSizeDefault;
|
||||
protected internal byte[] _buf1 = new byte[1];
|
||||
|
||||
protected internal System.IO.Stream _stream;
|
||||
protected internal CompressionStrategy Strategy = CompressionStrategy.Default;
|
||||
|
||||
// workitem 7159
|
||||
Ionic.Crc.CRC32 crc;
|
||||
protected internal string _GzipFileName;
|
||||
protected internal string _GzipComment;
|
||||
protected internal DateTime _GzipMtime;
|
||||
protected internal int _gzipHeaderByteCount;
|
||||
|
||||
internal int Crc32 { get { if (crc == null) return 0; return crc.Crc32Result; } }
|
||||
|
||||
public ZlibBaseStream(System.IO.Stream stream,
|
||||
CompressionMode compressionMode,
|
||||
CompressionLevel level,
|
||||
ZlibStreamFlavor flavor,
|
||||
bool leaveOpen)
|
||||
: base()
|
||||
{
|
||||
this._flushMode = FlushType.None;
|
||||
//this._workingBuffer = new byte[WORKING_BUFFER_SIZE_DEFAULT];
|
||||
this._stream = stream;
|
||||
this._leaveOpen = leaveOpen;
|
||||
this._compressionMode = compressionMode;
|
||||
this._flavor = flavor;
|
||||
this._level = level;
|
||||
// workitem 7159
|
||||
if (flavor == ZlibStreamFlavor.GZIP)
|
||||
{
|
||||
this.crc = new Ionic.Crc.CRC32();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected internal bool _wantCompress
|
||||
{
|
||||
get
|
||||
{
|
||||
return (this._compressionMode == CompressionMode.Compress);
|
||||
}
|
||||
}
|
||||
|
||||
private ZlibCodec z
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_z == null)
|
||||
{
|
||||
bool wantRfc1950Header = (this._flavor == ZlibStreamFlavor.ZLIB);
|
||||
_z = new ZlibCodec();
|
||||
if (this._compressionMode == CompressionMode.Decompress)
|
||||
{
|
||||
_z.InitializeInflate(wantRfc1950Header);
|
||||
}
|
||||
else
|
||||
{
|
||||
_z.Strategy = Strategy;
|
||||
_z.InitializeDeflate(this._level, wantRfc1950Header);
|
||||
}
|
||||
}
|
||||
return _z;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private byte[] workingBuffer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_workingBuffer == null)
|
||||
_workingBuffer = new byte[_bufferSize];
|
||||
return _workingBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override void Write(System.Byte[] buffer, int offset, int count)
|
||||
{
|
||||
// workitem 7159
|
||||
// calculate the CRC on the unccompressed data (before writing)
|
||||
if (crc != null)
|
||||
crc.SlurpBlock(buffer, offset, count);
|
||||
|
||||
if (_streamMode == StreamMode.Undefined)
|
||||
_streamMode = StreamMode.Writer;
|
||||
else if (_streamMode != StreamMode.Writer)
|
||||
throw new ZlibException("Cannot Write after Reading.");
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
// first reference of z property will initialize the private var _z
|
||||
z.InputBuffer = buffer;
|
||||
_z.NextIn = offset;
|
||||
_z.AvailableBytesIn = count;
|
||||
bool done = false;
|
||||
do
|
||||
{
|
||||
_z.OutputBuffer = workingBuffer;
|
||||
_z.NextOut = 0;
|
||||
_z.AvailableBytesOut = _workingBuffer.Length;
|
||||
int rc = (_wantCompress)
|
||||
? _z.Deflate(_flushMode)
|
||||
: _z.Inflate(_flushMode);
|
||||
if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END)
|
||||
throw new ZlibException((_wantCompress ? "de" : "in") + "flating: " + _z.Message);
|
||||
|
||||
//if (_workingBuffer.Length - _z.AvailableBytesOut > 0)
|
||||
_stream.Write(_workingBuffer, 0, _workingBuffer.Length - _z.AvailableBytesOut);
|
||||
|
||||
done = _z.AvailableBytesIn == 0 && _z.AvailableBytesOut != 0;
|
||||
|
||||
// If GZIP and de-compress, we're done when 8 bytes remain.
|
||||
if (_flavor == ZlibStreamFlavor.GZIP && !_wantCompress)
|
||||
done = (_z.AvailableBytesIn == 8 && _z.AvailableBytesOut != 0);
|
||||
|
||||
}
|
||||
while (!done);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void finish()
|
||||
{
|
||||
if (_z == null) return;
|
||||
|
||||
if (_streamMode == StreamMode.Writer)
|
||||
{
|
||||
bool done = false;
|
||||
do
|
||||
{
|
||||
_z.OutputBuffer = workingBuffer;
|
||||
_z.NextOut = 0;
|
||||
_z.AvailableBytesOut = _workingBuffer.Length;
|
||||
int rc = (_wantCompress)
|
||||
? _z.Deflate(FlushType.Finish)
|
||||
: _z.Inflate(FlushType.Finish);
|
||||
|
||||
if (rc != ZlibConstants.Z_STREAM_END && rc != ZlibConstants.Z_OK)
|
||||
{
|
||||
string verb = (_wantCompress ? "de" : "in") + "flating";
|
||||
if (_z.Message == null)
|
||||
throw new ZlibException(String.Format("{0}: (rc = {1})", verb, rc));
|
||||
else
|
||||
throw new ZlibException(verb + ": " + _z.Message);
|
||||
}
|
||||
|
||||
if (_workingBuffer.Length - _z.AvailableBytesOut > 0)
|
||||
{
|
||||
_stream.Write(_workingBuffer, 0, _workingBuffer.Length - _z.AvailableBytesOut);
|
||||
}
|
||||
|
||||
done = _z.AvailableBytesIn == 0 && _z.AvailableBytesOut != 0;
|
||||
// If GZIP and de-compress, we're done when 8 bytes remain.
|
||||
if (_flavor == ZlibStreamFlavor.GZIP && !_wantCompress)
|
||||
done = (_z.AvailableBytesIn == 8 && _z.AvailableBytesOut != 0);
|
||||
|
||||
}
|
||||
while (!done);
|
||||
|
||||
Flush();
|
||||
|
||||
// workitem 7159
|
||||
if (_flavor == ZlibStreamFlavor.GZIP)
|
||||
{
|
||||
if (_wantCompress)
|
||||
{
|
||||
// Emit the GZIP trailer: CRC32 and size mod 2^32
|
||||
int c1 = crc.Crc32Result;
|
||||
_stream.Write(BitConverter.GetBytes(c1), 0, 4);
|
||||
int c2 = (Int32)(crc.TotalBytesRead & 0x00000000FFFFFFFF);
|
||||
_stream.Write(BitConverter.GetBytes(c2), 0, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ZlibException("Writing with decompression is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
// workitem 7159
|
||||
else if (_streamMode == StreamMode.Reader)
|
||||
{
|
||||
if (_flavor == ZlibStreamFlavor.GZIP)
|
||||
{
|
||||
if (!_wantCompress)
|
||||
{
|
||||
// workitem 8501: handle edge case (decompress empty stream)
|
||||
if (_z.TotalBytesOut == 0L)
|
||||
return;
|
||||
|
||||
// Read and potentially verify the GZIP trailer:
|
||||
// CRC32 and size mod 2^32
|
||||
byte[] trailer = new byte[8];
|
||||
|
||||
// workitems 8679 & 12554
|
||||
if (_z.AvailableBytesIn < 8)
|
||||
{
|
||||
// Make sure we have read to the end of the stream
|
||||
Array.Copy(_z.InputBuffer, _z.NextIn, trailer, 0, _z.AvailableBytesIn);
|
||||
int bytesNeeded = 8 - _z.AvailableBytesIn;
|
||||
int bytesRead = _stream.Read(trailer,
|
||||
_z.AvailableBytesIn,
|
||||
bytesNeeded);
|
||||
if (bytesNeeded != bytesRead)
|
||||
{
|
||||
throw new ZlibException(String.Format("Missing or incomplete GZIP trailer. Expected 8 bytes, got {0}.",
|
||||
_z.AvailableBytesIn + bytesRead));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(_z.InputBuffer, _z.NextIn, trailer, 0, trailer.Length);
|
||||
}
|
||||
|
||||
Int32 crc32_expected = BitConverter.ToInt32(trailer, 0);
|
||||
Int32 crc32_actual = crc.Crc32Result;
|
||||
Int32 isize_expected = BitConverter.ToInt32(trailer, 4);
|
||||
Int32 isize_actual = (Int32)(_z.TotalBytesOut & 0x00000000FFFFFFFF);
|
||||
|
||||
if (crc32_actual != crc32_expected)
|
||||
throw new ZlibException(String.Format("Bad CRC32 in GZIP trailer. (actual({0:X8})!=expected({1:X8}))", crc32_actual, crc32_expected));
|
||||
|
||||
if (isize_actual != isize_expected)
|
||||
throw new ZlibException(String.Format("Bad size in GZIP trailer. (actual({0})!=expected({1}))", isize_actual, isize_expected));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ZlibException("Reading with compression is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void end()
|
||||
{
|
||||
if (z == null)
|
||||
return;
|
||||
if (_wantCompress)
|
||||
{
|
||||
_z.EndDeflate();
|
||||
}
|
||||
else
|
||||
{
|
||||
_z.EndInflate();
|
||||
}
|
||||
_z = null;
|
||||
}
|
||||
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
if (_stream == null) return;
|
||||
try
|
||||
{
|
||||
finish();
|
||||
}
|
||||
finally
|
||||
{
|
||||
end();
|
||||
if (!_leaveOpen) _stream.Close();
|
||||
_stream = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
_stream.Flush();
|
||||
}
|
||||
|
||||
public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
//_outStream.Seek(offset, origin);
|
||||
}
|
||||
public override void SetLength(System.Int64 value)
|
||||
{
|
||||
_stream.SetLength(value);
|
||||
}
|
||||
|
||||
|
||||
#if NOT
|
||||
public int Read()
|
||||
{
|
||||
if (Read(_buf1, 0, 1) == 0)
|
||||
return 0;
|
||||
// calculate CRC after reading
|
||||
if (crc!=null)
|
||||
crc.SlurpBlock(_buf1,0,1);
|
||||
return (_buf1[0] & 0xFF);
|
||||
}
|
||||
#endif
|
||||
|
||||
private bool nomoreinput = false;
|
||||
|
||||
|
||||
|
||||
private string ReadZeroTerminatedString()
|
||||
{
|
||||
var list = new System.Collections.Generic.List<byte>();
|
||||
bool done = false;
|
||||
do
|
||||
{
|
||||
// workitem 7740
|
||||
int n = _stream.Read(_buf1, 0, 1);
|
||||
if (n != 1)
|
||||
throw new ZlibException("Unexpected EOF reading GZIP header.");
|
||||
else
|
||||
{
|
||||
if (_buf1[0] == 0)
|
||||
done = true;
|
||||
else
|
||||
list.Add(_buf1[0]);
|
||||
}
|
||||
} while (!done);
|
||||
byte[] a = list.ToArray();
|
||||
return GZipStream.iso8859dash1.GetString(a, 0, a.Length);
|
||||
}
|
||||
|
||||
|
||||
private int _ReadAndValidateGzipHeader()
|
||||
{
|
||||
int totalBytesRead = 0;
|
||||
// read the header on the first read
|
||||
byte[] header = new byte[10];
|
||||
int n = _stream.Read(header, 0, header.Length);
|
||||
|
||||
// workitem 8501: handle edge case (decompress empty stream)
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
||||
if (n != 10)
|
||||
throw new ZlibException("Not a valid GZIP stream.");
|
||||
|
||||
if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8)
|
||||
throw new ZlibException("Bad GZIP header.");
|
||||
|
||||
Int32 timet = BitConverter.ToInt32(header, 4);
|
||||
_GzipMtime = GZipStream._unixEpoch.AddSeconds(timet);
|
||||
totalBytesRead += n;
|
||||
if ((header[3] & 0x04) == 0x04)
|
||||
{
|
||||
// read and discard extra field
|
||||
n = _stream.Read(header, 0, 2); // 2-byte length field
|
||||
totalBytesRead += n;
|
||||
|
||||
Int16 extraLength = (Int16)(header[0] + header[1] * 256);
|
||||
byte[] extra = new byte[extraLength];
|
||||
n = _stream.Read(extra, 0, extra.Length);
|
||||
if (n != extraLength)
|
||||
throw new ZlibException("Unexpected end-of-file reading GZIP header.");
|
||||
totalBytesRead += n;
|
||||
}
|
||||
if ((header[3] & 0x08) == 0x08)
|
||||
_GzipFileName = ReadZeroTerminatedString();
|
||||
if ((header[3] & 0x10) == 0x010)
|
||||
_GzipComment = ReadZeroTerminatedString();
|
||||
if ((header[3] & 0x02) == 0x02)
|
||||
Read(_buf1, 0, 1); // CRC16, ignore
|
||||
|
||||
return totalBytesRead;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override System.Int32 Read(System.Byte[] buffer, System.Int32 offset, System.Int32 count)
|
||||
{
|
||||
// According to MS documentation, any implementation of the IO.Stream.Read function must:
|
||||
// (a) throw an exception if offset & count reference an invalid part of the buffer,
|
||||
// or if count < 0, or if buffer is null
|
||||
// (b) return 0 only upon EOF, or if count = 0
|
||||
// (c) if not EOF, then return at least 1 byte, up to <count> bytes
|
||||
|
||||
if (_streamMode == StreamMode.Undefined)
|
||||
{
|
||||
if (!this._stream.CanRead) throw new ZlibException("The stream is not readable.");
|
||||
// for the first read, set up some controls.
|
||||
_streamMode = StreamMode.Reader;
|
||||
// (The first reference to _z goes through the private accessor which
|
||||
// may initialize it.)
|
||||
z.AvailableBytesIn = 0;
|
||||
if (_flavor == ZlibStreamFlavor.GZIP)
|
||||
{
|
||||
_gzipHeaderByteCount = _ReadAndValidateGzipHeader();
|
||||
// workitem 8501: handle edge case (decompress empty stream)
|
||||
if (_gzipHeaderByteCount == 0)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (_streamMode != StreamMode.Reader)
|
||||
throw new ZlibException("Cannot Read after Writing.");
|
||||
|
||||
if (count == 0) return 0;
|
||||
if (nomoreinput && _wantCompress) return 0; // workitem 8557
|
||||
if (buffer == null) throw new ArgumentNullException("buffer");
|
||||
if (count < 0) throw new ArgumentOutOfRangeException("count");
|
||||
if (offset < buffer.GetLowerBound(0)) throw new ArgumentOutOfRangeException("offset");
|
||||
if ((offset + count) > buffer.GetLength(0)) throw new ArgumentOutOfRangeException("count");
|
||||
|
||||
int rc = 0;
|
||||
|
||||
// set up the output of the deflate/inflate codec:
|
||||
_z.OutputBuffer = buffer;
|
||||
_z.NextOut = offset;
|
||||
_z.AvailableBytesOut = count;
|
||||
|
||||
// This is necessary in case _workingBuffer has been resized. (new byte[])
|
||||
// (The first reference to _workingBuffer goes through the private accessor which
|
||||
// may initialize it.)
|
||||
_z.InputBuffer = workingBuffer;
|
||||
|
||||
do
|
||||
{
|
||||
// need data in _workingBuffer in order to deflate/inflate. Here, we check if we have any.
|
||||
if ((_z.AvailableBytesIn == 0) && (!nomoreinput))
|
||||
{
|
||||
// No data available, so try to Read data from the captive stream.
|
||||
_z.NextIn = 0;
|
||||
_z.AvailableBytesIn = _stream.Read(_workingBuffer, 0, _workingBuffer.Length);
|
||||
if (_z.AvailableBytesIn == 0)
|
||||
nomoreinput = true;
|
||||
|
||||
}
|
||||
// we have data in InputBuffer; now compress or decompress as appropriate
|
||||
rc = (_wantCompress)
|
||||
? _z.Deflate(_flushMode)
|
||||
: _z.Inflate(_flushMode);
|
||||
|
||||
if (nomoreinput && (rc == ZlibConstants.Z_BUF_ERROR))
|
||||
return 0;
|
||||
|
||||
if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END)
|
||||
throw new ZlibException(String.Format("{0}flating: rc={1} msg={2}", (_wantCompress ? "de" : "in"), rc, _z.Message));
|
||||
|
||||
if ((nomoreinput || rc == ZlibConstants.Z_STREAM_END) && (_z.AvailableBytesOut == count))
|
||||
break; // nothing more to read
|
||||
}
|
||||
//while (_z.AvailableBytesOut == count && rc == ZlibConstants.Z_OK);
|
||||
while (_z.AvailableBytesOut > 0 && !nomoreinput && rc == ZlibConstants.Z_OK);
|
||||
|
||||
|
||||
// workitem 8557
|
||||
// is there more room in output?
|
||||
if (_z.AvailableBytesOut > 0)
|
||||
{
|
||||
if (rc == ZlibConstants.Z_OK && _z.AvailableBytesIn == 0)
|
||||
{
|
||||
// deferred
|
||||
}
|
||||
|
||||
// are we completely done reading?
|
||||
if (nomoreinput)
|
||||
{
|
||||
// and in compression?
|
||||
if (_wantCompress)
|
||||
{
|
||||
// no more input data available; therefore we flush to
|
||||
// try to complete the read
|
||||
rc = _z.Deflate(FlushType.Finish);
|
||||
|
||||
if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END)
|
||||
throw new ZlibException(String.Format("Deflating: rc={0} msg={1}", rc, _z.Message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
rc = (count - _z.AvailableBytesOut);
|
||||
|
||||
// calculate CRC after reading
|
||||
if (crc != null)
|
||||
crc.SlurpBlock(buffer, offset, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override System.Boolean CanRead
|
||||
{
|
||||
get { return this._stream.CanRead; }
|
||||
}
|
||||
|
||||
public override System.Boolean CanSeek
|
||||
{
|
||||
get { return this._stream.CanSeek; }
|
||||
}
|
||||
|
||||
public override System.Boolean CanWrite
|
||||
{
|
||||
get { return this._stream.CanWrite; }
|
||||
}
|
||||
|
||||
public override System.Int64 Length
|
||||
{
|
||||
get { return _stream.Length; }
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
set { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
internal enum StreamMode
|
||||
{
|
||||
Writer,
|
||||
Reader,
|
||||
Undefined,
|
||||
}
|
||||
|
||||
|
||||
public static void CompressString(String s, Stream compressor)
|
||||
{
|
||||
byte[] uncompressed = System.Text.Encoding.UTF8.GetBytes(s);
|
||||
using (compressor)
|
||||
{
|
||||
compressor.Write(uncompressed, 0, uncompressed.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public static void CompressBuffer(byte[] b, Stream compressor)
|
||||
{
|
||||
// workitem 8460
|
||||
using (compressor)
|
||||
{
|
||||
compressor.Write(b, 0, b.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public static String UncompressString(byte[] compressed, Stream decompressor)
|
||||
{
|
||||
// workitem 8460
|
||||
byte[] working = new byte[1024];
|
||||
var encoding = System.Text.Encoding.UTF8;
|
||||
using (var output = new MemoryStream())
|
||||
{
|
||||
using (decompressor)
|
||||
{
|
||||
int n;
|
||||
while ((n = decompressor.Read(working, 0, working.Length)) != 0)
|
||||
{
|
||||
output.Write(working, 0, n);
|
||||
}
|
||||
}
|
||||
|
||||
// reset to allow read from start
|
||||
output.Seek(0, SeekOrigin.Begin);
|
||||
var sr = new StreamReader(output, encoding);
|
||||
return sr.ReadToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] UncompressBuffer(byte[] compressed, Stream decompressor)
|
||||
{
|
||||
// workitem 8460
|
||||
byte[] working = new byte[1024];
|
||||
using (var output = new MemoryStream())
|
||||
{
|
||||
using (decompressor)
|
||||
{
|
||||
int n;
|
||||
while ((n = decompressor.Read(working, 0, working.Length)) != 0)
|
||||
{
|
||||
output.Write(working, 0, n);
|
||||
}
|
||||
}
|
||||
return output.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,717 @@
|
||||
// ZlibCodec.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-November-03 15:40:51>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines a Codec for ZLIB compression and
|
||||
// decompression. This code extends code that was based the jzlib
|
||||
// implementation of zlib, but this code is completely novel. The codec
|
||||
// class is new, and encapsulates some behaviors that are new, and some
|
||||
// that were present in other classes in the jzlib code base. In
|
||||
// keeping with the license for jzlib, the copyright to the jzlib code
|
||||
// is included below.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// This program is based on zlib-1.1.3; credit to authors
|
||||
// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
|
||||
// and contributors of zlib.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
|
||||
using System;
|
||||
using Interop=System.Runtime.InteropServices;
|
||||
|
||||
namespace Ionic.Zlib
|
||||
{
|
||||
/// <summary>
|
||||
/// Encoder and Decoder for ZLIB and DEFLATE (IETF RFC1950 and RFC1951).
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// This class compresses and decompresses data according to the Deflate algorithm
|
||||
/// and optionally, the ZLIB format, as documented in <see
|
||||
/// href="http://www.ietf.org/rfc/rfc1950.txt">RFC 1950 - ZLIB</see> and <see
|
||||
/// href="http://www.ietf.org/rfc/rfc1951.txt">RFC 1951 - DEFLATE</see>.
|
||||
/// </remarks>
|
||||
[Interop.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d0000D")]
|
||||
[Interop.ComVisible(true)]
|
||||
#if !NETCF
|
||||
[Interop.ClassInterface(Interop.ClassInterfaceType.AutoDispatch)]
|
||||
#endif
|
||||
sealed public class ZlibCodec
|
||||
{
|
||||
/// <summary>
|
||||
/// The buffer from which data is taken.
|
||||
/// </summary>
|
||||
public byte[] InputBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// An index into the InputBuffer array, indicating where to start reading.
|
||||
/// </summary>
|
||||
public int NextIn;
|
||||
|
||||
/// <summary>
|
||||
/// The number of bytes available in the InputBuffer, starting at NextIn.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Generally you should set this to InputBuffer.Length before the first Inflate() or Deflate() call.
|
||||
/// The class will update this number as calls to Inflate/Deflate are made.
|
||||
/// </remarks>
|
||||
public int AvailableBytesIn;
|
||||
|
||||
/// <summary>
|
||||
/// Total number of bytes read so far, through all calls to Inflate()/Deflate().
|
||||
/// </summary>
|
||||
public long TotalBytesIn;
|
||||
|
||||
/// <summary>
|
||||
/// Buffer to store output data.
|
||||
/// </summary>
|
||||
public byte[] OutputBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// An index into the OutputBuffer array, indicating where to start writing.
|
||||
/// </summary>
|
||||
public int NextOut;
|
||||
|
||||
/// <summary>
|
||||
/// The number of bytes available in the OutputBuffer, starting at NextOut.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Generally you should set this to OutputBuffer.Length before the first Inflate() or Deflate() call.
|
||||
/// The class will update this number as calls to Inflate/Deflate are made.
|
||||
/// </remarks>
|
||||
public int AvailableBytesOut;
|
||||
|
||||
/// <summary>
|
||||
/// Total number of bytes written to the output so far, through all calls to Inflate()/Deflate().
|
||||
/// </summary>
|
||||
public long TotalBytesOut;
|
||||
|
||||
/// <summary>
|
||||
/// used for diagnostics, when something goes wrong!
|
||||
/// </summary>
|
||||
public System.String Message;
|
||||
|
||||
internal DeflateManager dstate;
|
||||
internal InflateManager istate;
|
||||
|
||||
internal uint _Adler32;
|
||||
|
||||
/// <summary>
|
||||
/// The compression level to use in this codec. Useful only in compression mode.
|
||||
/// </summary>
|
||||
public CompressionLevel CompressLevel = CompressionLevel.Default;
|
||||
|
||||
/// <summary>
|
||||
/// The number of Window Bits to use.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This gauges the size of the sliding window, and hence the
|
||||
/// compression effectiveness as well as memory consumption. It's best to just leave this
|
||||
/// setting alone if you don't know what it is. The maximum value is 15 bits, which implies
|
||||
/// a 32k window.
|
||||
/// </remarks>
|
||||
public int WindowBits = ZlibConstants.WindowBitsDefault;
|
||||
|
||||
/// <summary>
|
||||
/// The compression strategy to use.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is only effective in compression. The theory offered by ZLIB is that different
|
||||
/// strategies could potentially produce significant differences in compression behavior
|
||||
/// for different data sets. Unfortunately I don't have any good recommendations for how
|
||||
/// to set it differently. When I tested changing the strategy I got minimally different
|
||||
/// compression performance. It's best to leave this property alone if you don't have a
|
||||
/// good feel for it. Or, you may want to produce a test harness that runs through the
|
||||
/// different strategy options and evaluates them on different file types. If you do that,
|
||||
/// let me know your results.
|
||||
/// </remarks>
|
||||
public CompressionStrategy Strategy = CompressionStrategy.Default;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The Adler32 checksum on the data transferred through the codec so far. You probably don't need to look at this.
|
||||
/// </summary>
|
||||
public int Adler32 { get { return (int)_Adler32; } }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a ZlibCodec.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If you use this default constructor, you will later have to explicitly call
|
||||
/// InitializeInflate() or InitializeDeflate() before using the ZlibCodec to compress
|
||||
/// or decompress.
|
||||
/// </remarks>
|
||||
public ZlibCodec() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a ZlibCodec that either compresses or decompresses.
|
||||
/// </summary>
|
||||
/// <param name="mode">
|
||||
/// Indicates whether the codec should compress (deflate) or decompress (inflate).
|
||||
/// </param>
|
||||
public ZlibCodec(CompressionMode mode)
|
||||
{
|
||||
if (mode == CompressionMode.Compress)
|
||||
{
|
||||
int rc = InitializeDeflate();
|
||||
if (rc != ZlibConstants.Z_OK) throw new ZlibException("Cannot initialize for deflate.");
|
||||
}
|
||||
else if (mode == CompressionMode.Decompress)
|
||||
{
|
||||
int rc = InitializeInflate();
|
||||
if (rc != ZlibConstants.Z_OK) throw new ZlibException("Cannot initialize for inflate.");
|
||||
}
|
||||
else throw new ZlibException("Invalid ZlibStreamFlavor.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the inflation state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It is not necessary to call this before using the ZlibCodec to inflate data;
|
||||
/// It is implicitly called when you call the constructor.
|
||||
/// </remarks>
|
||||
/// <returns>Z_OK if everything goes well.</returns>
|
||||
public int InitializeInflate()
|
||||
{
|
||||
return InitializeInflate(this.WindowBits);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the inflation state with an explicit flag to
|
||||
/// govern the handling of RFC1950 header bytes.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// By default, the ZLIB header defined in <see
|
||||
/// href="http://www.ietf.org/rfc/rfc1950.txt">RFC 1950</see> is expected. If
|
||||
/// you want to read a zlib stream you should specify true for
|
||||
/// expectRfc1950Header. If you have a deflate stream, you will want to specify
|
||||
/// false. It is only necessary to invoke this initializer explicitly if you
|
||||
/// want to specify false.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="expectRfc1950Header">whether to expect an RFC1950 header byte
|
||||
/// pair when reading the stream of data to be inflated.</param>
|
||||
///
|
||||
/// <returns>Z_OK if everything goes well.</returns>
|
||||
public int InitializeInflate(bool expectRfc1950Header)
|
||||
{
|
||||
return InitializeInflate(this.WindowBits, expectRfc1950Header);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the ZlibCodec for inflation, with the specified number of window bits.
|
||||
/// </summary>
|
||||
/// <param name="windowBits">The number of window bits to use. If you need to ask what that is,
|
||||
/// then you shouldn't be calling this initializer.</param>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public int InitializeInflate(int windowBits)
|
||||
{
|
||||
this.WindowBits = windowBits;
|
||||
return InitializeInflate(windowBits, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the inflation state with an explicit flag to govern the handling of
|
||||
/// RFC1950 header bytes.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// If you want to read a zlib stream you should specify true for
|
||||
/// expectRfc1950Header. In this case, the library will expect to find a ZLIB
|
||||
/// header, as defined in <see href="http://www.ietf.org/rfc/rfc1950.txt">RFC
|
||||
/// 1950</see>, in the compressed stream. If you will be reading a DEFLATE or
|
||||
/// GZIP stream, which does not have such a header, you will want to specify
|
||||
/// false.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="expectRfc1950Header">whether to expect an RFC1950 header byte pair when reading
|
||||
/// the stream of data to be inflated.</param>
|
||||
/// <param name="windowBits">The number of window bits to use. If you need to ask what that is,
|
||||
/// then you shouldn't be calling this initializer.</param>
|
||||
/// <returns>Z_OK if everything goes well.</returns>
|
||||
public int InitializeInflate(int windowBits, bool expectRfc1950Header)
|
||||
{
|
||||
this.WindowBits = windowBits;
|
||||
if (dstate != null) throw new ZlibException("You may not call InitializeInflate() after calling InitializeDeflate().");
|
||||
istate = new InflateManager(expectRfc1950Header);
|
||||
return istate.Initialize(this, windowBits);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inflate the data in the InputBuffer, placing the result in the OutputBuffer.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You must have set InputBuffer and OutputBuffer, NextIn and NextOut, and AvailableBytesIn and
|
||||
/// AvailableBytesOut before calling this method.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// private void InflateBuffer()
|
||||
/// {
|
||||
/// int bufferSize = 1024;
|
||||
/// byte[] buffer = new byte[bufferSize];
|
||||
/// ZlibCodec decompressor = new ZlibCodec();
|
||||
///
|
||||
/// Console.WriteLine("\n============================================");
|
||||
/// Console.WriteLine("Size of Buffer to Inflate: {0} bytes.", CompressedBytes.Length);
|
||||
/// MemoryStream ms = new MemoryStream(DecompressedBytes);
|
||||
///
|
||||
/// int rc = decompressor.InitializeInflate();
|
||||
///
|
||||
/// decompressor.InputBuffer = CompressedBytes;
|
||||
/// decompressor.NextIn = 0;
|
||||
/// decompressor.AvailableBytesIn = CompressedBytes.Length;
|
||||
///
|
||||
/// decompressor.OutputBuffer = buffer;
|
||||
///
|
||||
/// // pass 1: inflate
|
||||
/// do
|
||||
/// {
|
||||
/// decompressor.NextOut = 0;
|
||||
/// decompressor.AvailableBytesOut = buffer.Length;
|
||||
/// rc = decompressor.Inflate(FlushType.None);
|
||||
///
|
||||
/// if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END)
|
||||
/// throw new Exception("inflating: " + decompressor.Message);
|
||||
///
|
||||
/// ms.Write(decompressor.OutputBuffer, 0, buffer.Length - decompressor.AvailableBytesOut);
|
||||
/// }
|
||||
/// while (decompressor.AvailableBytesIn > 0 || decompressor.AvailableBytesOut == 0);
|
||||
///
|
||||
/// // pass 2: finish and flush
|
||||
/// do
|
||||
/// {
|
||||
/// decompressor.NextOut = 0;
|
||||
/// decompressor.AvailableBytesOut = buffer.Length;
|
||||
/// rc = decompressor.Inflate(FlushType.Finish);
|
||||
///
|
||||
/// if (rc != ZlibConstants.Z_STREAM_END && rc != ZlibConstants.Z_OK)
|
||||
/// throw new Exception("inflating: " + decompressor.Message);
|
||||
///
|
||||
/// if (buffer.Length - decompressor.AvailableBytesOut > 0)
|
||||
/// ms.Write(buffer, 0, buffer.Length - decompressor.AvailableBytesOut);
|
||||
/// }
|
||||
/// while (decompressor.AvailableBytesIn > 0 || decompressor.AvailableBytesOut == 0);
|
||||
///
|
||||
/// decompressor.EndInflate();
|
||||
/// }
|
||||
///
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="flush">The flush to use when inflating.</param>
|
||||
/// <returns>Z_OK if everything goes well.</returns>
|
||||
public int Inflate(FlushType flush)
|
||||
{
|
||||
if (istate == null)
|
||||
throw new ZlibException("No Inflate State!");
|
||||
return istate.Inflate(flush);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Ends an inflation session.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Call this after successively calling Inflate(). This will cause all buffers to be flushed.
|
||||
/// After calling this you cannot call Inflate() without a intervening call to one of the
|
||||
/// InitializeInflate() overloads.
|
||||
/// </remarks>
|
||||
/// <returns>Z_OK if everything goes well.</returns>
|
||||
public int EndInflate()
|
||||
{
|
||||
if (istate == null)
|
||||
throw new ZlibException("No Inflate State!");
|
||||
int ret = istate.End();
|
||||
istate = null;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// I don't know what this does!
|
||||
/// </summary>
|
||||
/// <returns>Z_OK if everything goes well.</returns>
|
||||
public int SyncInflate()
|
||||
{
|
||||
if (istate == null)
|
||||
throw new ZlibException("No Inflate State!");
|
||||
return istate.Sync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the ZlibCodec for deflation operation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The codec will use the MAX window bits and the default level of compression.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// int bufferSize = 40000;
|
||||
/// byte[] CompressedBytes = new byte[bufferSize];
|
||||
/// byte[] DecompressedBytes = new byte[bufferSize];
|
||||
///
|
||||
/// ZlibCodec compressor = new ZlibCodec();
|
||||
///
|
||||
/// compressor.InitializeDeflate(CompressionLevel.Default);
|
||||
///
|
||||
/// compressor.InputBuffer = System.Text.ASCIIEncoding.ASCII.GetBytes(TextToCompress);
|
||||
/// compressor.NextIn = 0;
|
||||
/// compressor.AvailableBytesIn = compressor.InputBuffer.Length;
|
||||
///
|
||||
/// compressor.OutputBuffer = CompressedBytes;
|
||||
/// compressor.NextOut = 0;
|
||||
/// compressor.AvailableBytesOut = CompressedBytes.Length;
|
||||
///
|
||||
/// while (compressor.TotalBytesIn != TextToCompress.Length && compressor.TotalBytesOut < bufferSize)
|
||||
/// {
|
||||
/// compressor.Deflate(FlushType.None);
|
||||
/// }
|
||||
///
|
||||
/// while (true)
|
||||
/// {
|
||||
/// int rc= compressor.Deflate(FlushType.Finish);
|
||||
/// if (rc == ZlibConstants.Z_STREAM_END) break;
|
||||
/// }
|
||||
///
|
||||
/// compressor.EndDeflate();
|
||||
///
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <returns>Z_OK if all goes well. You generally don't need to check the return code.</returns>
|
||||
public int InitializeDeflate()
|
||||
{
|
||||
return _InternalInitializeDeflate(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the ZlibCodec for deflation operation, using the specified CompressionLevel.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The codec will use the maximum window bits (15) and the specified
|
||||
/// CompressionLevel. It will emit a ZLIB stream as it compresses.
|
||||
/// </remarks>
|
||||
/// <param name="level">The compression level for the codec.</param>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public int InitializeDeflate(CompressionLevel level)
|
||||
{
|
||||
this.CompressLevel = level;
|
||||
return _InternalInitializeDeflate(true);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the ZlibCodec for deflation operation, using the specified CompressionLevel,
|
||||
/// and the explicit flag governing whether to emit an RFC1950 header byte pair.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The codec will use the maximum window bits (15) and the specified CompressionLevel.
|
||||
/// If you want to generate a zlib stream, you should specify true for
|
||||
/// wantRfc1950Header. In this case, the library will emit a ZLIB
|
||||
/// header, as defined in <see href="http://www.ietf.org/rfc/rfc1950.txt">RFC
|
||||
/// 1950</see>, in the compressed stream.
|
||||
/// </remarks>
|
||||
/// <param name="level">The compression level for the codec.</param>
|
||||
/// <param name="wantRfc1950Header">whether to emit an initial RFC1950 byte pair in the compressed stream.</param>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public int InitializeDeflate(CompressionLevel level, bool wantRfc1950Header)
|
||||
{
|
||||
this.CompressLevel = level;
|
||||
return _InternalInitializeDeflate(wantRfc1950Header);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the ZlibCodec for deflation operation, using the specified CompressionLevel,
|
||||
/// and the specified number of window bits.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The codec will use the specified number of window bits and the specified CompressionLevel.
|
||||
/// </remarks>
|
||||
/// <param name="level">The compression level for the codec.</param>
|
||||
/// <param name="bits">the number of window bits to use. If you don't know what this means, don't use this method.</param>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public int InitializeDeflate(CompressionLevel level, int bits)
|
||||
{
|
||||
this.CompressLevel = level;
|
||||
this.WindowBits = bits;
|
||||
return _InternalInitializeDeflate(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the ZlibCodec for deflation operation, using the specified
|
||||
/// CompressionLevel, the specified number of window bits, and the explicit flag
|
||||
/// governing whether to emit an RFC1950 header byte pair.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="level">The compression level for the codec.</param>
|
||||
/// <param name="wantRfc1950Header">whether to emit an initial RFC1950 byte pair in the compressed stream.</param>
|
||||
/// <param name="bits">the number of window bits to use. If you don't know what this means, don't use this method.</param>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public int InitializeDeflate(CompressionLevel level, int bits, bool wantRfc1950Header)
|
||||
{
|
||||
this.CompressLevel = level;
|
||||
this.WindowBits = bits;
|
||||
return _InternalInitializeDeflate(wantRfc1950Header);
|
||||
}
|
||||
|
||||
private int _InternalInitializeDeflate(bool wantRfc1950Header)
|
||||
{
|
||||
if (istate != null) throw new ZlibException("You may not call InitializeDeflate() after calling InitializeInflate().");
|
||||
dstate = new DeflateManager();
|
||||
dstate.WantRfc1950HeaderBytes = wantRfc1950Header;
|
||||
|
||||
return dstate.Initialize(this, this.CompressLevel, this.WindowBits, this.Strategy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deflate one batch of data.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You must have set InputBuffer and OutputBuffer before calling this method.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// private void DeflateBuffer(CompressionLevel level)
|
||||
/// {
|
||||
/// int bufferSize = 1024;
|
||||
/// byte[] buffer = new byte[bufferSize];
|
||||
/// ZlibCodec compressor = new ZlibCodec();
|
||||
///
|
||||
/// Console.WriteLine("\n============================================");
|
||||
/// Console.WriteLine("Size of Buffer to Deflate: {0} bytes.", UncompressedBytes.Length);
|
||||
/// MemoryStream ms = new MemoryStream();
|
||||
///
|
||||
/// int rc = compressor.InitializeDeflate(level);
|
||||
///
|
||||
/// compressor.InputBuffer = UncompressedBytes;
|
||||
/// compressor.NextIn = 0;
|
||||
/// compressor.AvailableBytesIn = UncompressedBytes.Length;
|
||||
///
|
||||
/// compressor.OutputBuffer = buffer;
|
||||
///
|
||||
/// // pass 1: deflate
|
||||
/// do
|
||||
/// {
|
||||
/// compressor.NextOut = 0;
|
||||
/// compressor.AvailableBytesOut = buffer.Length;
|
||||
/// rc = compressor.Deflate(FlushType.None);
|
||||
///
|
||||
/// if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END)
|
||||
/// throw new Exception("deflating: " + compressor.Message);
|
||||
///
|
||||
/// ms.Write(compressor.OutputBuffer, 0, buffer.Length - compressor.AvailableBytesOut);
|
||||
/// }
|
||||
/// while (compressor.AvailableBytesIn > 0 || compressor.AvailableBytesOut == 0);
|
||||
///
|
||||
/// // pass 2: finish and flush
|
||||
/// do
|
||||
/// {
|
||||
/// compressor.NextOut = 0;
|
||||
/// compressor.AvailableBytesOut = buffer.Length;
|
||||
/// rc = compressor.Deflate(FlushType.Finish);
|
||||
///
|
||||
/// if (rc != ZlibConstants.Z_STREAM_END && rc != ZlibConstants.Z_OK)
|
||||
/// throw new Exception("deflating: " + compressor.Message);
|
||||
///
|
||||
/// if (buffer.Length - compressor.AvailableBytesOut > 0)
|
||||
/// ms.Write(buffer, 0, buffer.Length - compressor.AvailableBytesOut);
|
||||
/// }
|
||||
/// while (compressor.AvailableBytesIn > 0 || compressor.AvailableBytesOut == 0);
|
||||
///
|
||||
/// compressor.EndDeflate();
|
||||
///
|
||||
/// ms.Seek(0, SeekOrigin.Begin);
|
||||
/// CompressedBytes = new byte[compressor.TotalBytesOut];
|
||||
/// ms.Read(CompressedBytes, 0, CompressedBytes.Length);
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="flush">whether to flush all data as you deflate. Generally you will want to
|
||||
/// use Z_NO_FLUSH here, in a series of calls to Deflate(), and then call EndDeflate() to
|
||||
/// flush everything.
|
||||
/// </param>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public int Deflate(FlushType flush)
|
||||
{
|
||||
if (dstate == null)
|
||||
throw new ZlibException("No Deflate State!");
|
||||
return dstate.Deflate(flush);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// End a deflation session.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Call this after making a series of one or more calls to Deflate(). All buffers are flushed.
|
||||
/// </remarks>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public int EndDeflate()
|
||||
{
|
||||
if (dstate == null)
|
||||
throw new ZlibException("No Deflate State!");
|
||||
// TODO: dinoch Tue, 03 Nov 2009 15:39 (test this)
|
||||
//int ret = dstate.End();
|
||||
dstate = null;
|
||||
return ZlibConstants.Z_OK; //ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset a codec for another deflation session.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Call this to reset the deflation state. For example if a thread is deflating
|
||||
/// non-consecutive blocks, you can call Reset() after the Deflate(Sync) of the first
|
||||
/// block and before the next Deflate(None) of the second block.
|
||||
/// </remarks>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public void ResetDeflate()
|
||||
{
|
||||
if (dstate == null)
|
||||
throw new ZlibException("No Deflate State!");
|
||||
dstate.Reset();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Set the CompressionStrategy and CompressionLevel for a deflation session.
|
||||
/// </summary>
|
||||
/// <param name="level">the level of compression to use.</param>
|
||||
/// <param name="strategy">the strategy to use for compression.</param>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public int SetDeflateParams(CompressionLevel level, CompressionStrategy strategy)
|
||||
{
|
||||
if (dstate == null)
|
||||
throw new ZlibException("No Deflate State!");
|
||||
return dstate.SetParams(level, strategy);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Set the dictionary to be used for either Inflation or Deflation.
|
||||
/// </summary>
|
||||
/// <param name="dictionary">The dictionary bytes to use.</param>
|
||||
/// <returns>Z_OK if all goes well.</returns>
|
||||
public int SetDictionary(byte[] dictionary)
|
||||
{
|
||||
if (istate != null)
|
||||
return istate.SetDictionary(dictionary);
|
||||
|
||||
if (dstate != null)
|
||||
return dstate.SetDictionary(dictionary);
|
||||
|
||||
throw new ZlibException("No Inflate or Deflate state!");
|
||||
}
|
||||
|
||||
// Flush as much pending output as possible. All deflate() output goes
|
||||
// through this function so some applications may wish to modify it
|
||||
// to avoid allocating a large strm->next_out buffer and copying into it.
|
||||
// (See also read_buf()).
|
||||
internal void flush_pending()
|
||||
{
|
||||
int len = dstate.pendingCount;
|
||||
|
||||
if (len > AvailableBytesOut)
|
||||
len = AvailableBytesOut;
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
if (dstate.pending.Length <= dstate.nextPending ||
|
||||
OutputBuffer.Length <= NextOut ||
|
||||
dstate.pending.Length < (dstate.nextPending + len) ||
|
||||
OutputBuffer.Length < (NextOut + len))
|
||||
{
|
||||
throw new ZlibException(String.Format("Invalid State. (pending.Length={0}, pendingCount={1})",
|
||||
dstate.pending.Length, dstate.pendingCount));
|
||||
}
|
||||
|
||||
Array.Copy(dstate.pending, dstate.nextPending, OutputBuffer, NextOut, len);
|
||||
|
||||
NextOut += len;
|
||||
dstate.nextPending += len;
|
||||
TotalBytesOut += len;
|
||||
AvailableBytesOut -= len;
|
||||
dstate.pendingCount -= len;
|
||||
if (dstate.pendingCount == 0)
|
||||
{
|
||||
dstate.nextPending = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Read a new buffer from the current input stream, update the adler32
|
||||
// and total number of bytes read. All deflate() input goes through
|
||||
// this function so some applications may wish to modify it to avoid
|
||||
// allocating a large strm->next_in buffer and copying from it.
|
||||
// (See also flush_pending()).
|
||||
internal int read_buf(byte[] buf, int start, int size)
|
||||
{
|
||||
int len = AvailableBytesIn;
|
||||
|
||||
if (len > size)
|
||||
len = size;
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
AvailableBytesIn -= len;
|
||||
|
||||
if (dstate.WantRfc1950HeaderBytes)
|
||||
{
|
||||
_Adler32 = Adler.Adler32(_Adler32, InputBuffer, NextIn, len);
|
||||
}
|
||||
Array.Copy(InputBuffer, NextIn, buf, start, len);
|
||||
NextIn += len;
|
||||
TotalBytesIn += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
// ZlibConstants.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-November-03 18:50:19>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines constants used by the zlib class library. This
|
||||
// code is derived from the jzlib implementation of zlib, but
|
||||
// significantly modified. In keeping with the license for jzlib, the
|
||||
// copyright to that code is included here.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
// This program is based on zlib-1.1.3; credit to authors
|
||||
// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
|
||||
// and contributors of zlib.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
|
||||
using System;
|
||||
|
||||
namespace Ionic.Zlib
|
||||
{
|
||||
/// <summary>
|
||||
/// A bunch of constants used in the Zlib interface.
|
||||
/// </summary>
|
||||
public static class ZlibConstants
|
||||
{
|
||||
/// <summary>
|
||||
/// The maximum number of window bits for the Deflate algorithm.
|
||||
/// </summary>
|
||||
public const int WindowBitsMax = 15; // 32K LZ77 window
|
||||
|
||||
/// <summary>
|
||||
/// The default number of window bits for the Deflate algorithm.
|
||||
/// </summary>
|
||||
public const int WindowBitsDefault = WindowBitsMax;
|
||||
|
||||
/// <summary>
|
||||
/// indicates everything is A-OK
|
||||
/// </summary>
|
||||
public const int Z_OK = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the last operation reached the end of the stream.
|
||||
/// </summary>
|
||||
public const int Z_STREAM_END = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The operation ended in need of a dictionary.
|
||||
/// </summary>
|
||||
public const int Z_NEED_DICT = 2;
|
||||
|
||||
/// <summary>
|
||||
/// There was an error with the stream - not enough data, not open and readable, etc.
|
||||
/// </summary>
|
||||
public const int Z_STREAM_ERROR = -2;
|
||||
|
||||
/// <summary>
|
||||
/// There was an error with the data - not enough data, bad data, etc.
|
||||
/// </summary>
|
||||
public const int Z_DATA_ERROR = -3;
|
||||
|
||||
/// <summary>
|
||||
/// There was an error with the working buffer.
|
||||
/// </summary>
|
||||
public const int Z_BUF_ERROR = -5;
|
||||
|
||||
/// <summary>
|
||||
/// The size of the working buffer used in the ZlibCodec class. Defaults to 8192 bytes.
|
||||
/// </summary>
|
||||
#if NETCF
|
||||
public const int WorkingBufferSizeDefault = 8192;
|
||||
#else
|
||||
public const int WorkingBufferSizeDefault = 16384;
|
||||
#endif
|
||||
/// <summary>
|
||||
/// The minimum size of the working buffer used in the ZlibCodec class. Currently it is 128 bytes.
|
||||
/// </summary>
|
||||
public const int WorkingBufferSizeMin = 1024;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,725 @@
|
||||
// ZlibStream.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-31 14:53:33>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the ZlibStream class, which is similar in idea to
|
||||
// the System.IO.Compression.DeflateStream and
|
||||
// System.IO.Compression.GZipStream classes in the .NET BCL.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Ionic.Zlib
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Zlib stream for compression or decompression.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// The ZlibStream is a <see
|
||||
/// href="http://en.wikipedia.org/wiki/Decorator_pattern">Decorator</see> on a <see
|
||||
/// cref="System.IO.Stream"/>. It adds ZLIB compression or decompression to any
|
||||
/// stream.
|
||||
/// </para>
|
||||
///
|
||||
/// <para> Using this stream, applications can compress or decompress data via
|
||||
/// stream <c>Read()</c> and <c>Write()</c> operations. Either compresssion or
|
||||
/// decompression can occur through either reading or writing. The compression
|
||||
/// format used is ZLIB, which is documented in <see
|
||||
/// href="http://www.ietf.org/rfc/rfc1950.txt">IETF RFC 1950</see>, "ZLIB Compressed
|
||||
/// Data Format Specification version 3.3". This implementation of ZLIB always uses
|
||||
/// DEFLATE as the compression method. (see <see
|
||||
/// href="http://www.ietf.org/rfc/rfc1951.txt">IETF RFC 1951</see>, "DEFLATE
|
||||
/// Compressed Data Format Specification version 1.3.") </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The ZLIB format allows for varying compression methods, window sizes, and dictionaries.
|
||||
/// This implementation always uses the DEFLATE compression method, a preset dictionary,
|
||||
/// and 15 window bits by default.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This class is similar to <see cref="DeflateStream"/>, except that it adds the
|
||||
/// RFC1950 header and trailer bytes to a compressed stream when compressing, or expects
|
||||
/// the RFC1950 header and trailer bytes when decompressing. It is also similar to the
|
||||
/// <see cref="GZipStream"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <seealso cref="DeflateStream" />
|
||||
/// <seealso cref="GZipStream" />
|
||||
public class ZlibStream : System.IO.Stream
|
||||
{
|
||||
internal ZlibBaseStream _baseStream;
|
||||
bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Create a <c>ZlibStream</c> using the specified <c>CompressionMode</c>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// When mode is <c>CompressionMode.Compress</c>, the <c>ZlibStream</c>
|
||||
/// will use the default compression level. The "captive" stream will be
|
||||
/// closed when the <c>ZlibStream</c> is closed.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
/// This example uses a <c>ZlibStream</c> to compress a file, and writes the
|
||||
/// compressed data to another file.
|
||||
/// <code>
|
||||
/// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
|
||||
/// {
|
||||
/// using (var raw = System.IO.File.Create(fileToCompress + ".zlib"))
|
||||
/// {
|
||||
/// using (Stream compressor = new ZlibStream(raw, CompressionMode.Compress))
|
||||
/// {
|
||||
/// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
|
||||
/// int n;
|
||||
/// while ((n= input.Read(buffer, 0, buffer.Length)) != 0)
|
||||
/// {
|
||||
/// compressor.Write(buffer, 0, n);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// <code lang="VB">
|
||||
/// Using input As Stream = File.OpenRead(fileToCompress)
|
||||
/// Using raw As FileStream = File.Create(fileToCompress & ".zlib")
|
||||
/// Using compressor As Stream = New ZlibStream(raw, CompressionMode.Compress)
|
||||
/// Dim buffer As Byte() = New Byte(4096) {}
|
||||
/// Dim n As Integer = -1
|
||||
/// Do While (n <> 0)
|
||||
/// If (n > 0) Then
|
||||
/// compressor.Write(buffer, 0, n)
|
||||
/// End If
|
||||
/// n = input.Read(buffer, 0, buffer.Length)
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// <param name="stream">The stream which will be read or written.</param>
|
||||
/// <param name="mode">Indicates whether the ZlibStream will compress or decompress.</param>
|
||||
public ZlibStream(System.IO.Stream stream, CompressionMode mode)
|
||||
: this(stream, mode, CompressionLevel.Default, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <c>ZlibStream</c> using the specified <c>CompressionMode</c> and
|
||||
/// the specified <c>CompressionLevel</c>.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// When mode is <c>CompressionMode.Decompress</c>, the level parameter is ignored.
|
||||
/// The "captive" stream will be closed when the <c>ZlibStream</c> is closed.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
/// This example uses a <c>ZlibStream</c> to compress data from a file, and writes the
|
||||
/// compressed data to another file.
|
||||
///
|
||||
/// <code>
|
||||
/// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
|
||||
/// {
|
||||
/// using (var raw = System.IO.File.Create(fileToCompress + ".zlib"))
|
||||
/// {
|
||||
/// using (Stream compressor = new ZlibStream(raw,
|
||||
/// CompressionMode.Compress,
|
||||
/// CompressionLevel.BestCompression))
|
||||
/// {
|
||||
/// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
|
||||
/// int n;
|
||||
/// while ((n= input.Read(buffer, 0, buffer.Length)) != 0)
|
||||
/// {
|
||||
/// compressor.Write(buffer, 0, n);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Using input As Stream = File.OpenRead(fileToCompress)
|
||||
/// Using raw As FileStream = File.Create(fileToCompress & ".zlib")
|
||||
/// Using compressor As Stream = New ZlibStream(raw, CompressionMode.Compress, CompressionLevel.BestCompression)
|
||||
/// Dim buffer As Byte() = New Byte(4096) {}
|
||||
/// Dim n As Integer = -1
|
||||
/// Do While (n <> 0)
|
||||
/// If (n > 0) Then
|
||||
/// compressor.Write(buffer, 0, n)
|
||||
/// End If
|
||||
/// n = input.Read(buffer, 0, buffer.Length)
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// <param name="stream">The stream to be read or written while deflating or inflating.</param>
|
||||
/// <param name="mode">Indicates whether the ZlibStream will compress or decompress.</param>
|
||||
/// <param name="level">A tuning knob to trade speed for effectiveness.</param>
|
||||
public ZlibStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level)
|
||||
: this(stream, mode, level, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <c>ZlibStream</c> using the specified <c>CompressionMode</c>, and
|
||||
/// explicitly specify whether the captive stream should be left open after
|
||||
/// Deflation or Inflation.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// When mode is <c>CompressionMode.Compress</c>, the <c>ZlibStream</c> will use
|
||||
/// the default compression level.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This constructor allows the application to request that the captive stream
|
||||
/// remain open after the deflation or inflation occurs. By default, after
|
||||
/// <c>Close()</c> is called on the stream, the captive stream is also
|
||||
/// closed. In some cases this is not desired, for example if the stream is a
|
||||
/// <see cref="System.IO.MemoryStream"/> that will be re-read after
|
||||
/// compression. Specify true for the <paramref name="leaveOpen"/> parameter to leave the stream
|
||||
/// open.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// See the other overloads of this constructor for example code.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="stream">The stream which will be read or written. This is called the
|
||||
/// "captive" stream in other places in this documentation.</param>
|
||||
/// <param name="mode">Indicates whether the ZlibStream will compress or decompress.</param>
|
||||
/// <param name="leaveOpen">true if the application would like the stream to remain
|
||||
/// open after inflation/deflation.</param>
|
||||
public ZlibStream(System.IO.Stream stream, CompressionMode mode, bool leaveOpen)
|
||||
: this(stream, mode, CompressionLevel.Default, leaveOpen)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <c>ZlibStream</c> using the specified <c>CompressionMode</c>
|
||||
/// and the specified <c>CompressionLevel</c>, and explicitly specify
|
||||
/// whether the stream should be left open after Deflation or Inflation.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// This constructor allows the application to request that the captive
|
||||
/// stream remain open after the deflation or inflation occurs. By
|
||||
/// default, after <c>Close()</c> is called on the stream, the captive
|
||||
/// stream is also closed. In some cases this is not desired, for example
|
||||
/// if the stream is a <see cref="System.IO.MemoryStream"/> that will be
|
||||
/// re-read after compression. Specify true for the <paramref
|
||||
/// name="leaveOpen"/> parameter to leave the stream open.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// When mode is <c>CompressionMode.Decompress</c>, the level parameter is
|
||||
/// ignored.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example shows how to use a ZlibStream to compress the data from a file,
|
||||
/// and store the result into another file. The filestream remains open to allow
|
||||
/// additional data to be written to it.
|
||||
///
|
||||
/// <code>
|
||||
/// using (var output = System.IO.File.Create(fileToCompress + ".zlib"))
|
||||
/// {
|
||||
/// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
|
||||
/// {
|
||||
/// using (Stream compressor = new ZlibStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, true))
|
||||
/// {
|
||||
/// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
|
||||
/// int n;
|
||||
/// while ((n= input.Read(buffer, 0, buffer.Length)) != 0)
|
||||
/// {
|
||||
/// compressor.Write(buffer, 0, n);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// // can write additional data to the output stream here
|
||||
/// }
|
||||
/// </code>
|
||||
/// <code lang="VB">
|
||||
/// Using output As FileStream = File.Create(fileToCompress & ".zlib")
|
||||
/// Using input As Stream = File.OpenRead(fileToCompress)
|
||||
/// Using compressor As Stream = New ZlibStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, True)
|
||||
/// Dim buffer As Byte() = New Byte(4096) {}
|
||||
/// Dim n As Integer = -1
|
||||
/// Do While (n <> 0)
|
||||
/// If (n > 0) Then
|
||||
/// compressor.Write(buffer, 0, n)
|
||||
/// End If
|
||||
/// n = input.Read(buffer, 0, buffer.Length)
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// ' can write additional data to the output stream here.
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// <param name="stream">The stream which will be read or written.</param>
|
||||
///
|
||||
/// <param name="mode">Indicates whether the ZlibStream will compress or decompress.</param>
|
||||
///
|
||||
/// <param name="leaveOpen">
|
||||
/// true if the application would like the stream to remain open after
|
||||
/// inflation/deflation.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="level">
|
||||
/// A tuning knob to trade speed for effectiveness. This parameter is
|
||||
/// effective only when mode is <c>CompressionMode.Compress</c>.
|
||||
/// </param>
|
||||
public ZlibStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen)
|
||||
{
|
||||
_baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.ZLIB, leaveOpen);
|
||||
}
|
||||
|
||||
#region Zlib properties
|
||||
|
||||
/// <summary>
|
||||
/// This property sets the flush behavior on the stream.
|
||||
/// Sorry, though, not sure exactly how to describe all the various settings.
|
||||
/// </summary>
|
||||
virtual public FlushType FlushMode
|
||||
{
|
||||
get { return (this._baseStream._flushMode); }
|
||||
set
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("ZlibStream");
|
||||
this._baseStream._flushMode = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The size of the working buffer for the compression codec.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The working buffer is used for all stream operations. The default size is
|
||||
/// 1024 bytes. The minimum size is 128 bytes. You may get better performance
|
||||
/// with a larger buffer. Then again, you might not. You would have to test
|
||||
/// it.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Set this before the first call to <c>Read()</c> or <c>Write()</c> on the
|
||||
/// stream. If you try to set it afterwards, it will throw.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public int BufferSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._baseStream._bufferSize;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("ZlibStream");
|
||||
if (this._baseStream._workingBuffer != null)
|
||||
throw new ZlibException("The working buffer is already set.");
|
||||
if (value < ZlibConstants.WorkingBufferSizeMin)
|
||||
throw new ZlibException(String.Format("Don't be silly. {0} bytes?? Use a bigger buffer, at least {1}.", value, ZlibConstants.WorkingBufferSizeMin));
|
||||
this._baseStream._bufferSize = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Returns the total number of bytes input so far.</summary>
|
||||
virtual public long TotalIn
|
||||
{
|
||||
get { return this._baseStream._z.TotalBytesIn; }
|
||||
}
|
||||
|
||||
/// <summary> Returns the total number of bytes output so far.</summary>
|
||||
virtual public long TotalOut
|
||||
{
|
||||
get { return this._baseStream._z.TotalBytesOut; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.IO.Stream methods
|
||||
|
||||
/// <summary>
|
||||
/// Dispose the stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This may or may not result in a <c>Close()</c> call on the captive
|
||||
/// stream. See the constructors that have a <c>leaveOpen</c> parameter
|
||||
/// for more information.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This method may be invoked in two distinct scenarios. If disposing
|
||||
/// == true, the method has been called directly or indirectly by a
|
||||
/// user's code, for example via the public Dispose() method. In this
|
||||
/// case, both managed and unmanaged resources can be referenced and
|
||||
/// disposed. If disposing == false, the method has been called by the
|
||||
/// runtime from inside the object finalizer and this method should not
|
||||
/// reference other objects; in that case only unmanaged resources must
|
||||
/// be referenced or disposed.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="disposing">
|
||||
/// indicates whether the Dispose method was invoked by user code.
|
||||
/// </param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing && (this._baseStream != null))
|
||||
this._baseStream.Close();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream can be read.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The return value depends on whether the captive stream supports reading.
|
||||
/// </remarks>
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("ZlibStream");
|
||||
return _baseStream._stream.CanRead;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream supports Seek operations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Always returns false.
|
||||
/// </remarks>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream can be written.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The return value depends on whether the captive stream supports writing.
|
||||
/// </remarks>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("ZlibStream");
|
||||
return _baseStream._stream.CanWrite;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flush the stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("ZlibStream");
|
||||
_baseStream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reading this property always throws a <see cref="NotSupportedException"/>.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The position of the stream pointer.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Setting this property always throws a <see
|
||||
/// cref="NotSupportedException"/>. Reading will return the total bytes
|
||||
/// written out, if used in writing, or the total bytes read in, if used in
|
||||
/// reading. The count may refer to compressed bytes or uncompressed bytes,
|
||||
/// depending on how you've used the stream.
|
||||
/// </remarks>
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this._baseStream._streamMode == Ionic.Zlib.ZlibBaseStream.StreamMode.Writer)
|
||||
return this._baseStream._z.TotalBytesOut;
|
||||
if (this._baseStream._streamMode == Ionic.Zlib.ZlibBaseStream.StreamMode.Reader)
|
||||
return this._baseStream._z.TotalBytesIn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read data from the stream.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// If you wish to use the <c>ZlibStream</c> to compress data while reading,
|
||||
/// you can create a <c>ZlibStream</c> with <c>CompressionMode.Compress</c>,
|
||||
/// providing an uncompressed data stream. Then call <c>Read()</c> on that
|
||||
/// <c>ZlibStream</c>, and the data read will be compressed. If you wish to
|
||||
/// use the <c>ZlibStream</c> to decompress data while reading, you can create
|
||||
/// a <c>ZlibStream</c> with <c>CompressionMode.Decompress</c>, providing a
|
||||
/// readable compressed data stream. Then call <c>Read()</c> on that
|
||||
/// <c>ZlibStream</c>, and the data will be decompressed as it is read.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A <c>ZlibStream</c> can be used for <c>Read()</c> or <c>Write()</c>, but
|
||||
/// not both.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="buffer">
|
||||
/// The buffer into which the read data should be placed.</param>
|
||||
///
|
||||
/// <param name="offset">
|
||||
/// the offset within that data array to put the first byte read.</param>
|
||||
///
|
||||
/// <param name="count">the number of bytes to read.</param>
|
||||
///
|
||||
/// <returns>the number of bytes read</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("ZlibStream");
|
||||
return _baseStream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calling this method always throws a <see cref="NotSupportedException"/>.
|
||||
/// </summary>
|
||||
/// <param name="offset">
|
||||
/// The offset to seek to....
|
||||
/// IF THIS METHOD ACTUALLY DID ANYTHING.
|
||||
/// </param>
|
||||
/// <param name="origin">
|
||||
/// The reference specifying how to apply the offset.... IF
|
||||
/// THIS METHOD ACTUALLY DID ANYTHING.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>nothing. This method always throws.</returns>
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calling this method always throws a <see cref="NotSupportedException"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">
|
||||
/// The new value for the stream length.... IF
|
||||
/// THIS METHOD ACTUALLY DID ANYTHING.
|
||||
/// </param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write data to the stream.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// If you wish to use the <c>ZlibStream</c> to compress data while writing,
|
||||
/// you can create a <c>ZlibStream</c> with <c>CompressionMode.Compress</c>,
|
||||
/// and a writable output stream. Then call <c>Write()</c> on that
|
||||
/// <c>ZlibStream</c>, providing uncompressed data as input. The data sent to
|
||||
/// the output stream will be the compressed form of the data written. If you
|
||||
/// wish to use the <c>ZlibStream</c> to decompress data while writing, you
|
||||
/// can create a <c>ZlibStream</c> with <c>CompressionMode.Decompress</c>, and a
|
||||
/// writable output stream. Then call <c>Write()</c> on that stream,
|
||||
/// providing previously compressed data. The data sent to the output stream
|
||||
/// will be the decompressed form of the data written.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A <c>ZlibStream</c> can be used for <c>Read()</c> or <c>Write()</c>, but not both.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="buffer">The buffer holding data to write to the stream.</param>
|
||||
/// <param name="offset">the offset within that data array to find the first byte to write.</param>
|
||||
/// <param name="count">the number of bytes to write.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("ZlibStream");
|
||||
_baseStream.Write(buffer, offset, count);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compress a string into a byte array using ZLIB.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Uncompress it with <see cref="ZlibStream.UncompressString(byte[])"/>.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <seealso cref="ZlibStream.UncompressString(byte[])"/>
|
||||
/// <seealso cref="ZlibStream.CompressBuffer(byte[])"/>
|
||||
/// <seealso cref="GZipStream.CompressString(string)"/>
|
||||
///
|
||||
/// <param name="s">
|
||||
/// A string to compress. The string will first be encoded
|
||||
/// using UTF8, then compressed.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The string in compressed form</returns>
|
||||
public static byte[] CompressString(String s)
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
Stream compressor =
|
||||
new ZlibStream(ms, CompressionMode.Compress, CompressionLevel.BestCompression);
|
||||
ZlibBaseStream.CompressString(s, compressor);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compress a byte array into a new byte array using ZLIB.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Uncompress it with <see cref="ZlibStream.UncompressBuffer(byte[])"/>.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <seealso cref="ZlibStream.CompressString(string)"/>
|
||||
/// <seealso cref="ZlibStream.UncompressBuffer(byte[])"/>
|
||||
///
|
||||
/// <param name="b">
|
||||
/// A buffer to compress.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The data in compressed form</returns>
|
||||
public static byte[] CompressBuffer(byte[] b)
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
Stream compressor =
|
||||
new ZlibStream( ms, CompressionMode.Compress, CompressionLevel.BestCompression );
|
||||
|
||||
ZlibBaseStream.CompressBuffer(b, compressor);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Uncompress a ZLIB-compressed byte array into a single string.
|
||||
/// </summary>
|
||||
///
|
||||
/// <seealso cref="ZlibStream.CompressString(String)"/>
|
||||
/// <seealso cref="ZlibStream.UncompressBuffer(byte[])"/>
|
||||
///
|
||||
/// <param name="compressed">
|
||||
/// A buffer containing ZLIB-compressed data.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The uncompressed string</returns>
|
||||
public static String UncompressString(byte[] compressed)
|
||||
{
|
||||
using (var input = new MemoryStream(compressed))
|
||||
{
|
||||
Stream decompressor =
|
||||
new ZlibStream(input, CompressionMode.Decompress);
|
||||
|
||||
return ZlibBaseStream.UncompressString(compressed, decompressor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Uncompress a ZLIB-compressed byte array into a byte array.
|
||||
/// </summary>
|
||||
///
|
||||
/// <seealso cref="ZlibStream.CompressBuffer(byte[])"/>
|
||||
/// <seealso cref="ZlibStream.UncompressString(byte[])"/>
|
||||
///
|
||||
/// <param name="compressed">
|
||||
/// A buffer containing ZLIB-compressed data.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The data in uncompressed form</returns>
|
||||
public static byte[] UncompressBuffer(byte[] compressed)
|
||||
{
|
||||
using (var input = new MemoryStream(compressed))
|
||||
{
|
||||
Stream decompressor =
|
||||
new ZlibStream( input, CompressionMode.Decompress );
|
||||
|
||||
return ZlibBaseStream.UncompressBuffer(compressed, decompressor);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user