Is there a library to unzip .Z files using VB.NET?
Asked Answered
B

2

5

I need to unzip a list of .Z files which will be kept in a folder using Visual Basic.NET. For example, consider that there is a folder like C:\FilesToBeUnzipped. Inside this folder there will some 5 to 6 files with .Z as extension. I need to unzip all these .Z files and save the unzipped files in a folder like C:\UnzippedDataFiles.

Is this possible in VB.NET? Is there any free component or class library to achive it?

Bookman answered 28/12, 2010 at 6:16 Comment(0)
W
5

I also ran into this issue as well, just needed to decompress a *.Z file

Please note, I am not the original author of this however, wanted to provide copy of source on stack overflow in case the original site is no longer available.

The original post can be found here

Here is an example of how to use the class:

using System;
using System.IO;

using Ebixio.LZW;

class MainClass
{
    public static void Main(string[] args)
    {
        byte[] buffer = new byte[4096];
        string outFile = Path.GetFileNameWithoutExtension(args[0]);

        using (Stream inStream = new LzwInputStream(File.OpenRead(args[0])))
        using (FileStream outStream = File.Create(outFile)) {
            int read;
            while ((read = inStream.Read(buffer, 0, buffer.Length)) > 0) {
                outStream.Write(buffer, 0, read);
            }
        }
    }
}

Here is a modifed version of this as a static method that i made. It allows choosing the *.Z file location and a path to extract to:

public static void LzwUncompress(this string path, string unzippedPath) {
            byte[] buffer = new byte[4096];
            //string outFile = Path.GetFileNameWithoutExtension(path);

            using (Stream inStream = new Ebixio.LZW.LzwInputStream(File.OpenRead(path)))
            using (FileStream outStream = File.Create(unzippedPath)) {
                int read;
                while ((read = inStream.Read(buffer, 0, buffer.Length)) > 0) {
                    outStream.Write(buffer, 0, read);
                }
            }
        }

An example using the static method:

savePath.LzwUncompress(unzippedPath);

Add this class file to your project:

// LzwInputStream.cs
//
// Copyright (C) 2009 Gabriel Burca
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library.  Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
//

using System;
using System.IO;

namespace Ebixio.LZW {

    /// <summary>
    /// This class contains constants used for LZW
    /// </summary>
    sealed public class LzwConstants {
        /// <summary>
        /// Magic number found at start of LZW header: 0x1f 0x9d
        /// </summary>
        public const int MAGIC = 0x1f9d;

        /// <summary>
        /// Maximum number of bits per code
        /// </summary>
        public const int MAX_BITS = 16;

        /* 3rd header byte:
         * bit 0..4 Number of compression bits
         * bit 5    Extended header
         * bit 6    Free
         * bit 7    Block mode
         */

        /// <summary>
        /// Mask for 'number of compression bits'
        /// </summary>
        public const int BIT_MASK = 0x1f;

        /// <summary>
        /// Indicates the presence of a fourth header byte
        /// </summary>
        public const int EXTENDED_MASK = 0x20;
        //public const int FREE_MASK      = 0x40;

        /// <summary>
        /// Reserved bits
        /// </summary>
        public const int RESERVED_MASK = 0x60;

        /// <summary>
        /// Block compression: if table is full and compression rate is dropping,
        /// clear the dictionary.
        /// </summary>
        public const int BLOCK_MODE_MASK = 0x80;

        /// <summary>
        /// LZW file header size (in bytes)
        /// </summary>
        public const int HDR_SIZE = 3;

        /// <summary>
        /// Initial number of bits per code
        /// </summary>
        public const int INIT_BITS = 9;

        LzwConstants() {
        }
    }


    /// <summary>
    /// This filter stream is used to decompress a LZW format stream.
    /// Specifically, a stream that uses the LZC compression method.
    /// This file format is usually associated with the .Z file extension.
    ///
    /// See http://en.wikipedia.org/wiki/Compress
    /// See http://wiki.wxwidgets.org/Development:_Z_File_Format
    ///
    /// The file header consists of 3 (or optionally 4) bytes. The first two bytes
    /// contain the magic marker "0x1f 0x9d", followed by a byte of flags.
    ///
    /// Based on Java code by Ronald Tschalar, which in turn was based on the unlzw.c
    /// code in the gzip package.
    /// </summary>
    /// <example> This sample shows how to unzip a compressed file
    /// <code>
    /// using System;
    /// using System.IO;
    ///
    /// using Ebixio.LZW;
    ///
    /// class MainClass
    /// {
    ///     public static void Main(string[] args)
    ///     {
    ///         byte[] buffer = new byte[4096];
    ///         using (Stream inStream = new LzwInputStream(File.OpenRead(args[0])))
    ///         using (FileStream outStream = File.Create(Path.GetFileNameWithoutExtension(args[0]))) {
    ///             int read;
    ///             while ((read = inStream.Read(buffer, 0, buffer.Length)) > 0) {
    ///                 outStream.Write(buffer, 0, read);
    ///             }
    ///         }
    ///     }
    /// }
    /// </code>
    /// </example>
    public class LzwInputStream : Stream {
        byte[] one = new byte[1];
        bool headerParsed = false;

        // string table stuff
        private const int TBL_CLEAR = 0x100;
        private const int TBL_FIRST = TBL_CLEAR + 1;

        private int[] tabPrefix;
        private byte[] tabSuffix;
        private int[] zeros = new int[256];
        private byte[] stack;

        // various state
        private bool blockMode;
        private int nBits;
        private int maxBits;
        private int maxMaxCode;
        private int maxCode;
        private int bitMask;
        private int oldCode;
        private byte finChar;
        private int stackP;
        private int freeEnt;

        // input buffer
        private byte[] data = new byte[10000];
        private int bitPos = 0, end = 0, got = 0;
        private bool eof = false;
        private const int EXTRA = 64;

        /// <summary>
        /// Creates a LzwInputStream
        /// </summary>
        /// <param name="baseInputStream">
        /// The stream to read compressed data from (baseInputStream LZW format)
        /// </param>
        public LzwInputStream(Stream baseInputStream) {
            this.baseInputStream = baseInputStream;
        }

        /// <summary>
        /// See <see cref="System.IO.Stream.ReadByte"/>
        /// </summary>
        /// <returns></returns>
        public override int ReadByte() {
            int b = Read(one, 0, 1);
            if (b == 1)
                return (one[0] & 0xff);
            else
                return -1;
        }

        /// <summary>
        /// Moves the unread data in the buffer to the beginning and resets
        /// the pointers.
        /// </summary>
        /// <param name="bitPos"></param>
        /// <returns></returns>
        private int ResetBuf(int bitPos) {
            int pos = bitPos >> 3;
            Array.Copy(data, pos, data, 0, end - pos);
            end -= pos;
            return 0;
        }


        private void Fill() {
            got = baseInputStream.Read(data, end, data.Length - 1 - end);
            if (got > 0) end += got;
        }


        private void ParseHeader() {
            headerParsed = true;

            byte[] hdr = new byte[LzwConstants.HDR_SIZE];

            int result = baseInputStream.Read(hdr, 0, hdr.Length);

            // Check the magic marker
            if (result < 0) throw new ApplicationException("Failed to read LZW header");
            if (hdr[0] != (LzwConstants.MAGIC >> 8) || hdr[1] != (LzwConstants.MAGIC & 0xff)) {
                throw new ApplicationException(String.Format(
                    "Wrong LZW header. Magic bytes don't match. 0x{0:x2} 0x{1:x2}",
                    hdr[0], hdr[1]));
            }

            // Check the 3rd header byte
            blockMode = (hdr[2] & LzwConstants.BLOCK_MODE_MASK) > 0;
            maxBits = hdr[2] & LzwConstants.BIT_MASK;

            if (maxBits > LzwConstants.MAX_BITS) {
                throw new ApplicationException("Stream compressed with " + maxBits +
                    " bits, but decompression can only handle " +
                    LzwConstants.MAX_BITS + " bits.");
            }

            if ((hdr[2] & LzwConstants.RESERVED_MASK) > 0) {
                throw new ApplicationException("Unsupported bits set in the header.");
            }

            // Initialize variables
            maxMaxCode = 1 << maxBits;
            nBits = LzwConstants.INIT_BITS;
            maxCode = (1 << nBits) - 1;
            bitMask = maxCode;
            oldCode = -1;
            finChar = 0;
            freeEnt = blockMode ? TBL_FIRST : 256;

            tabPrefix = new int[1 << maxBits];
            tabSuffix = new byte[1 << maxBits];
            stack = new byte[1 << maxBits];
            stackP = stack.Length;

            for (int idx = 255; idx >= 0; idx--)
                tabSuffix[idx] = (byte)idx;
        }

        #region Stream Overrides

        /// <summary>
        /// Reads decompressed data into the provided buffer byte array
        /// </summary>
        /// <param name ="buffer">
        /// The array to read and decompress data into
        /// </param>
        /// <param name ="offset">
        /// The offset indicating where the data should be placed
        /// </param>
        /// <param name ="count">
        /// The number of bytes to decompress
        /// </param>
        /// <returns>The number of bytes read. Zero signals the end of stream</returns>
        public override int Read(byte[] buffer, int offset, int count) {
            if (!headerParsed) ParseHeader();

            if (eof) return -1;
            int start = offset;

            /* Using local copies of various variables speeds things up by as
             * much as 30% in Java! Performance not tested in C#.
             */
            int[] lTabPrefix = tabPrefix;
            byte[] lTabSuffix = tabSuffix;
            byte[] lStack = stack;
            int lNBits = nBits;
            int lMaxCode = maxCode;
            int lMaxMaxCode = maxMaxCode;
            int lBitMask = bitMask;
            int lOldCode = oldCode;
            byte lFinChar = finChar;
            int lStackP = stackP;
            int lFreeEnt = freeEnt;
            byte[] lData = data;
            int lBitPos = bitPos;


            // empty stack if stuff still left
            int sSize = lStack.Length - lStackP;
            if (sSize > 0) {
                int num = (sSize >= count) ? count : sSize;
                Array.Copy(lStack, lStackP, buffer, offset, num);
                offset += num;
                count -= num;
                lStackP += num;
            }

            if (count == 0) {
                stackP = lStackP;
                return offset - start;
            }


            // loop, filling local buffer until enough data has been decompressed
        MainLoop: do {
                if (end < EXTRA) Fill();

                int bitIn = (got > 0) ? (end - end % lNBits) << 3 :
                                        (end << 3) - (lNBits - 1);

                while (lBitPos < bitIn) {
                    #region A
                    // handle 1-byte reads correctly
                    if (count == 0) {
                        nBits = lNBits;
                        maxCode = lMaxCode;
                        maxMaxCode = lMaxMaxCode;
                        bitMask = lBitMask;
                        oldCode = lOldCode;
                        finChar = lFinChar;
                        stackP = lStackP;
                        freeEnt = lFreeEnt;
                        bitPos = lBitPos;

                        return offset - start;
                    }

                    // check for code-width expansion
                    if (lFreeEnt > lMaxCode) {
                        int nBytes = lNBits << 3;
                        lBitPos = (lBitPos - 1) +
                        nBytes - (lBitPos - 1 + nBytes) % nBytes;

                        lNBits++;
                        lMaxCode = (lNBits == maxBits) ? lMaxMaxCode :
                                                        (1 << lNBits) - 1;

                        lBitMask = (1 << lNBits) - 1;
                        lBitPos = ResetBuf(lBitPos);
                        goto MainLoop;
                    }
                    #endregion

                    #region B
                    // read next code
                    int pos = lBitPos >> 3;
                    int code = (((lData[pos] & 0xFF) |
                        ((lData[pos + 1] & 0xFF) << 8) |
                        ((lData[pos + 2] & 0xFF) << 16)) >>
                        (lBitPos & 0x7)) & lBitMask;

                    lBitPos += lNBits;

                    // handle first iteration
                    if (lOldCode == -1) {
                        if (code >= 256) throw new ApplicationException("corrupt input: " + code + " > 255");

                        lFinChar = (byte)(lOldCode = code);
                        buffer[offset++] = lFinChar;
                        count--;
                        continue;
                    }

                    // handle CLEAR code
                    if (code == TBL_CLEAR && blockMode) {
                        Array.Copy(zeros, 0, lTabPrefix, 0, zeros.Length);
                        lFreeEnt = TBL_FIRST - 1;

                        int nBytes = lNBits << 3;
                        lBitPos = (lBitPos - 1) + nBytes - (lBitPos - 1 + nBytes) % nBytes;
                        lNBits = LzwConstants.INIT_BITS;
                        lMaxCode = (1 << lNBits) - 1;
                        lBitMask = lMaxCode;

                        // Code tables reset

                        lBitPos = ResetBuf(lBitPos);
                        goto MainLoop;
                    }
                    #endregion

                    #region C
                    // setup
                    int inCode = code;
                    lStackP = lStack.Length;

                    // Handle KwK case
                    if (code >= lFreeEnt) {
                        if (code > lFreeEnt) {
                            throw new ApplicationException("corrupt input: code=" + code +
                                ", freeEnt=" + lFreeEnt);
                        }

                        lStack[--lStackP] = lFinChar;
                        code = lOldCode;
                    }

                    // Generate output characters in reverse order
                    while (code >= 256) {
                        lStack[--lStackP] = lTabSuffix[code];
                        code = lTabPrefix[code];
                    }

                    lFinChar = lTabSuffix[code];
                    buffer[offset++] = lFinChar;
                    count--;

                    // And put them out in forward order
                    sSize = lStack.Length - lStackP;
                    int num = (sSize >= count) ? count : sSize;
                    Array.Copy(lStack, lStackP, buffer, offset, num);
                    offset += num;
                    count -= num;
                    lStackP += num;
                    #endregion

                    #region D
                    // generate new entry in table
                    if (lFreeEnt < lMaxMaxCode) {
                        lTabPrefix[lFreeEnt] = lOldCode;
                        lTabSuffix[lFreeEnt] = lFinChar;
                        lFreeEnt++;
                    }

                    // Remember previous code
                    lOldCode = inCode;

                    // if output buffer full, then return
                    if (count == 0) {
                        nBits = lNBits;
                        maxCode = lMaxCode;
                        bitMask = lBitMask;
                        oldCode = lOldCode;
                        finChar = lFinChar;
                        stackP = lStackP;
                        freeEnt = lFreeEnt;
                        bitPos = lBitPos;

                        return offset - start;
                    }
                    #endregion
                }   // while

                lBitPos = ResetBuf(lBitPos);

            } while (got > 0);  // do..while

            nBits = lNBits;
            maxCode = lMaxCode;
            bitMask = lBitMask;
            oldCode = lOldCode;
            finChar = lFinChar;
            stackP = lStackP;
            freeEnt = lFreeEnt;
            bitPos = lBitPos;

            eof = true;
            return offset - start;
        }


        /// <summary>
        /// Gets a value indicating whether the current stream supports reading
        /// </summary>
        public override bool CanRead {
            get {
                return baseInputStream.CanRead;
            }
        }

        /// <summary>
        /// Gets a value of false indicating seeking is not supported for this stream.
        /// </summary>
        public override bool CanSeek {
            get {
                return false;
            }
        }

        /// <summary>
        /// Gets a value of false indicating that this stream is not writeable.
        /// </summary>
        public override bool CanWrite {
            get {
                return false;
            }
        }

        /// <summary>
        /// A value representing the length of the stream in bytes.
        /// Always throws a NotSupportedException when attempting to get the length.
        /// </summary>
        /// <exception cref="NotSupportedException">Attempting to get the length</exception>
        public override long Length {
            get {
                throw new NotSupportedException("LzwInputStream Length not supported");
            }
        }

        /// <summary>
        /// The current position within the stream.
        /// Throws a NotSupportedException when attempting to set the position.
        /// </summary>
        /// <exception cref="NotSupportedException">Attempting to set the position</exception>
        public override long Position {
            get {
                return baseInputStream.Position;
            }
            set {
                throw new NotSupportedException("LzwInputStream Position not supported");
            }
        }

        /// <summary>
        /// Flushes the baseInputStream
        /// </summary>
        public override void Flush() {
            baseInputStream.Flush();
        }

        /// <summary>
        /// Sets the position within the current stream
        /// Always throws a NotSupportedException
        /// </summary>
        /// <param name="offset">The relative offset to seek to.</param>
        /// <param name="origin">The <see cref="SeekOrigin"/> defining where to seek from.</param>
        /// <returns>The new position in the stream.</returns>
        /// <exception cref="NotSupportedException">Any access</exception>
        public override long Seek(long offset, SeekOrigin origin) {
            throw new NotSupportedException("LzwInputStream Seek not supported");
        }

        /// <summary>
        /// Set the length of the current stream
        /// Always throws a NotSupportedException
        /// </summary>
        /// <param name="value">The new length value for the stream.</param>
        /// <exception cref="NotSupportedException">Any access</exception>
        public override void SetLength(long value) {
            throw new NotSupportedException("LzwInputStream SetLength not supported");
        }

        /// <summary>
        /// Writes a sequence of bytes to stream and advances the current position
        /// This method always throws a NotSupportedException
        /// </summary>
        /// <param name="buffer">Thew buffer containing data to write.</param>
        /// <param name="offset">The offset of the first byte to write.</param>
        /// <param name="count">The number of bytes to write.</param>
        /// <exception cref="NotSupportedException">Any access</exception>
        public override void Write(byte[] buffer, int offset, int count) {
            throw new NotSupportedException("LzwInputStream Write not supported");
        }

        /// <summary>
        /// Writes one byte to the current stream and advances the current position
        /// Always throws a NotSupportedException
        /// </summary>
        /// <param name="value">The byte to write.</param>
        /// <exception cref="NotSupportedException">Any access</exception>
        public override void WriteByte(byte value) {
            throw new NotSupportedException("LzwInputStream WriteByte not supported");
        }

        /// <summary>
        /// Entry point to begin an asynchronous write.  Always throws a NotSupportedException.
        /// </summary>
        /// <param name="buffer">The buffer to write data from</param>
        /// <param name="offset">Offset of first byte to write</param>
        /// <param name="count">The maximum number of bytes to write</param>
        /// <param name="callback">The method to be called when the asynchronous write operation is completed</param>
        /// <param name="state">A user-provided object that distinguishes this particular asynchronous write request from other requests</param>
        /// <returns>An <see cref="System.IAsyncResult">IAsyncResult</see> that references the asynchronous write</returns>
        /// <exception cref="NotSupportedException">Any access</exception>
        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) {
            throw new NotSupportedException("LzwInputStream BeginWrite not supported");
        }

        /// <summary>
        /// Closes the input stream.  When <see cref="IsStreamOwner"></see>
        /// is true the underlying stream is also closed.
        /// </summary>
        public override void Close() {
            if (!isClosed) {
                isClosed = true;
                if (isStreamOwner) {
                    baseInputStream.Close();
                }
            }
        }

        #endregion


        /// <summary>
        /// Get/set flag indicating ownership of underlying stream.
        /// When the flag is true <see cref="Close"/> will close the underlying stream also.
        /// </summary>
        /// <remarks>
        /// The default value is true.
        /// </remarks>
        public bool IsStreamOwner {
            get { return isStreamOwner; }
            set { isStreamOwner = value; }
        }

        /// <summary>
        /// Base stream the inflater reads from.
        /// </summary>
        protected Stream baseInputStream;

        /// <summary>
        /// Flag indicating wether this instance has been closed or not.
        /// </summary>
        bool isClosed;

        /// <summary>
        /// Flag indicating wether this instance is designated the stream owner.
        /// When closing if this flag is true the underlying stream is closed.
        /// </summary>
        bool isStreamOwner = true;
    }
}
Wagonage answered 12/1, 2015 at 16:10 Comment(0)
D
2

How about SevenZipSharp?

It's a managed wrapper around the 7-Zip compression/decompression engine that is released under the LGPL. Although the code is written in C#, all of the functions provided by the library are fully accessible in your VB.NET code.

If you're a do-it-yourselfer, check out the 7-Zip SDK.

I see SharpZipLib and DotNetZip recommended a lot, but I am not sure if they support .Z archives.
I know that 7-Zip does, not to mention it's one of my favorite open source apps.

Dm answered 28/12, 2010 at 7:31 Comment(1)
DotNetZip does not do .Z files.Jenijenica

© 2022 - 2024 — McMap. All rights reserved.