context: namespace Maticsoft.ZipLib.BZip2
  {
      using Maticsoft.ZipLib.Checksums;
      using System;
      using System.IO;
  
      public class BZip2InputStream : Stream
      {
          private int[][] baseArray = new int[6][];
          private Stream baseStream;
          private bool blockRandomised;
          private int blockSize100k;
          private int bsBuff;
          private int bsLive;
          private int ch2;
          private int chPrev;
          private int computedBlockCRC;
          private uint computedCombinedCRC;
          private int count;
          private int currentChar = -1;
          private int currentState = 1;
          private int i2;
          private bool[] inUse = new bool[0x100];
          private bool isStreamOwner = true;
          private int j2;
          private int last;
          private int[][] limit = new int[6][];
          private byte[] ll8;
          private IChecksum mCrc = new StrangeCRC();
          private int[] minLens = new int[6];
          private int nInUse;
          private const int NO_RAND_PART_A_STATE = 5;
          private const int NO_RAND_PART_B_STATE = 6;
          private const int NO_RAND_PART_C_STATE = 7;
          private int origPtr;
          private int[][] perm = new int[6][];
          private const int RAND_PART_A_STATE = 2;
          private const int RAND_PART_B_STATE = 3;
          private const int RAND_PART_C_STATE = 4;
          private int rNToGo;
          private int rTPos;
          private byte[] selector = new byte[0x4652];
          private byte[] selectorMtf = new byte[0x4652];
          private byte[] seqToUnseq = new byte[0x100];
          private const int START_BLOCK_STATE = 1;
          private int storedBlockCRC;
          private int storedCombinedCRC;
          private bool streamEnd;
          private int tPos;
          private int[] tt;
          private byte[] unseqToSeq = new byte[0x100];
          private int[] unzftab = new int[0x100];
          private byte z;
  
          public BZip2InputStream(Stream stream)
          {
              for (int i = 0; i < 6; i++)
              {
                  this.limit[i] = new int[0x102];
                  this.baseArray[i] = new int[0x102];
                  this.perm[i] = new int[0x102];
              }
              this.BsSetStream(stream);
              this.Initialize();
              this.InitBlock();
              this.SetupBlock();
          }
  
          private static void BadBlockHeader()
          {
              throw new BZip2Exception("BZip2 input stream bad block header");
          }
  
          private static void BlockOverrun()
          {
              throw new BZip2Exception("BZip2 input stream block overrun");
          }
  
          private int BsGetInt32()
          {
              int num = (this.BsR(8) << 8) | this.BsR(8);
              num = (num << 8) | this.BsR(8);
              return ((num << 8) | this.BsR(8));
          }
  
          private int BsGetIntVS(int numBits)
          {
              return this.BsR(numBits);
          }
  
          private char BsGetUChar()
          {
              return (char) this.BsR(8);
          }
  
          private int BsR(int n)
          {
              while (this.bsLive < n)
              {
                  this.FillBuffer();
              }
              int num = (this.bsBuff >> (this.bsLive - n)) & ((((int) 1) << n) - 1);
              this.bsLive -= n;
              return num;
          }
  
          private void BsSetStream(Stream stream)
          {
              this.baseStream = stream;
              this.bsLive = 0;
              this.bsBuff = 0;
          }
  
          public override void Close()
          {
              if (this.IsStreamOwner && (this.baseStream != null))
              {
                  this.baseStream.Close();
              }
          }
  
          private void Complete()
          {
              this.storedCombinedCRC = this.BsGetInt32();
              if (this.storedCombinedCRC != this.computedCombinedCRC)
              {
                  CrcError();
              }
              this.streamEnd = true;
          }
  
          private static void CompressedStreamEOF()
          {
              throw new EndOfStreamException("BZip2 input stream end of compressed stream");
          }
  
          private static void CrcError()
          {
              throw new BZip2Exception("BZip2 input stream crc error");
          }
  
          private void EndBlock()
          {
              this.computedBlockCRC = (int) this.mCrc.Value;
              if (this.storedBlockCRC != this.computedBlockCRC)
              {
                  CrcError();
              }
              this.computedCombinedCRC = ((this.computedCombinedCRC << 1) & uint.MaxValue) | (this.computedCombinedCRC >> 0x1f);
              this.computedCombinedCRC ^= (uint) this.computedBlockCRC;
          }
  
          private void FillBuffer()
          {
              int num = 0;
              try
              {
                  num = this.baseStream.ReadByte();
              }
              catch (Exception)
              {
                  CompressedStreamEOF();
              }
              if (num == -1)
              {
                  CompressedStreamEOF();
              }
              this.bsBuff = (this.bsBuff << 8) | (num & 0xff);
              this.bsLive += 8;
          }
  
          public override void Flush()
          {
              if (this.baseStream != null)
              {
                  this.baseStream.Flush();
              }
          }
  
          private void GetAndMoveToFrontDecode()
          {
              int num6;
              int num10;
              bool flag;
              byte[] buffer = new byte[0x100];
              int num2 = 0x186a0 * this.blockSize100k;
              this.origPtr = this.BsGetIntVS(0x18);
              this.RecvDecodingTables();
              int num3 = this.nInUse + 1;
              int index = -1;
              int num5 = 0;
              for (num6 = 0; num6 <= 0xff; num6++)
              {
                  this.unzftab[num6] = 0;
              }
              for (num6 = 0; num6 <= 0xff; num6++)
              {
                  buffer[num6] = (byte) num6;
              }
              this.last = -1;
              if (num5 == 0)
              {
                  index++;
                  num5 = 50;
              }
              num5--;
              int num7 = this.selector[index];
              int n = this.minLens[num7];
              int num9 = this.BsR(n);
              while (num9 > this.limit[num7][n])
              {
                  if (n > 20)
                  {
                      throw new BZip2Exception("Bzip data error");
                  }
                  n++;
                  while (this.bsLive < 1)
                  {
                      this.FillBuffer();
                  }
                  num10 = (this.bsBuff >> (this.bsLive - 1)) & 1;
                  this.bsLive--;
                  num9 = (num9 << 1) | num10;
              }
              if (((num9 - this.baseArray[num7][n]) < 0) || ((num9 - this.baseArray[num7][n]) >= 0x102))
              {
                  throw new BZip2Exception("Bzip data error");
              }
              int num = this.perm[num7][num9 - this.baseArray[num7][n]];
          Label_04B9:
              flag = true;
              if (num == num3)
              {
                  return;
              }
              switch (num)
              {
                  case 0:
                  case 1:
                  {
                      int num11 = -1;
                      int num12 = 1;
                      do
                      {
                          switch (num)
                          {
                              case 0:
                                  num11 += num12;
                                  break;
  
                              case 1:
                                  num11 += 2 * num12;
                                  break;
                          }
                          num12 = num12 << 1;
                          if (num5 == 0)
                          {
                              index++;
                              num5 = 50;
                          }
                          num5--;
                          num7 = this.selector[index];
                          n = this.minLens[num7];
                          num9 = this.BsR(n);
                          while (num9 > this.limit[num7][n])
                          {
                              n++;
                              while (this.bsLive < 1)
                              {
                                  this.FillBuffer();
                              }
                              num10 = (this.bsBuff >> (this.bsLive - 1)) & 1;
                              this.bsLive--;
                              num9 = (num9 << 1) | num10;
                          }
                          num = this.perm[num7][num9 - this.baseArray[num7][n]];
                      }
                      while ((num == 0) || (num == 1));
                      num11++;
                      byte num13 = this.seqToUnseq[buffer[0]];
                      this.unzftab[num13] += num11;
                      while (num11 > 0)
                      {
                          this.last++;
                          this.ll8[this.last] = num13;
                          num11--;
                      }
                      if (this.last >= num2)
                      {
                          BlockOverrun();
                      }
                      goto Label_04B9;
                  }
              }
              this.last++;
              if (this.last >= num2)
              {
                  BlockOverrun();
              }
              byte num14 = buffer[num - 1];
              this.unzftab[this.seqToUnseq[num14]]++;
              this.ll8[this.last] = this.seqToUnseq[num14];
              for (int i = num - 1; i > 0; i--)
              {
                  buffer[i] = buffer[i - 1];
              }
              buffer[0] = num14;
              if (num5 == 0)
              {
                  index++;
                  num5 = 50;
              }
              num5--;
              num7 = this.selector[index];
  
gt:             n = this.minLens[num7];