Back to... Zip-Ada

Source file : unzip-decompress.adb



--  UnZip.Decompress
--------------------
--  Internal to the UnZip package. See root package (UnZip) for details & credits.

--  Legal licensing note:

--  Copyright (c) 2007 .. 2024 Gautier de Montmollin
--  SWITZERLAND

--  Permission is hereby granted, free of charge, to any person obtaining a copy
--  of this software and associated documentation files (the "Software"), to deal
--  in the Software without restriction, including without limitation the rights
--  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
--  copies of the Software, and to permit persons to whom the Software is
--  furnished to do so, subject to the following conditions:

--  The above copyright notice and this permission notice shall be included in
--  all copies or substantial portions of the Software.

--  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
--  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
--  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
--  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
--  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
--  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
--  THE SOFTWARE.

--  NB: this is the MIT License, as found on the site
--  http://www.opensource.org/licenses/mit-license.php

with Zip.CRC_Crypto, UnZip.Decompress.Huffman, BZip2.Decoding, LZMA.Decoding;

with Ada.Exceptions, Ada.Streams.Stream_IO, Ada.Text_IO, Interfaces;

package body UnZip.Decompress is

  procedure Decompress_Data
    (zip_file                   : in out Zip_Streams.Root_Zipstream_Type'Class;
     --  zip_file must be open and its index is meant
     --  to point to the beginning of compressed data
     format                     : in     Zip.PKZip_method;
     write_mode                 : in     Write_Mode_Type;
     output_file_name           : in     String;  --  relevant only if mode = write_to_file
     output_memory_access       :    out p_Stream_Element_Array;  -- \ = write_to_memory
     output_stream_access       : in     p_Stream;                -- \ = write_to_stream
     feedback                   : in     Zip.Feedback_Proc;
     explode_literal_tree       : in     Boolean;  --  relevant for the "explode" format
     explode_slide_8KB_LZMA_EOS : in     Boolean;  --  relevant for the "explode" and "LZMA" formats
     data_descriptor_after_data : in     Boolean;
     is_encrypted               : in     Boolean;
     password                   : in out Ada.Strings.Unbounded.Unbounded_String;
     get_new_password           : in     Get_Password_Proc;  --  if null, initial pwd must fit
     hint                       : in out Zip.Headers.Local_File_Header)
  is
    --  Disable AdaControl rule for detecting global variables, they have become local here.
    --## RULE OFF Directly_Accessed_Globals
    --
    --  I/O Buffers: Size of input buffer
    inbuf_size : constant := 16#8000#;  --  (orig: 16#1000# B =  4 KiB)
    --  I/O Buffers: Size of sliding dictionary and output buffer
    wsize     : constant := 16#10000#;  --  (orig: 16#8000# B = 32 KiB)

    ----------------------------------------------------------------------------
    -- Specifications of UnZ_* packages (remain of Info Zip's code structure) --
    ----------------------------------------------------------------------------
    use Ada.Exceptions, Interfaces;

    package UnZ_Glob is -- Not global anymore, since local to Decompress_data :-)
      --  I/O Buffers: Sliding dictionary for unzipping, and output buffer as well
      slide : Zip.Byte_Buffer (0 .. wsize);
      slide_index : Integer := 0;  --  Current Position in slide
      --  I/O Buffers: Input buffer
      inbuf : Zip.Byte_Buffer (0 .. inbuf_size - 1);
      inpos, readpos : Integer;  --  pos. in input buffer, pos. read from file
      compsize,            --  compressed size of file
      reachedsize,         --  number of bytes read from zipfile
      uncompsize,          --  uncompressed size of file
      effective_writes : Zip.Zip_64_Data_Size_Type;
      --  ^ count of effective bytes written or tested, for feedback only
      percents_done    : Natural;
      crc32val : Unsigned_32;  -- crc calculated from data
      uncompressed_index  : Ada.Streams.Stream_Element_Offset;
    end UnZ_Glob;

    Zip_EOF  : Boolean; -- read over end of zip section for this file
    LZ77_dump : Ada.Text_IO.File_Type;

    package UnZ_IO is
      out_bin_file : Ada.Streams.Stream_IO.File_Type;
      out_txt_file : Ada.Text_IO.File_Type;
      last_char    : Character := ' ';

      procedure Init_Buffers;

      procedure Read_Byte_no_Decrypt (bt : out Zip.Byte);
        pragma Inline (Read_Byte_no_Decrypt);

      function Read_Byte_Decrypted return Unsigned_8;  --  NB: reading goes on a while even if
        pragma Inline (Read_Byte_Decrypted);           --  Zip_EOF is set: just gives garbage

      package Bit_buffer is
        procedure Init;
        --  Read at least n bits into the bit buffer, returns the n first bits
        function Read (n : Natural) return Integer;
          pragma Inline (Read);
        function Read_U32 (n : Natural) return Unsigned_32;
          pragma Inline (Read_U32);
        --  Inverts (NOT operator) the result before masking by n bits
        function Read_inverted (n : Natural) return Integer;
          pragma Inline (Read_inverted);
        --  Dump n bits no longer needed from the bit buffer
        procedure Dump (n : Natural);
          pragma Inline (Dump);
        procedure Dump_to_byte_boundary;
        function Read_and_dump (n : Natural) return Integer;
          pragma Inline (Read_and_dump);
        function Read_and_dump_U32 (n : Natural) return Unsigned_32;
          pragma Inline (Read_and_dump_U32);
      end Bit_buffer;

      procedure Flush (x : Natural);  --  directly from slide to output stream

      procedure Flush_if_full (W : in out Integer; unflushed : in out Boolean);
      pragma Inline (Flush_if_full);

      procedure Flush_if_full (W : in out Integer);
      pragma Inline (Flush_if_full);

      procedure Copy (distance, copy_length : Natural; index : in out Natural);
      pragma Inline (Copy);

      procedure Copy_or_zero (
        distance, length :        Natural;
        index            : in out Natural;
        unflushed        : in out Boolean);
      pragma Inline (Copy_or_zero);

      procedure Delete_output;  --  an error has occured (bad compressed data)

    end UnZ_IO;

    package UnZ_Meth is
      procedure Copy_stored;
      procedure Unshrink;
      subtype Reduction_factor is Integer range 1 .. 4;
      procedure Unreduce (factor : Reduction_factor);
      procedure Explode (literal_tree, slide_8_KB : Boolean);
      deflate_e_mode : Boolean := False;
      procedure Inflate;
      procedure Bunzip2;      --  Nov-2009
      procedure LZMA_Decode;  --  Jun-2014
    end UnZ_Meth;

    procedure Process_feedback (new_bytes : Zip.Zip_64_Data_Size_Type) is
    pragma Inline (Process_feedback);
      new_percents_done : Natural;
      user_aborting : Boolean;
      use Zip;
    begin
      if feedback = null or UnZ_Glob.uncompsize = 0 then
        return; -- no feedback proc. or cannot calculate percentage
      end if;
      UnZ_Glob.effective_writes := UnZ_Glob.effective_writes + new_bytes;
      new_percents_done := Natural (
        (100.0 * Float (UnZ_Glob.effective_writes)) / Float (UnZ_Glob.uncompsize)
      );
      if new_percents_done > UnZ_Glob.percents_done then
        feedback (
          percents_done => new_percents_done,
          entry_skipped => False,
          user_abort    => user_aborting
        );
        if user_aborting then
          raise User_abort;
        end if;
        UnZ_Glob.percents_done := new_percents_done;
      end if;
    end Process_feedback;

    use Zip.CRC_Crypto;
    local_crypto_pack : Crypto_pack;

    ------------------------------
    -- Bodies of UnZ_* packages --
    ------------------------------
    package body UnZ_IO is

      procedure Init_Buffers is
      begin
        UnZ_Glob.inpos   :=  0;  --  Input buffer position
        UnZ_Glob.readpos := -1;  --  Nothing read
        UnZ_Glob.slide_index := 0;
        UnZ_Glob.reachedsize      := 0;
        UnZ_Glob.effective_writes := 0;
        UnZ_Glob.percents_done    := 0;
        Zip_EOF := False;
        Zip.CRC_Crypto.Init (UnZ_Glob.crc32val);
        Bit_buffer.Init;
      end Init_Buffers;

      procedure Process_compressed_end_reached is
      begin
        if Zip_EOF then  --  We came already here once
          raise Zip.Archive_corrupted with
            "Decoding went past compressed data size plus one buffer length";
          --  Avoid infinite loop on data with exactly buffer's length and no end marker
        else
          UnZ_Glob.readpos := UnZ_Glob.inbuf'Length;
          --  Simulates reading -> no blocking.
          --  The buffer is full of "random" data.
          --  A correct compressed stream will hit its own end-of-compressed-stream.
          --  On a corrupted data we will get a wrong code or a CRC error on the way.
          Zip_EOF := True;
        end if;
      end Process_compressed_end_reached;

      procedure Read_buffer is
      begin
        if full_trace then
          Ada.Text_IO.Put ("[Read_buffer...");
        end if;
        if UnZ_Glob.reachedsize > UnZ_Glob.compsize + 2 then
          --  +2: last code is smaller than requested!
          Process_compressed_end_reached;
        else
          begin
            Zip.Block_Read (
              stream        => zip_file,
              buffer        => UnZ_Glob.inbuf,
              actually_read => UnZ_Glob.readpos
            );
          exception
            when others => -- I/O error
              Process_compressed_end_reached;
          end;
          if UnZ_Glob.readpos = 0 then -- No byte at all was read
            Process_compressed_end_reached;
          end if;
          UnZ_Glob.reachedsize :=
            UnZ_Glob.reachedsize + Zip.Zip_64_Data_Size_Type (UnZ_Glob.readpos);
          UnZ_Glob.readpos := UnZ_Glob.readpos - 1;  --  Reason: index of inbuf starts at 0
        end if;
        UnZ_Glob.inpos := 0;
        if full_trace then
          Ada.Text_IO.Put_Line ("finished]");
        end if;
      end Read_buffer;

      procedure Read_Byte_no_Decrypt (bt : out Zip.Byte) is
      begin
        if UnZ_Glob.inpos > UnZ_Glob.readpos then
          Read_buffer;
        end if;
        bt := UnZ_Glob.inbuf (UnZ_Glob.inpos);
        UnZ_Glob.inpos := UnZ_Glob.inpos + 1;
      end Read_Byte_no_Decrypt;

      function Read_Byte_Decrypted return Unsigned_8 is
        bt : Zip.Byte;
      begin
        Read_Byte_no_Decrypt (bt);
        Decode (local_crypto_pack, bt);
        return bt;
      end Read_Byte_Decrypted;

      package body Bit_buffer is
        B : Unsigned_32;
        K : Integer;

        procedure Init is
        begin
          B := 0;
          K := 0;
        end Init;

        procedure Need (n : Natural) is
          pragma Inline (Need);
        begin
          while K < n loop
            B := B or Shift_Left (Unsigned_32 (Read_Byte_Decrypted), K);
            K := K + 8;
          end loop;
        end Need;

        procedure Dump (n : Natural) is
        begin
          B := Shift_Right (B, n);
          K := K - n;
        end Dump;

        procedure Dump_to_byte_boundary is
        begin
          Dump (K mod 8);
        end Dump_to_byte_boundary;

        function Read_U32 (n : Natural) return Unsigned_32 is
        begin
          Need (n);
          return B and (Shift_Left (1, n) - 1);
        end Read_U32;

        function Read_inverted (n : Natural) return Integer is
        begin
          Need (n);
          return Integer ((not B) and (Shift_Left (1, n) - 1));
        end Read_inverted;

        function Read (n : Natural) return Integer is
        begin
          return Integer (Read_U32 (n));
        end Read;

        function Read_and_dump (n : Natural) return Integer is
          res : Integer;
        begin
          res := Read (n);
          Dump (n);
          return res;
        end Read_and_dump;

        function Read_and_dump_U32 (n : Natural) return Unsigned_32 is
          res : Unsigned_32;
        begin
          res := Read_U32 (n);
          Dump (n);
          return res;
        end Read_and_dump_U32;

      end Bit_buffer;

      procedure Flush (x : Natural) is
        use Zip, Ada.Streams;
      begin
        if full_trace then
          Ada.Text_IO.Put ("[Flush...");
        end if;
        begin
          case write_mode is
            when write_to_binary_file =>
              Block_Write (Ada.Streams.Stream_IO.Stream (out_bin_file).all, UnZ_Glob.slide (0 .. x - 1));
            when write_to_text_file =>
              Write_as_Text
                (UnZ_IO.out_txt_file, UnZ_Glob.slide (0 .. x - 1), UnZ_IO.last_char);
            when write_to_memory =>
              for i in 0 .. x - 1 loop
                output_memory_access (UnZ_Glob.uncompressed_index) :=
                  Ada.Streams.Stream_Element (UnZ_Glob.slide (i));
                UnZ_Glob.uncompressed_index := UnZ_Glob.uncompressed_index + 1;
              end loop;
            when write_to_stream =>
              Block_Write (output_stream_access.all, UnZ_Glob.slide (0 .. x - 1));
            when just_test =>
              null;
          end case;
        exception
          when others =>
            raise UnZip.Write_Error;
        end;
        Zip.CRC_Crypto.Update (UnZ_Glob.crc32val, UnZ_Glob.slide (0 .. x - 1));
        Process_feedback (Zip_64_Data_Size_Type (x));
        if full_trace then
          Ada.Text_IO.Put_Line ("finished]");
        end if;
      end Flush;

      procedure Flush_if_full (W : in out Integer; unflushed : in out Boolean) is
      begin
        if W = wsize then
          Flush (wsize);
          W := 0;
          unflushed := False;
        end if;
      end Flush_if_full;

      procedure Flush_if_full (W : in out Integer) is
      begin
        if W = wsize then
          Flush (wsize);
          W := 0;
        end if;
      end Flush_if_full;

      ----------------------------------------------------
      -- Reproduction of sequences in the output slide. --
      ----------------------------------------------------

      --  Internal:

      procedure Adjust_to_Slide (
          source         : in out Integer;
          remain         : in out Natural;
          part           :    out Integer;
          index          :        Integer)
      is
        pragma Inline (Adjust_to_Slide);
      begin
        source := source mod wsize;
        --  source and index are now in 0 .. WSize-1
        if source > index then
          part := wsize - source;
        else
          part := wsize - index;
        end if;
        --  NB: part is in 1..WSize (part cannot be 0)
        if part > remain then
          part := remain;
        end if;
        --  Now part <= remain
        remain := remain - part;
        --  NB: remain cannot be < 0
      end Adjust_to_Slide;

      procedure Copy_range (source, index : in out Natural; amount : Positive) is
        pragma Inline (Copy_range);
      begin
        if full_trace then
          Ada.Text_IO.Put (
            "(Copy_range: source=" & Integer'Image (source) &
            " index=" & Integer'Image (index) &
            " amount=" & Integer'Image (amount));
        end if;
        if abs (index - source) < amount then
          if full_trace and then source < index then
            Ada.Text_IO.Put (
              "; replicates" &
              Integer'Image (amount) & " /" & Integer'Image (index - source) &
              " )"
            );
            --  ...times the range source..index-1
          end if;
          --  if source >= index, the effect of copy is just like the non-overlapping case
          for count in reverse 1 .. amount loop
            UnZ_Glob.slide (index) := UnZ_Glob.slide (source);
            index  := index  + 1;
            source := source + 1;
          end loop;
        else  --  non-overlapping -> copy slice
          UnZ_Glob.slide (index .. index + amount - 1) :=
            UnZ_Glob.slide (source .. source + amount - 1);
          index  := index  + amount;
          source := source + amount;
        end if;
        if full_trace then
          Ada.Text_IO.Put (')');
        end if;
      end Copy_range;

      --  The copying routines:

      procedure Copy (distance, copy_length : Natural; index : in out Natural) is
        source, part, remain : Integer;
      begin
        if some_trace then
          Ada.Text_IO.Put_Line (LZ77_dump, "DLE" & Integer'Image (distance) & Integer'Image (copy_length));
        end if;
        source := index - distance;
        remain := copy_length;
        loop
          Adjust_to_Slide (source, remain, part, index);
          Copy_range (source, index, part);
          Flush_if_full (index);
          exit when remain = 0;
        end loop;
      end Copy;

      procedure Copy_or_zero (
          distance, length :        Natural;
          index            : in out Natural;
          unflushed        : in out Boolean)
      is
        source, part, remain : Integer;
      begin
        source := index - distance;
        remain := length;
        loop
          Adjust_to_Slide (source, remain, part, index);
          if unflushed and then index <= source then
            UnZ_Glob.slide (index .. index + part - 1) := (others => 0);
            index  := index  + part;
            source := source + part;
          else
            Copy_range (source, index, part);
          end if;
          Flush_if_full (index, unflushed);
          exit when remain = 0;
        end loop;
      end Copy_or_zero;

      procedure Delete_output is  --  an error has occured (bad compressed data)
      begin
        if no_trace then  --  if there is a trace, we are debugging
          case write_mode is   --  and want to keep the malformed file
            when write_to_binary_file =>
              Ada.Streams.Stream_IO.Delete (UnZ_IO.out_bin_file);
            when write_to_text_file =>
              Ada.Text_IO.Delete (UnZ_IO.out_txt_file);
            when write_to_memory | write_to_stream | just_test =>
              null; -- Nothing to delete!
          end case;
        end if;
      end Delete_output;

    end UnZ_IO;

    procedure Init_Decryption (password_for_keys : String; crc_check : Unsigned_32) is
      c : Zip.Byte := 0;
      t : Unsigned_32;
    begin
      --  Step 1 - Initializing the encryption keys
      Init_Keys (local_crypto_pack, password_for_keys);
      --  Step 2 - Decrypting the encryption header. 11 bytes are random,
      --           just to shuffle the keys, 1 byte is from the CRC value.
      Set_Mode (local_crypto_pack, encrypted);
      for i in 1 .. 12 loop
        UnZ_IO.Read_Byte_no_Decrypt (c);
        Decode (local_crypto_pack, c);
      end loop;
      t := Zip_Streams.Calendar.Convert (hint.file_timedate);
      --  Last byte used to check password; 1/256 probability of success with any password!
      if c /= Zip.Byte (Shift_Right (crc_check, 24)) and not
        --  Dec. 2012. This is a feature of Info-Zip (crypt.c), not of PKWARE.
        --  Since CRC is only known at the end of a one-way stream
        --  compression, and cannot be written back, they are using a byte of
        --  the time stamp instead. This is NOT documented in PKWARE's appnote.txt v.6.3.3.
        (data_descriptor_after_data and c = Zip.Byte (Shift_Right (t, 8) and 16#FF#))
      then
        raise UnZip.Wrong_password;
      end if;
    end Init_Decryption;

    package body UnZ_Meth is

      --------[ Method: Unshrink ]--------

      --  Original in Pascal written by Christian Ghisler.

      Initial_Code_Size : constant := 9;
      Maximum_Code_Size : constant := 13;
      Max_Code          : constant := 2 ** Maximum_Code_Size;
      Max_Stack         : constant := 2 ** Maximum_Code_Size;

      --  Rest of slide=write buffer =766 bytes

      Write_Max : constant := wsize - 3 * (Max_Code - 256) - Max_Stack - 2;

      Next_Free : Integer;      --  Next free code in trie
      Write_Ptr : Integer;      --  Pointer to output buffer

      Writebuf : Zip.Byte_Buffer (0 .. Write_Max);  --  Write buffer

      procedure Unshrink_Flush is
        use Zip, Ada.Streams, Ada.Streams.Stream_IO;
      begin
        if full_trace then
          Ada.Text_IO.Put ("[Unshrink_Flush]");
        end if;
        begin
          case write_mode is
            when write_to_binary_file =>
              Block_Write (Stream (UnZ_IO.out_bin_file).all, Writebuf (0 .. Write_Ptr - 1));
            when write_to_text_file =>
              Zip.Write_as_Text (UnZ_IO.out_txt_file, Writebuf (0 .. Write_Ptr - 1), UnZ_IO.last_char);
            when write_to_memory =>
              for I in 0 .. Write_Ptr - 1 loop
                output_memory_access (UnZ_Glob.uncompressed_index) :=
                  Stream_Element (Writebuf (I));
                UnZ_Glob.uncompressed_index :=  UnZ_Glob.uncompressed_index + 1;
              end loop;
            when write_to_stream =>
              Block_Write (output_stream_access.all, Writebuf (0 .. Write_Ptr - 1));
            when just_test =>
              null;
          end case;
        exception
          when others =>
            raise UnZip.Write_Error;
        end;
        Zip.CRC_Crypto.Update (UnZ_Glob.crc32val, Writebuf (0 .. Write_Ptr - 1));
        Process_feedback (Zip_64_Data_Size_Type (Write_Ptr));
      end Unshrink_Flush;

      procedure UD_Write_Byte (B : Zip.Byte) is
      begin
        Writebuf (Write_Ptr) := B;
        Write_Ptr := Write_Ptr + 1;
        if Write_Ptr > Write_Max then
          Unshrink_Flush;
          Write_Ptr := 0;
        end if;
      end UD_Write_Byte;

      procedure Unshrink is
        S : Zip.Zip_64_Data_Size_Type := UnZ_Glob.uncompsize;

        Last_Incode     : Integer;
        Last_Outcode    : Zip.Byte;
        Code_Size       : Integer := Initial_Code_Size;  --  Actual code size [9 .. 13]
        Actual_Max_Code : Integer;  --  Max code to be searched for leaf nodes
        First_Entry     : constant := 257;
        Previous_Code   : array (First_Entry .. Max_Code) of Integer;
        Stored_Literal  : array (First_Entry .. Max_Code) of Zip.Byte;

        procedure Clear_Leaf_Nodes is
          Is_Leaf : array (First_Entry .. Max_Code) of Boolean := (others => True);
          Pc : Integer;  --  Previous code
        begin
          if full_trace then
            Ada.Text_IO.Put ("[Clear leaf nodes @ pos" &
              Zip.Zip_64_Data_Size_Type'Image (UnZ_Glob.uncompsize - S) &
              "; old Next_Free =" & Integer'Image (Next_Free));
          end if;
          for I in First_Entry .. Actual_Max_Code loop
            Pc := Previous_Code (I);
            if  Pc > 256 then
              --  Pc is in a tree as well
              Is_Leaf (Pc) := False;
            end if;
          end loop;

          --  Build new free list
          Pc := -1;
          Next_Free := -1;
          for I in First_Entry .. Actual_Max_Code loop
            --  Either free before, or marked now as leaf
            if Previous_Code (I) < 0 or Is_Leaf (I) then
              --  Link last item to this item
              if Pc = -1 then
                Next_Free := I;
              else
                --  Next free node from Pc is I.
                Previous_Code (Pc) := -I;
              end if;
              Pc := I;
            end if;
          end loop;

          if Pc /= -1 then
            --  Last (old or new) free node points to the first "never used".
            Previous_Code (Pc) := -(Actual_Max_Code + 1);
          end if;
          if Next_Free = -1 then
            --  Unlikely but possible case:
            --     - no previously free or leaf node found, or
            --     - table clearing is ordered when the table is still empty.
            Next_Free := Actual_Max_Code + 1;
          end if;

          if full_trace then
            Ada.Text_IO.Put ("; new Next_Free =" & Integer'Image (Next_Free) & ']');
          end if;
        end Clear_Leaf_Nodes;

        procedure Attempt_Table_Increase is
          Candidate : constant Integer := Next_Free;
        begin
          if Candidate > Max_Code then
            --  This case is supported by PKZip's LZW variant.
            --  Table clearing is done only on a special command.
            if some_trace then
              Ada.Text_IO.Put ("[Table is full]");
            end if;
          else
            if Candidate not in Previous_Code'Range then
              raise Zip.Archive_corrupted with "Wrong LZW (Shrink) index";
            end if;
            Next_Free := -Previous_Code (Candidate);
            Actual_Max_Code := Integer'Max (Actual_Max_Code, Next_Free - 1);

            --  Next node in free list
            Previous_Code (Candidate)  := Last_Incode;
            Stored_Literal (Candidate) := Last_Outcode;
          end if;
        end Attempt_Table_Increase;

        Incode    : Integer;  --  Code read in
        New_Code  : Integer;  --  Save new normal code read
        Stack     : Zip.Byte_Buffer (0 .. Max_Stack);  --  Stack for output
        Stack_Ptr : Integer := Max_Stack;

        --  PKZip's Shrink is a variant of the LZW algorithm in that the
        --  compressor controls the code increase and the table clearing.
        --  See appnote.txt, section 5.1.
        Special_Code : constant := 256;
        Code_for_increasing_code_size : constant := 1;
        Code_for_clearing_table       : constant := 2;

        procedure Read_Code is
          pragma Inline (Read_Code);
        begin
          Incode := UnZ_IO.Bit_buffer.Read_and_dump (Code_Size);
        end Read_Code;

      begin
        --  Initialize free codes list
        for I in Previous_Code'Range loop
          Previous_Code (I) := -(I + 1);
        end loop;
        --
        Stored_Literal := (others => 0);
        Stack          := (others => 0);
        Writebuf       := (others => 0);

        if UnZ_Glob.compsize = Zip.Zip_64_Data_Size_Type'Last then
          --  Compressed Size was not in header!
          raise UnZip.Not_supported;
        elsif UnZ_Glob.uncompsize = 0 then
          return;  --  compression of a 0-file with Shrink.pas
        end if;

        Next_Free := First_Entry;
        Actual_Max_Code := First_Entry - 1;
        Write_Ptr := 0;

        Read_Code;
        Last_Incode := Incode;
        if Incode not in 0 .. 255 then
          raise Zip.Archive_corrupted with "Wrong LZW (Shrink) 1st byte; must be a literal";
        end if;
        Last_Outcode := Zip.Byte (Incode);
        UD_Write_Byte (Last_Outcode);
        S := S - 1;

        Main_Unshrink_Loop :
        while S > 0 and then not Zip_EOF loop
          Read_Code;
          if Incode = Special_Code then  --  Code = 256
            Read_Code;
            case Incode is
              when Code_for_increasing_code_size =>
                Code_Size := Code_Size + 1;
                if some_trace then
                  Ada.Text_IO.Put (
                    "[Increment LZW code size to" & Integer'Image (Code_Size) &
                    " bits @ pos" & Zip.Zip_64_Data_Size_Type'Image (UnZ_Glob.uncompsize - S) & ']'
                  );
                end if;
                if Code_Size > Maximum_Code_Size then
                  raise Zip.Archive_corrupted with "Wrong LZW (Shrink) code size";
                end if;
              when Code_for_clearing_table =>
                Clear_Leaf_Nodes;
              when others =>
                raise Zip.Archive_corrupted with
                  "Wrong LZW (Shrink) special code" & Integer'Image (Incode);
            end case;
          else  --  Normal code (either a literal (< 256), or a tree node (> 256))
            New_Code := Incode;
            if Incode < 256 then          --  Literal (simple character)
              Last_Outcode :=  Zip.Byte (Incode);
              UD_Write_Byte (Last_Outcode);
              S := S - 1;
            else  --  Tree node (code > 256)
              if Previous_Code (Incode) < 0 then
                --  First node is orphan (parent is a free node).
                if full_trace then
                  Ada.Text_IO.Put ("[ Node from stream is orphan ]");
                end if;
                Stack (Stack_Ptr) := Last_Outcode;
                Stack_Ptr := Stack_Ptr - 1;
                Incode := Last_Incode;
              end if;
              while Incode > 256 loop
                if Stack_Ptr < Stack'First then
                  raise Zip.Archive_corrupted with "LZW (Shrink): String stack exhausted";
                end if;
                if Incode > Max_Code then
                  raise Zip.Archive_corrupted with "LZW (Shrink): Incode out of range";
                end if;
                if Previous_Code (Incode) < 0 then
                  --  Linked node is orphan (parent is a free node).
                  --  This rare case appears on some data, compressed only by PKZIP.
                  --  The last PKZIP version known to us that is able to compress
                  --  with the Shrink algorithm is PKZIP v.1.10, 1990-03-15.
                  if some_trace then
                    Ada.Text_IO.Put ("[ Linked node is orphan ]");
                  end if;
                  Stack (Stack_Ptr) := Last_Outcode;
                  Incode := Last_Incode;
                else
                  Stack (Stack_Ptr) := Stored_Literal (Incode);
                  Incode := Previous_Code (Incode);
                end if;
                Stack_Ptr := Stack_Ptr - 1;
              end loop;
              --  NB: Incode cannot be negative (orphan case treated above).
              --      It is <= 256 because of the while loop.
              --      It is /= 256 because it is set to a Last_Incode value (directly or
              --        through Previous_Code) which is either in [0 .. 255] or > 256.
              --      So Incode is in [0 .. 255].
              Last_Outcode := Zip.Byte (Incode);
              UD_Write_Byte (Last_Outcode);
              --  Now we output the string in forward order.
              for I in Stack_Ptr + 1 .. Max_Stack  loop
                UD_Write_Byte (Stack (I));
              end loop;
              S := S - Zip.Zip_64_Data_Size_Type (Max_Stack - Stack_Ptr + 1);
              Stack_Ptr := Max_Stack;
            end if;
            Attempt_Table_Increase;
            Last_Incode := New_Code;
          end if;
        end loop Main_Unshrink_Loop;

        if some_trace then
          Ada.Text_IO.Put ("[ Unshrink main loop finished ]");
        end if;
        Unshrink_Flush;
      end Unshrink;

      --------[ Method: Unreduce ]--------

      procedure Unreduce (factor : Reduction_factor) is

        --  Original slide limit: 16#4000#
        DLE_code : constant := 144;
        subtype Symbol_range is Integer range 0 .. 255;
        subtype Follower_range is Integer range 0 .. 63;  --  Appnote: <= 32 !
        Followers : array (Symbol_range, Follower_range) of Symbol_range :=
          (others => (others => 0));
        Slen : array (Symbol_range) of Follower_range;

        --  Bits taken by (x-1) mod 256:
        B_Table : constant array (Symbol_range) of Integer :=
            (0        => 8,
             1 .. 2   => 1,
             3 .. 4   => 2,
             5 .. 8   => 3,
             9 .. 16  => 4,
            17 .. 32  => 5,
            33 .. 64  => 6,
            65 .. 128 => 7,
           129 .. 255 => 8);

        procedure LoadFollowers is
          list_followers : constant Boolean := some_trace;
          procedure Show_symbol (S : Symbol_range) is
          begin
            if S in 32 .. 254 then
              Ada.Text_IO.Put (Character'Val (S));
            else
              Ada.Text_IO.Put ('{' & Symbol_range'Image (S) & '}');
            end if;
          end Show_symbol;
        begin
          for X in reverse Symbol_range loop
            Slen (X) := UnZ_IO.Bit_buffer.Read_and_dump (6);
            if list_followers then
              Show_symbol (X);
              Ada.Text_IO.Put (" -> (" & Integer'Image (Slen (X)) & ") ");
            end if;
            for I in 0 .. Slen (X) - 1  loop
              Followers (X, I) := UnZ_IO.Bit_buffer.Read_and_dump (8);
              if list_followers then
                Show_symbol (Followers (X, I));
              end if;
            end loop;
            if list_followers then
              Ada.Text_IO.New_Line;
            end if;
          end loop;
        end LoadFollowers;

        length,
        char_read,
        last_char : Integer := 0;
        --  ^ some := 0 are useless, just to calm down ObjectAda 7.2.2
        S : Zip.Zip_64_Data_Size_Type := UnZ_Glob.uncompsize;
        --  number of bytes left to decompress
        unflushed : Boolean := True;
        maximum_AND_mask : constant Unsigned_32 := Shift_Left (1, 8 - factor) - 1;

        procedure Out_byte (b : Zip.Byte) is
        begin
          S := S - 1;
          UnZ_Glob.slide (UnZ_Glob.slide_index) := b;
          UnZ_Glob.slide_index := UnZ_Glob.slide_index + 1;
          UnZ_IO.Flush_if_full (UnZ_Glob.slide_index, unflushed);
        end Out_byte;

        V : Unsigned_32 := 0;
        type State_type is (normal, length_a, length_b, distance);
        state : State_type := normal;

      begin
        LoadFollowers;

        while S > 0 and then not Zip_EOF loop

          --  1/ Probabilistic expansion
          if Slen (last_char) = 0 then
            --  follower set is empty for this character
            char_read := UnZ_IO.Bit_buffer.Read_and_dump (8);
          elsif UnZ_IO.Bit_buffer.Read_and_dump (1) = 0  then
            char_read := Followers (
              last_char,
              UnZ_IO.Bit_buffer.Read_and_dump (B_Table (Slen (last_char)))
            );
          else
            char_read := UnZ_IO.Bit_buffer.Read_and_dump (8);
          end if;

          --  2/ Expand the resulting Zip.Byte into repeated sequences
          case state is

            when normal =>
              if char_read = DLE_code then
                --  >> Next will be a DLE
                state := length_a;
              else
                --  >> A single char
                Out_byte (Zip.Byte (char_read));
              end if;

            when length_a =>
              if char_read = 0 then
                --  >> DLE_code & 0 -> was just the Zip.Byte coded DLE_code
                Out_byte (DLE_code);
                state := normal;
              else
                V := Unsigned_32 (char_read);
                length := Integer (V and maximum_AND_mask);
                --  The remaining bits of V will be used for the distance
                if length = Integer (maximum_AND_mask) then
                  state := length_b;
                  --  >> length must be completed before reading distance
                else
                  state := distance;
                end if;
              end if;

            when length_b =>
              length := length + char_read;
              state := distance;

            when distance =>
              length := length + 3;
              S := S - Zip.Zip_64_Data_Size_Type (length);

              UnZ_IO.Copy_or_zero (
                distance   => char_read + 1 + Integer (Shift_Right (V, 8 - factor) * 2**8),
                length     => length,
                index      => UnZ_Glob.slide_index,
                unflushed  => unflushed
              );
              state := normal;

          end case;

          last_char := char_read;  -- store character for next iteration
        end loop;

        UnZ_IO.Flush (UnZ_Glob.slide_index);
      end Unreduce;

      --------[ Method: Explode ]--------

      --  C code by info-zip group, translated to Pascal by Christian Ghisler
      --  based on unz51g.zip

      use UnZip.Decompress.Huffman;

      procedure Get_Tree (L : out Length_array) is
        I, K, J, B : Unsigned_32;
        N          : constant Unsigned_32 := L'Length;
        L_Idx      : Integer    := L'First;
      begin
        if full_trace then
          Ada.Text_IO.Put_Line ("Begin UnZ_Expl.Get_tree");
        end if;

        I := Unsigned_32 (UnZ_IO.Read_Byte_Decrypted) + 1;
        K := 0;

        loop
          J := Unsigned_32 (UnZ_IO.Read_Byte_Decrypted);
          B := (J  and  16#0F#) + 1;
          J := (J  and  16#F0#) / 16 + 1;
          if  K + J > N then
            raise Zip.Archive_corrupted;
          end if;

          loop
            L (L_Idx) := Natural (B);
            L_Idx := L_Idx + 1;
            K := K + 1;
            J := J - 1;
            exit when  J = 0;
          end loop;

          I := I - 1;
          exit when  I = 0;
        end loop;

        if  K /= N then
          raise Zip.Archive_corrupted;
        end if;

        if full_trace then
          Ada.Text_IO.Put_Line ("End   UnZ_Expl.Get_tree");
        end if;
      end Get_Tree;

      procedure Explode_Lit ( -- method with 3 trees
        Needed : Integer;
        Tb, Tl, Td : p_Table_list;
        Bb, Bl, Bd : Integer
      )
      is
        S       : Zip.Zip_64_Data_Size_Type;
        E, N, D : Integer;

        W : Integer := 0;
        Ct : p_HufT_table; -- current table
        Ci : Natural;                               -- current index
        unflushed : Boolean := True; -- true while slide not yet unflushed

      begin
        if full_trace then
          Ada.Text_IO.Put_Line ("Begin Explode_lit");
        end if;

        UnZ_IO.Bit_buffer.Init;

        S := UnZ_Glob.uncompsize;
        while  S > 0  and  not Zip_EOF  loop
          if UnZ_IO.Bit_buffer.Read_and_dump (1) /= 0 then  -- 1: Literal
            S := S - 1;
            Ct := Tb.table;
            Ci := UnZ_IO.Bit_buffer.Read_inverted (Bb);

            loop
              E :=  Ct (Ci).extra_bits;
              exit when E <= 16;

              if E = invalid then
                raise Zip.Archive_corrupted;
              end if;

              UnZ_IO.Bit_buffer.Dump (Ct (Ci).bits);
              E := E - 16;
              Ct := Ct (Ci).next_table;
              Ci := UnZ_IO.Bit_buffer.Read_inverted (E);
            end loop;

            UnZ_IO.Bit_buffer.Dump (Ct (Ci).bits);
            UnZ_Glob.slide (W) :=  Zip.Byte (Ct (Ci).n);
            W := W + 1;
            UnZ_IO.Flush_if_full (W, unflushed);

          else                                       -- 0: Copy
            D := UnZ_IO.Bit_buffer.Read_and_dump (Needed);
            Ct := Td.table;
            Ci := UnZ_IO.Bit_buffer.Read_inverted (Bd);

            loop
              E := Ct (Ci).extra_bits;
              exit when  E <= 16;

              if E = invalid then
                raise Zip.Archive_corrupted;
              end if;

              UnZ_IO.Bit_buffer.Dump (Ct (Ci).bits);
              E := E - 16;
              Ct := Ct (Ci).next_table;
              Ci := UnZ_IO.Bit_buffer.Read_inverted (E);
            end loop;

            UnZ_IO.Bit_buffer.Dump (Ct (Ci).bits);
            D := D + Ct (Ci).n;

            Ct := Tl.table;
            Ci := UnZ_IO.Bit_buffer.Read_inverted (Bl);

            loop
              E := Ct (Ci).extra_bits;
              exit when  E <= 16;

              if E = invalid then
                raise Zip.Archive_corrupted;
              end if;

              UnZ_IO.Bit_buffer.Dump (Ct (Ci).bits);
              E := E - 16;
              Ct := Ct (Ci).next_table;
              Ci := UnZ_IO.Bit_buffer.Read_inverted (E);
            end loop;

            UnZ_IO.Bit_buffer.Dump (Ct (Ci).bits);

            N :=  Ct (Ci).n;
            if E /= 0 then
              N := N + UnZ_IO.Bit_buffer.Read_and_dump (8);
            end if;
            S := S - Zip.Zip_64_Data_Size_Type (N);

            UnZ_IO.Copy_or_zero (
              distance   => D,
              length     => N,
              index      => W,
              unflushed  => unflushed
            );

          end if;
        end loop;

        UnZ_IO.Flush (W);
        if Zip_EOF then
          raise Zip.Archive_corrupted with "End of stream reached";
        end if;

        if full_trace then
          Ada.Text_IO.Put_Line ("End   Explode_lit");
        end if;
      end Explode_Lit;

      procedure Explode_Nolit ( -- method with 2 trees
          Needed : Integer;
          Tl, Td : p_Table_list;
          Bl, Bd : Integer
      )
      is
        S       : Zip.Zip_64_Data_Size_Type;
        E, N, D : Integer;
        W : Integer := 0;
        Ct : p_HufT_table; -- current table
        Ci : Natural;                               -- current index
        unflushed : Boolean := True; -- true while slide not yet unflushed

      begin
        if full_trace then
          Ada.Text_IO.Put_Line ("Begin Explode_nolit");
        end if;

        UnZ_IO.Bit_buffer.Init;
        S := UnZ_Glob.uncompsize;
        while  S > 0  and not Zip_EOF  loop
          if UnZ_IO.Bit_buffer.Read_and_dump (1) /= 0 then  -- 1: Literal
            S := S - 1;
            UnZ_Glob.slide (W) :=
              Zip.Byte (UnZ_IO.Bit_buffer.Read_and_dump (8));
            W := W + 1;
            UnZ_IO.Flush_if_full (W, unflushed);
          else                                       -- 0: Copy
            D := UnZ_IO.Bit_buffer.Read_and_dump (Needed);
            Ct := Td.table;
            Ci := UnZ_IO.Bit_buffer.Read_inverted (Bd);

            loop
              E := Ct (Ci).extra_bits;
              exit when  E <= 16;

              if E = invalid then
                raise Zip.Archive_corrupted;
              end if;

              UnZ_IO.Bit_buffer.Dump (Ct (Ci).bits);
              E := E - 16;
              Ct := Ct (Ci).next_table;
              Ci := UnZ_IO.Bit_buffer.Read_inverted (E);
            end loop;

            UnZ_IO.Bit_buffer.Dump (Ct (Ci).bits);

            D :=  D + Ct (Ci).n;
            Ct := Tl.table;
            Ci := UnZ_IO.Bit_buffer.Read_inverted (Bl);

            loop
              E := Ct (Ci).extra_bits;
              exit when E <= 16;

              if E = invalid then
                raise Zip.Archive_corrupted;
              end if;

              UnZ_IO.Bit_buffer.Dump (Ct (Ci).bits);
              E := E - 16;
              Ct := Ct (Ci).next_table;
              Ci := UnZ_IO.Bit_buffer.Read_inverted (E);
            end loop;

            UnZ_IO.Bit_buffer.Dump (Ct (Ci).bits);

            N := Ct (Ci).n;
            if  E /= 0 then
              N := N + UnZ_IO.Bit_buffer.Read_and_dump (8);
            end if;
            S := S - Zip.Zip_64_Data_Size_Type (N);

            UnZ_IO.Copy_or_zero (
              distance   => D,
              length     => N,
              index      => W,
              unflushed  => unflushed
            );

          end if;
        end loop;

        UnZ_IO.Flush (W);
        if Zip_EOF then
          raise Zip.Archive_corrupted with "End of stream reached";
        end if;

        if full_trace then
          Ada.Text_IO.Put_Line ("End   Explode_nolit");
        end if;

      end Explode_Nolit;

      procedure Explode (literal_tree, slide_8_KB : Boolean) is

        Tb, Tl, Td : p_Table_list;
        Bb, Bl, Bd : Integer;
        L :  Length_array (0 .. 255);
        huft_incomplete : Boolean;

        cp_length_2_trees :
          constant Length_array (0 .. 63) :=
           (2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
           18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
           35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
           52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65);

        cp_length_3_trees :
          constant Length_array (0 .. 63) :=
           (3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
           19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
           36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
           53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66);

        cp_dist_4KB :
          constant Length_array (0 .. 63) :=
          (1, 65, 129, 193, 257, 321, 385, 449, 513, 577, 641, 705,
           769, 833, 897, 961, 1025, 1089, 1153, 1217, 1281, 1345, 1409, 1473,
           1537, 1601, 1665, 1729, 1793, 1857, 1921, 1985, 2049, 2113, 2177,
           2241, 2305, 2369, 2433, 2497, 2561, 2625, 2689, 2753, 2817, 2881,
           2945, 3009, 3073, 3137, 3201, 3265, 3329, 3393, 3457, 3521, 3585,
           3649, 3713, 3777, 3841, 3905, 3969, 4033);

        cp_dist_8KB :
          constant Length_array (0 .. 63) :=
             (1,  129,  257,  385,  513,  641,  769,  897, 1025, 1153, 1281,
           1409, 1537, 1665, 1793, 1921, 2049, 2177, 2305, 2433, 2561, 2689,
           2817, 2945, 3073, 3201, 3329, 3457, 3585, 3713, 3841, 3969, 4097,
           4225, 4353, 4481, 4609, 4737, 4865, 4993, 5121, 5249, 5377, 5505,
           5633, 5761, 5889, 6017, 6145, 6273, 6401, 6529, 6657, 6785, 6913,
           7041, 7169, 7297, 7425, 7553, 7681, 7809, 7937, 8065);

        extra :
          constant Length_array (0 .. 63) := (0 .. 62 => 0, 63 => 8);

      begin
        Bl := 7;
        if UnZ_Glob.compsize > 200000 then
          Bd := 8;
        else
          Bd := 7;
        end if;

        if literal_tree then
          Bb := 9;
          Get_Tree (L);
          begin
            HufT_build (L, 256, empty, empty, Tb, Bb, huft_incomplete);
            if huft_incomplete then
              HufT_free (Tb);
              raise Zip.Archive_corrupted;
            end if;
          exception
            when others =>
              raise Zip.Archive_corrupted;
          end;

          begin
            Get_Tree (L (0 .. 63));
          exception
            when others =>
              HufT_free (Tb);
              raise Zip.Archive_corrupted;
          end;

          begin
            HufT_build (
              L (0 .. 63), 0, cp_length_3_trees, extra, Tl, Bl, huft_incomplete
            );
            if huft_incomplete then
              HufT_free (Tl);
              HufT_free (Tb);
              raise Zip.Archive_corrupted;
            end if;
          exception
            when others =>
              HufT_free (Tb);
              raise Zip.Archive_corrupted;
          end;

          begin
            Get_Tree (L (0 .. 63));
          exception
            when others =>
              HufT_free (Tb);
              HufT_free (Tl);
              raise Zip.Archive_corrupted;
          end;

          begin
            if slide_8_KB then
              HufT_build (
                L (0 .. 63), 0, cp_dist_8KB, extra, Td, Bd, huft_incomplete
              );
              if huft_incomplete then
                HufT_free (Td);
                HufT_free (Tl);
                HufT_free (Tb);
                raise Zip.Archive_corrupted;
              end if;
              --  Exploding, method: 8k slide, 3 trees
              Explode_Lit (7, Tb, Tl, Td, Bb, Bl, Bd);
            else
              HufT_build (
                L (0 .. 63), 0, cp_dist_4KB, extra, Td, Bd, huft_incomplete
              );
              if huft_incomplete then
                HufT_free (Td);
                HufT_free (Tl);
                HufT_free (Tb);
                raise Zip.Archive_corrupted;
              end if;
              --  Exploding, method: 4k slide, 3 trees
              Explode_Lit (6, Tb, Tl, Td, Bb, Bl, Bd);
            end if;
          exception
            when  others =>
              HufT_free (Tl);
              HufT_free (Tb);
              raise Zip.Archive_corrupted;
          end;
          HufT_free (Td);
          HufT_free (Tl);
          HufT_free (Tb);

        else         -- No literal tree

          begin
            Get_Tree (L (0 .. 63));
          exception
            when others =>
              raise Zip.Archive_corrupted;
          end;

          begin
            HufT_build (
              L (0 .. 63), 0, cp_length_2_trees, extra, Tl, Bl, huft_incomplete
            );
            if huft_incomplete then
              HufT_free (Tl);
              raise Zip.Archive_corrupted;
            end if;
          exception
            when others =>
              raise Zip.Archive_corrupted;
          end;

          begin
            Get_Tree (L (0 .. 63));
          exception
            when others =>
              HufT_free (Tl);
              raise Zip.Archive_corrupted;
          end;

          begin
            if slide_8_KB then
              HufT_build (
                L (0 .. 63), 0, cp_dist_8KB, extra, Td, Bd, huft_incomplete
              );
              if huft_incomplete then
                HufT_free (Td);
                HufT_free (Tl);
                raise Zip.Archive_corrupted;
              end if;
              --  Exploding, method: 8k slide, 2 trees
              Explode_Nolit (7, Tl, Td, Bl, Bd);
            else
              HufT_build (
                L (0 .. 63), 0, cp_dist_4KB, extra, Td, Bd, huft_incomplete
              );
              if huft_incomplete then
                HufT_free (Td);
                HufT_free (Tl);
                raise Zip.Archive_corrupted;
              end if;
              --  Exploding, method: 4k slide, 2 trees
              Explode_Nolit (6, Tl, Td, Bl, Bd);
            end if;
          exception
            when others =>
              HufT_free (Tl);
              raise Zip.Archive_corrupted;
          end;
          HufT_free (Td);
          HufT_free (Tl);
        end if;

      end Explode;

      --------[ Method: Copy stored ]--------

      procedure Copy_stored is
        size : constant Zip.Zip_64_Data_Size_Type := UnZ_Glob.compsize;
        read_in, absorbed : Zip.Zip_64_Data_Size_Type;
      begin
        absorbed := 0;
        if Get_Mode (local_crypto_pack) = encrypted then
          absorbed := 12;
        end if;
        while absorbed < size loop
          read_in := size - absorbed;
          if read_in > wsize then
            read_in := wsize;
          end if;
          begin
            for I in 0 .. read_in - 1 loop
              UnZ_Glob.slide (Natural (I)) := UnZ_IO.Read_Byte_Decrypted;
            end loop;
          exception
            when others =>
              raise Zip.Archive_corrupted with
                "End of stream reached (format: Store)";
          end;
          begin
            UnZ_IO.Flush (Natural (read_in));  --  Takes care of CRC too
          exception
            when User_abort =>
              raise;
            when others =>
              raise UnZip.Write_Error;
          end;
          absorbed := absorbed + read_in;
        end loop;
      end Copy_stored;

      --------[ Method: Inflate ]--------

      lt_count,     dl_count,
      lt_count_0,   dl_count_0,
      lt_count_dyn, dl_count_dyn,
      lt_count_fix, dl_count_fix : Long_Integer := 0;  --  Statistics of LZ codes per block

      procedure Inflate_Codes (Tl, Td : p_Table_list; Bl, Bd : Integer) is
        CT      : p_HufT_table;       -- current table
        CT_idx  : Natural;            -- current table's index
        length  : Natural;
        E       : Integer;      -- table entry flag/number of extra bits
        W       : Integer := UnZ_Glob.slide_index;  -- more local variable for slide index
        literal : Zip.Byte;
      begin
        if some_trace then
          lt_count_0 := lt_count;
          dl_count_0 := dl_count;
          Ada.Text_IO.Put_Line ("Begin Inflate_codes");
        end if;

        --  inflate the coded data
        main_loop :
        while not Zip_EOF loop
          if Tl = null then
            raise Zip.Archive_corrupted with
              "Null table list (on data decoding, Huffman tree for literals or LZ lengths)";
          end if;
          CT := Tl.table;
          CT_idx := UnZ_IO.Bit_buffer.Read (Bl);
          loop
            E := CT (CT_idx).extra_bits;
            exit when E <= 16;
            if E = invalid then
              raise Zip.Archive_corrupted;
            end if;

            --  then it's a literal
            UnZ_IO.Bit_buffer.Dump (CT (CT_idx).bits);
            E := E - 16;
            CT := CT (CT_idx).next_table;
            CT_idx := UnZ_IO.Bit_buffer.Read (E);
          end loop;

          UnZ_IO.Bit_buffer.Dump (CT (CT_idx).bits);

          case E is
            when 16 =>      --  CT(CT_idx).N is a Literal (code 0 .. 255)
              literal := Zip.Byte (CT (CT_idx).n);
              if some_trace then
                lt_count := lt_count + 1;
                Ada.Text_IO.Put (LZ77_dump, "Lit" & Zip.Byte'Image (literal));
                if literal in 32 .. 126 then
                  Ada.Text_IO.Put (LZ77_dump, " '" & Character'Val (literal) & ''');
                end if;
                Ada.Text_IO.New_Line (LZ77_dump);
              end if;
              UnZ_Glob.slide (W) :=  literal;
              W := W + 1;
              UnZ_IO.Flush_if_full (W);

            when 15 =>      --  End of block (EOB, code 256)
              if full_trace then
                Ada.Text_IO.Put_Line ("Exit  Inflate_codes, e=15 -> EOB");
              end if;
              exit main_loop;

            when others =>  --  We have a length/distance code
              if some_trace then
                dl_count := dl_count + 1;
              end if;
              --  Get length of block to copy:
              length := CT (CT_idx).n + UnZ_IO.Bit_buffer.Read_and_dump (E);

              --  Decode distance of block to copy:
              if Td = null then
                raise Zip.Archive_corrupted with
                  "Null table list (on data decoding, Huffman tree for LZ distances)";
              end if;
              CT := Td.table;
              CT_idx := UnZ_IO.Bit_buffer.Read (Bd);
              loop
                E := CT (CT_idx).extra_bits;
                exit when E <= 16;
                if E = invalid then
                  raise Zip.Archive_corrupted;
                end if;
                UnZ_IO.Bit_buffer.Dump (CT (CT_idx).bits);
                E := E - 16;
                CT := CT (CT_idx).next_table;
                CT_idx := UnZ_IO.Bit_buffer.Read (E);
              end loop;
              UnZ_IO.Bit_buffer.Dump (CT (CT_idx).bits);
              UnZ_IO.Copy (
                distance    => CT (CT_idx).n + UnZ_IO.Bit_buffer.Read_and_dump (E),
                copy_length => length,
                index       => W
              );
          end case;
        end loop main_loop;

        UnZ_Glob.slide_index := W;

        if some_trace then
          Ada.Text_IO.Put_Line ("End   Inflate_codes;  " &
            Long_Integer'Image (lt_count - lt_count_0) & " literals," &
            Long_Integer'Image (dl_count - dl_count_0) & " DL codes," &
            Long_Integer'Image (dl_count + lt_count - lt_count_0 - dl_count_0) & " in total");
        end if;
      end Inflate_Codes;

      procedure Inflate_stored_block is -- Actually, nothing to inflate
        N : Integer;
      begin
        UnZ_IO.Bit_buffer.Dump_to_byte_boundary;
        --  Get the block length and its complement
        N := UnZ_IO.Bit_buffer.Read_and_dump (16);
        if some_trace then
          Ada.Text_IO.Put_Line ("Begin Inflate_stored_block, bytes stored: " & Integer'Image (N));
        end if;
        if  N /= Integer (
         (not UnZ_IO.Bit_buffer.Read_and_dump_U32 (16))
         and 16#ffff#)
        then
          raise Zip.Archive_corrupted;
        end if;
        while N > 0 and then not Zip_EOF loop
          --  Read and output the non-compressed data
          N := N - 1;
          UnZ_Glob.slide (UnZ_Glob.slide_index) :=
            Zip.Byte (UnZ_IO.Bit_buffer.Read_and_dump (8));
          UnZ_Glob.slide_index := UnZ_Glob.slide_index + 1;
          UnZ_IO.Flush_if_full (UnZ_Glob.slide_index);
        end loop;
        if some_trace then
          Ada.Text_IO.Put_Line ("End   Inflate_stored_block");
        end if;
      end Inflate_stored_block;

      --  Copy lengths for literal codes 257..285

      copy_lengths_literal : Length_array (0 .. 30) :=
             (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);

      --  Extra bits for literal codes 257..285

      extra_bits_literal : Length_array (0 .. 30) :=
              (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, invalid, invalid);

      --  Copy offsets for distance codes 0..29 (30..31: deflate_e)

      copy_offset_distance : constant Length_array (0 .. 31) :=
            (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, 32769, 49153);

      --  Extra bits for distance codes

      extra_bits_distance : constant Length_array (0 .. 31) :=
            (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, 14, 14);

      max_dist : Integer := 29;  --  changed to 31 for deflate_e

      length_list_for_fixed_block_literals : constant Length_array (0 .. 287) :=
          (0 .. 143 => 8, 144 .. 255 => 9, 256 .. 279 => 7, 280 .. 287 => 8);

      procedure Inflate_fixed_block is
        Tl,                        --   literal/length code table
            Td : p_Table_list;            --  distance code table
        Bl, Bd : Integer;          --  lookup bits for tl/bd
        huft_incomplete : Boolean;
      begin
        if some_trace then
          Ada.Text_IO.Put_Line ("Begin Inflate_fixed_block");
        end if;
        --  Make a complete, but wrong [why ?] code set (see Appnote: 5.5.2, RFC 1951: 3.2.6)
        Bl := 7;
        HufT_build (
          length_list_for_fixed_block_literals, 257, copy_lengths_literal,
          extra_bits_literal, Tl, Bl, huft_incomplete
        );
        --  Make an incomplete code set (see Appnote: 5.5.2, RFC 1951: 3.2.6)
        Bd := 5;
        begin
          HufT_build (
            (0 .. max_dist => 5), 0,
            copy_offset_distance, extra_bits_distance,
            Td, Bd, huft_incomplete
          );
          if huft_incomplete then
            if full_trace then
              Ada.Text_IO.Put_Line (
                "td is incomplete, pointer=null: " &
                Boolean'Image (Td = null)
              );
            end if;
          end if;
        exception
          when huft_out_of_memory | huft_error =>
            HufT_free (Tl);
            raise Zip.Archive_corrupted;
        end;
        --  Decompress the block's data, until an end-of-block code.
        Inflate_Codes (Tl, Td, Bl, Bd);
        --  Done with this block, free resources.
        HufT_free (Tl);
        HufT_free (Td);
        if some_trace then
          Ada.Text_IO.Put_Line ("End   Inflate_fixed_block");
          lt_count_fix := lt_count_fix + (lt_count - lt_count_0);
          dl_count_fix := dl_count_fix + (dl_count - dl_count_0);
        end if;
      end Inflate_fixed_block;

      bit_order_for_dynamic_block : constant array (0 .. 18) of Natural :=
         (16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15);

      procedure Inflate_dynamic_block is

        Lbits : constant := 9;
        Dbits : constant := 6;

        current_length : Natural;
        defined, number_of_lengths : Natural;

        Tl,                             -- literal/length code tables
          Td : p_Table_list;            -- distance code tables

        CT     : p_HufT_table;       -- current table
        CT_idx : Natural;            -- current table's index

        Bl, Bd : Integer;                  -- lookup bits for tl/bd
        Nb : Natural;  -- number of bit length codes
        Nl : Natural;  -- number of literal length codes
        Nd : Natural;  -- number of distance codes

        --  literal/length and distance code lengths
        Ll : Length_array (0 .. 288 + 32 - 1) := (others => 0);

        huft_incomplete : Boolean;

        procedure Repeat_length_code (amount : Natural) is
        begin
          if defined + amount > number_of_lengths then
            raise Zip.Archive_corrupted;
          end if;
          for c in reverse 1 .. amount loop
            Ll (defined) := current_length;
            defined := defined + 1;
          end loop;
        end Repeat_length_code;

      begin
        if some_trace then
          Ada.Text_IO.Put_Line ("Begin Inflate_dynamic_block");
        end if;

        --  Read in table lengths
        Nl := 257 + UnZ_IO.Bit_buffer.Read_and_dump (5);
        Nd :=   1 + UnZ_IO.Bit_buffer.Read_and_dump (5);
        Nb :=   4 + UnZ_IO.Bit_buffer.Read_and_dump (4);

        if Nl > 288 or else Nd > 32 then
          raise Zip.Archive_corrupted;
        end if;

        --  Read in bit-length-code lengths for decoding the compression structure.
        --  The rest, Ll( Bit_Order( Nb .. 18 ) ), is already = 0
        for J in  0 .. Nb - 1  loop
          Ll (bit_order_for_dynamic_block (J)) := UnZ_IO.Bit_buffer.Read_and_dump (3);
        end loop;

        --  Build decoding table for trees--single level, 7 bit lookup
        Bl := 7;
        begin
          HufT_build (
            Ll (0 .. 18), 19, empty, empty, Tl, Bl, huft_incomplete
          );
          if huft_incomplete then
            HufT_free (Tl);
            raise Zip.Archive_corrupted with "Incomplete code set for compression structure";
          end if;
        exception
          when others =>
            raise Zip.Archive_corrupted with "Error when building tables for compression structure";
        end;

        --  Read in the compression structure: literal and distance code lengths
        number_of_lengths := Nl + Nd;
        defined := 0;
        current_length := 0;

        while  defined < number_of_lengths  loop
          if Tl = null then
            raise Zip.Archive_corrupted with
            "Null table list (on compression structure)";
          end if;
          CT := Tl.table;
          CT_idx := UnZ_IO.Bit_buffer.Read (Bl);
          UnZ_IO.Bit_buffer.Dump (CT (CT_idx).bits);

          case CT (CT_idx).n is
            when 0 .. 15 =>     --  Length of code for symbol of index 'defined', in bits (0..15)
              current_length := CT (CT_idx).n;
              Ll (defined) := current_length;
              defined := defined + 1;
            when 16 =>          --  16 means: repeat last bit length 3 to 6 times
              if defined = 0 then
                --  Nothing in the Ll array has been defined so far. Then, current_length is
                --  (theoretically) undefined and cannot be repeated.
                --  This unspecified case is treated as an error by zlib's inflate.c.
                raise Zip.Archive_corrupted with
                  "Illegal data for compression structure (repeat an undefined code length)";
              end if;
              Repeat_length_code (3 + UnZ_IO.Bit_buffer.Read_and_dump (2));
            when 17 =>          --  17 means: the next 3 to 10 symbols' codes have zero bit lengths
              current_length := 0;
              Repeat_length_code (3 + UnZ_IO.Bit_buffer.Read_and_dump (3));
            when 18 =>          --  18 means: the next 11 to 138 symbols' codes have zero bit lengths
              current_length := 0;
              Repeat_length_code (11 + UnZ_IO.Bit_buffer.Read_and_dump (7));
            when others =>      --  Shouldn't occur if this tree is correct
              raise Zip.Archive_corrupted with
                "Illegal data for compression structure (values should be in the range 0 .. 18): "
                & Integer'Image (CT (CT_idx).n);
          end case;
        end loop;
        --  Free the Huffman tree that was used for decoding the compression
        --  structure, which is contained now in Ll.
        HufT_free (Tl);
        if Ll (256) = 0 then
          --  No code length for the End-Of-Block symbol, implies infinite stream!
          --  This case is unspecified but obviously we must stop here.
          raise Zip.Archive_corrupted with "No code length for End-Of-Block symbol #256";
        end if;
        --  Build the decoding tables for literal/length codes
        Bl := Lbits;
        begin
          HufT_build (
            Ll (0 .. Nl - 1), 257,
            copy_lengths_literal, extra_bits_literal,
            Tl, Bl, huft_incomplete
          );
          if huft_incomplete then
            HufT_free (Tl);
            raise Zip.Archive_corrupted with "Incomplete code set for literals/lengths";
          end if;
        exception
          when others =>
            raise Zip.Archive_corrupted with "Error when building tables for literals/lengths";
        end;
        --  Build the decoding tables for distance codes
        Bd := Dbits;
        begin
          HufT_build (
            Ll (Nl .. Nl + Nd - 1), 0,
            copy_offset_distance, extra_bits_distance,
            Td, Bd, huft_incomplete
          );
          if huft_incomplete then
            if deflate_strict then
              raise Zip.Archive_corrupted with "Incomplete code set for distances";
            elsif some_trace then  --  not deflate_strict => don't stop
              Ada.Text_IO.Put_Line ("Huffman tree incomplete - PKZIP 1.93a bug workaround");
            end if;
          end if;
        exception
          when huft_out_of_memory | huft_error =>
            HufT_free (Tl);
            raise Zip.Archive_corrupted with "Error when building tables for distances";
        end;
        --  Decompress the block's data, until an end-of-block code.
        Inflate_Codes (Tl, Td, Bl, Bd);
        --  Done with this block, free resources.
        HufT_free (Tl);
        HufT_free (Td);
        if some_trace then
          Ada.Text_IO.Put_Line ("End   Inflate_dynamic_block");
          lt_count_dyn := lt_count_dyn + (lt_count - lt_count_0);
          dl_count_dyn := dl_count_dyn + (dl_count - dl_count_0);
        end if;
      end Inflate_dynamic_block;

      procedure Inflate_Block (last_block : out Boolean; fix, dyn : in out Long_Integer) is
      begin
        last_block := Boolean'Val (UnZ_IO.Bit_buffer.Read_and_dump (1));
        case UnZ_IO.Bit_buffer.Read_and_dump (2) is  --  Block type = 0, 1, 2, 3
          when 0 =>      Inflate_stored_block;
          when 1 =>      Inflate_fixed_block;
                         fix := fix + 1;
          when 2 =>      Inflate_dynamic_block;
                         dyn := dyn + 1;
          when others => raise Zip.Archive_corrupted with "Inflate: Bad block type (3)";
        end case;
      end Inflate_Block;

      procedure Inflate is
        is_last_block : Boolean;
        blocks, blocks_fix, blocks_dyn : Long_Integer := 0;
      begin
        if deflate_e_mode then
          copy_lengths_literal (28) := 3;  --  instead of 258
          extra_bits_literal (28) := 16;   --  instead of 0
          max_dist := 31;
        end if;
        loop
          blocks := blocks + 1;
          Inflate_Block (is_last_block, blocks_fix, blocks_dyn);
          exit when is_last_block;
        end loop;
        UnZ_IO.Flush (UnZ_Glob.slide_index);
        UnZ_Glob.slide_index := 0;
        if some_trace then
          Ada.Text_IO.Put_Line (
            "# blocks:" & Long_Integer'Image (blocks) &
            "; fixed:" & Long_Integer'Image (blocks_fix) &
            "; dynamic:" & Long_Integer'Image (blocks_dyn));
          if blocks_fix > 0 then
            Ada.Text_IO.Put_Line (
              "Averages per fixed block: literals:" & Long_Integer'Image (lt_count_fix / blocks_fix) &
              "; DL codes:" & Long_Integer'Image (dl_count_fix / blocks_fix) &
              "; all codes:" & Long_Integer'Image ((lt_count_fix + dl_count_fix) / blocks_fix));
          end if;
          if blocks_dyn > 0 then
            Ada.Text_IO.Put_Line (
              "Averages per dynamic block: literals:" & Long_Integer'Image (lt_count_dyn / blocks_dyn) &
              "; DL codes:" & Long_Integer'Image (dl_count_dyn / blocks_dyn) &
              "; all codes:" & Long_Integer'Image ((lt_count_dyn + dl_count_dyn) / blocks_dyn));
          end if;
        end if;
      end Inflate;

      procedure Write_Single_Byte (b : Unsigned_8) with Inline is
      begin
        UnZ_Glob.slide (UnZ_Glob.slide_index) := b;
        UnZ_Glob.slide_index := UnZ_Glob.slide_index + 1;
        UnZ_IO.Flush_if_full (UnZ_Glob.slide_index);
      end Write_Single_Byte;

      --------[ Method: BZip2 ]--------

      procedure Bunzip2 is
        package My_BZip2 is new BZip2.Decoding
          (Read_Byte  => UnZ_IO.Read_Byte_Decrypted,
           Write_Byte => Write_Single_Byte,
           check_CRC  => False);  --  CRC check is already done by UnZ_IO
      begin
        My_BZip2.Decompress;
        UnZ_IO.Flush (UnZ_Glob.slide_index);
      exception
        when E : My_BZip2.bad_header_magic | My_BZip2.bad_block_magic | My_BZip2.data_error =>
          raise Zip.Archive_corrupted with
            "BZip2 error: " & Exception_Name (E) & " - " & Exception_Message (E);
        when E : My_BZip2.randomized_not_yet_implemented =>
          raise UnZip.Unsupported_method with
            "BZip2: " & Exception_Name (E) & " - " & Exception_Message (E);
      end Bunzip2;

      --------[ Method: LZMA ]--------

      procedure LZMA_Decode is
        package My_LZMA_Decoding is new LZMA.Decoding (UnZ_IO.Read_Byte_Decrypted, Write_Single_Byte);
        b3, b4 : Unsigned_8;
      begin
        b3 := UnZ_IO.Read_Byte_Decrypted;  --  LZMA SDK major version (e.g.: 9)
        b3 := UnZ_IO.Read_Byte_Decrypted;  --  LZMA SDK minor version (e.g.: 20)
        b3 := UnZ_IO.Read_Byte_Decrypted;  --  LZMA properties size low byte
        b4 := UnZ_IO.Read_Byte_Decrypted;  --  LZMA properties size high byte
        if Natural (b3) + 256 * Natural (b4) /= 5 then
          raise Zip.Archive_corrupted with "Unexpected LZMA properties block size";
        end if;
        My_LZMA_Decoding.Decompress
          ((has_size               => False,  --  Data size is not part of the LZMA header.
            given_size             => LZMA.Data_Bytes_Count (UnZ_Glob.uncompsize),
            marker_expected        => explode_slide_8KB_LZMA_EOS,  --  End-Of-Stream marker?
            fail_on_bad_range_code => True));
        UnZ_IO.Flush (UnZ_Glob.slide_index);
      exception
        when E : My_LZMA_Decoding.LZMA_Error =>
          raise Zip.Archive_corrupted with
            "LZMA error: " & Exception_Name (E) & " - " & Exception_Message (E);
      end LZMA_Decode;

    end UnZ_Meth;

    procedure Process_descriptor (dd : out Zip.Headers.Data_Descriptor) is
      start : Integer;
      b : Unsigned_8;
      dd_buffer : Zip.Byte_Buffer (1 .. 30);
    begin
      UnZ_IO.Bit_buffer.Dump_to_byte_boundary;
      Set_Mode (local_crypto_pack, clear); -- We are after compressed data, switch off decryption.
      b := UnZ_IO.Read_Byte_Decrypted;
      if b = 75 then -- 'K' ('P' is before, this is a Java/JAR bug!)
        dd_buffer (1) := 80;
        dd_buffer (2) := 75;
        start := 3;
      else
        dd_buffer (1) := b; -- hopefully = 80 (will be checked)
        start := 2;
      end if;
      for i in start .. 16 loop
        dd_buffer (i) := UnZ_IO.Read_Byte_Decrypted;
      end loop;
      Zip.Headers.Copy_and_Check (dd_buffer, dd);
    exception
      when Zip.Headers.bad_data_descriptor =>
        raise Zip.Archive_corrupted;
    end Process_descriptor;

    work_index : Zip_Streams.ZS_Index_Type;
    use Zip, UnZ_Meth, Ada.Strings.Unbounded;

  begin  --  Decompress_Data
    if some_trace then
      Ada.Text_IO.Create (LZ77_dump, Ada.Text_IO.Out_File, "dump.lz77");
    end if;
    output_memory_access := null;
    --  ^ this is an 'out' parameter, we have to set it anyway
    case write_mode is
      when write_to_binary_file =>
         Ada.Streams.Stream_IO.Create (UnZ_IO.out_bin_file, Ada.Streams.Stream_IO.Out_File, output_file_name,
                                         Form => To_String (Zip_Streams.Form_For_IO_Open_and_Create));
      when write_to_text_file =>
         Ada.Text_IO.Create (UnZ_IO.out_txt_file, Ada.Text_IO.Out_File, output_file_name,
                               Form => To_String (Zip_Streams.Form_For_IO_Open_and_Create));
      when write_to_memory =>
        output_memory_access := new
          Ada.Streams.Stream_Element_Array (
            1 .. Ada.Streams.Stream_Element_Offset (hint.dd.uncompressed_size)
          );
        UnZ_Glob.uncompressed_index := output_memory_access'First;
      when write_to_stream | just_test =>
        null;
    end case;

    UnZ_Glob.compsize   := hint.dd.compressed_size;
    UnZ_Glob.uncompsize := hint.dd.uncompressed_size;
    UnZ_IO.Init_Buffers;
    if is_encrypted then
      Set_Mode (local_crypto_pack, encrypted);
      work_index := Zip_Streams.Index (zip_file);
      password_passes : for pass in 1 .. tolerance_wrong_password loop
        begin
          Init_Decryption (To_String (password), hint.dd.crc_32);
          exit password_passes; -- the current password fits, then go on!
        exception
          when Wrong_password =>
            if pass = tolerance_wrong_password then
              raise;
            elsif get_new_password /= null then
              get_new_password (password);  --  ask for a new one
            end if;
        end;
        --  Go back to data beginning:
        begin
          Zip_Streams.Set_Index (zip_file, work_index);
        exception
          when others =>
            raise UnZip.Read_Error with "Failure after password interaction";
        end;
        UnZ_IO.Init_Buffers;
      end loop password_passes;
    else
      Set_Mode (local_crypto_pack, clear);
    end if;

    --  UnZip correct type
    begin
      case format is
        when store          => Copy_stored;
        when shrink         => Unshrink;
        when Reduce_Format  => Unreduce (1 + Reduce_Format'Pos (format) - Reduce_Format'Pos (reduce_1));
        when implode        =>
          UnZ_Meth.Explode (explode_literal_tree, explode_slide_8KB_LZMA_EOS);
        when deflate | deflate_e =>
          UnZ_Meth.deflate_e_mode := format = deflate_e;
          UnZ_Meth.Inflate;
        when Zip.bzip2_meth => UnZ_Meth.Bunzip2;
        when Zip.lzma_meth  => UnZ_Meth.LZMA_Decode;
        when others =>
          raise Unsupported_method with
            "Format/method " & Image (format) &
            " not supported for decompression";
      end case;
    exception
      when others =>
        UnZ_IO.Delete_output;
        raise;
    end;
    UnZ_Glob.crc32val := Zip.CRC_Crypto.Final (UnZ_Glob.crc32val);
    --  Decompression done !

    if data_descriptor_after_data then  --  Sizes and CRC at the end
      declare
        memo_uncomp_size : constant Zip.Zip_64_Data_Size_Type := hint.dd.uncompressed_size;
      begin
        Process_descriptor (hint.dd);  --  CRC is for checking; sizes are for informing user
        if memo_uncomp_size < Zip_64_Data_Size_Type (Zip_32_Data_Size_Type'Last) and then --
           memo_uncomp_size /= hint.dd.uncompressed_size
        then
          UnZ_IO.Delete_output;
          raise Uncompressed_Size_Error
            with "Uncompressed size mismatch: in catalogue:" & memo_uncomp_size'Image &
                 "; in post-data data descriptor:" & hint.dd.uncompressed_size'Image;
        end if;
      end;
    end if;

    if hint.dd.crc_32 /= UnZ_Glob.crc32val then
      UnZ_IO.Delete_output;
      raise CRC_Error with
        "CRC stored in archive: " & Hexadecimal (hint.dd.crc_32) &
        "; CRC computed now: " & Hexadecimal (UnZ_Glob.crc32val);
    end if;

    case write_mode is
      when write_to_binary_file =>
        Ada.Streams.Stream_IO.Close (UnZ_IO.out_bin_file);
      when write_to_text_file =>
        Ada.Text_IO.Close (UnZ_IO.out_txt_file);
      when write_to_memory | write_to_stream | just_test =>
        null;  --  Nothing to close!
    end case;
    if some_trace then
      Ada.Text_IO.Close (LZ77_dump);
    end if;

  exception
    when others =>  --  close the file in case of an error, if not yet closed
      case write_mode is  --  or deleted
        when write_to_binary_file =>
          if Ada.Streams.Stream_IO.Is_Open (UnZ_IO.out_bin_file) then
            Ada.Streams.Stream_IO.Close (UnZ_IO.out_bin_file);
          end if;
        when write_to_text_file =>
          if Ada.Text_IO.Is_Open (UnZ_IO.out_txt_file) then
            Ada.Text_IO.Close (UnZ_IO.out_txt_file);
          end if;
        when write_to_memory | write_to_stream | just_test =>
          null;  --  Nothing to close!
      end case;
      raise;
  end Decompress_Data;

end UnZip.Decompress;


Zip-Ada: Ada library for zip archive files (.zip). Ada programming.
Some news about Zip-Ada and other Ada projects on Gautier's blog.