/*
    UnLZX 2.x by Oliver Gantert

    based on 1.0 by David Tritscher
*/

#ifndef UNLZX_DECRUNCH_H
#define UNLZX_DECRUNCH_H

#include "unlzx.h"

void decrunch()
{
  register unsigned int control;
  register int shift;
  unsigned int temp; /* could be a register */
  unsigned int symbol, count;
  unsigned char *string;

  control = global_control;
  shift = global_shift;

  do
  {
    if((symbol = literal_table[control & 4095]) >= 768)
    {
      control >>= 12;
      if((shift -= 12) < 0)
      {
        shift += 16;
        control += *source++ << (8 + shift);
        control += *source++ << shift;
      }
      do /* literal is longer than 12 bits */
      {
        symbol = literal_table[(control & 1) + (symbol << 1)];
        if(!shift--)
        {
          shift += 16;
          control += *source++ << 24;
          control += *source++ << 16;
        }
        control >>= 1;
      }
      while(symbol >= 768);
    }
    else
    {
      temp = literal_len[symbol];
      control >>= temp;
      if((shift -= temp) < 0)
      {
        shift += 16;
        control += *source++ << (8 + shift);
        control += *source++ << shift;
      }
    }
    if(symbol < 256)
    {
      *destination++ = symbol;
    }
    else
    {
      symbol -= 256;
      count = table_two[temp = symbol & 31];
      temp = table_one[temp];
      if((temp >= 3) && (decrunch_method == 3))
      {
        temp -= 3;
        count += ((control & table_three[temp]) << 3);
        control >>= temp;
        if((shift -= temp) < 0)
        {
          shift += 16;
          control += *source++ << (8 + shift);
          control += *source++ << shift;
        }
        count += (temp = offset_table[control & 127]);
        temp = offset_len[temp];
      }
      else
      {
        count += control & table_three[temp];
        if(!count) count = last_offset;
      }
      control >>= temp;
      if((shift -= temp) < 0)
      {
        shift += 16;
        control += *source++ << (8 + shift);
        control += *source++ << shift;
      }
      last_offset = count;

      count = table_two[temp = (symbol >> 5) & 15] + 3;
      temp = table_one[temp];
      count += (control & table_three[temp]);
      control >>= temp;
      if((shift -= temp) < 0)
      {
        shift += 16;
        control += *source++ << (8 + shift);
        control += *source++ << shift;
      }
      string = (decrunch_buffer + last_offset < destination) ?
      destination - last_offset : destination + 65536 - last_offset;
      do
      {
        *destination++ = *string++;
      }
      while(--count);
    }
  }
  while((destination < destination_end) && (source < source_end));

  global_control = control;
  global_shift = shift;
}

#endif  /*  UNLZX_DECRUNCH_H  */
