// Rar3Decoder.cpp
// According to unRAR license, this code may not be used to develop
// a program that creates RAR archives

/* This code uses Carryless rangecoder (1999): Dmitry Subbotin : Public domain */
 
#include "StdAfx.h"

#include "../../../C/Alloc.h"

#include "../Common/StreamUtils.h"

#include "Rar3Decoder.h"

namespace NCompress {
namespace NRar3 {

static const UInt32 kNumAlignReps = 15;

static const unsigned kSymbolReadTable = 256;
static const unsigned kSymbolRep = 259;

static const Byte kDistDirectBits[kDistTableSize] =
  {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,15,15,
  16,16,16,16,16,16,16,16,16,16,16,16,16,16,
  18,18,18,18,18,18,18,18,18,18,18,18};

static const Byte kLen2DistStarts[kNumLen2Symbols] = {0,4,8,16,32,64,128,192};
static const Byte kLen2DistDirectBits[kNumLen2Symbols] = {2,2,3, 4, 5, 6,  6,  6};

static const UInt32 kDistLimit3 = 0x2000 - 2;
static const UInt32 kDistLimit4 = 0x40000 - 2;

static const UInt32 kNormalMatchMinLen = 3;

static const UInt32 kVmDataSizeMax = 1 << 16;
static const UInt32 kVmCodeSizeMax = 1 << 16;

extern "C" {

static Byte Wrap_ReadByte(IByteInPtr pp) throw()
{
  CByteIn *p = Z7_CONTAINER_FROM_VTBL_CLS(pp, CByteIn, IByteIn_obj);
  return p->BitDecoder.Stream.ReadByte();
}

static Byte Wrap_ReadBits8(IByteInPtr pp) throw()
{
  CByteIn *p = Z7_CONTAINER_FROM_VTBL_CLS(pp, CByteIn, IByteIn_obj);
  return (Byte)p->BitDecoder.ReadByteFromAligned();
}

}


CDecoder::CDecoder():
  _isSolid(false),
  _solidAllowed(false),
  _window(NULL),
  _winPos(0),
  _wrPtr(0),
  _lzSize(0),
  _writtenFileSize(0),
  _vmData(NULL),
  _vmCode(NULL)
{
  Ppmd7_Construct(&_ppmd);
  
  UInt32 start = 0;
  for (UInt32 i = 0; i < kDistTableSize; i++)
  {
    kDistStart[i] = start;
    start += ((UInt32)1 << kDistDirectBits[i]);
  }
}

CDecoder::~CDecoder()
{
  InitFilters();
  ::MidFree(_vmData);
  ::MidFree(_window);
  Ppmd7_Free(&_ppmd, &g_BigAlloc);
}

HRESULT CDecoder::WriteDataToStream(const Byte *data, UInt32 size)
{
  return WriteStream(_outStream, data, size);
}

HRESULT CDecoder::WriteData(const Byte *data, UInt32 size)
{
  HRESULT res = S_OK;
  if (_writtenFileSize < _unpackSize)
  {
    UInt32 curSize = size;
    UInt64 remain = _unpackSize - _writtenFileSize;
    if (remain < curSize)
      curSize = (UInt32)remain;
    res = WriteDataToStream(data, curSize);
  }
  _writtenFileSize += size;
  return res;
}

HRESULT CDecoder::WriteArea(UInt32 startPtr, UInt32 endPtr)
{
  if (startPtr <= endPtr)
    return WriteData(_window + startPtr, endPtr - startPtr);
  RINOK(WriteData(_window + startPtr, kWindowSize - startPtr))
  return WriteData(_window, endPtr);
}

void CDecoder::ExecuteFilter(unsigned tempFilterIndex, NVm::CBlockRef &outBlockRef)
{
  CTempFilter *tempFilter = _tempFilters[tempFilterIndex];
  tempFilter->InitR[6] = (UInt32)_writtenFileSize;
  NVm::SetValue32(&tempFilter->GlobalData[0x24], (UInt32)_writtenFileSize);
  NVm::SetValue32(&tempFilter->GlobalData[0x28], (UInt32)(_writtenFileSize >> 32));
  CFilter *filter = _filters[tempFilter->FilterIndex];
  if (!filter->IsSupported)
    _unsupportedFilter = true;
  if (!_vm.Execute(filter, tempFilter, outBlockRef, filter->GlobalData))
    _unsupportedFilter = true;
  delete tempFilter;
  _tempFilters[tempFilterIndex] = NULL;
  _numEmptyTempFilters++;
}

HRESULT CDecoder::WriteBuf()
{
  UInt32 writtenBorder = _wrPtr;
  UInt32 writeSize = (_winPos - writtenBorder) & kWindowMask;
  FOR_VECTOR (i, _tempFilters)
  {
    CTempFilter *filter = _tempFilters[i];
    if (!filter)
      continue;
    if (filter->NextWindow)
    {
      filter->NextWindow = false;
      continue;
    }
    UInt32 blockStart = filter->BlockStart;
    UInt32 blockSize = filter->BlockSize;
    if (((blockStart - writtenBorder) & kWindowMask) < writeSize)
    {
      if (writtenBorder != blockStart)
      {
        RINOK(WriteArea(writtenBorder, blockStart))
        writtenBorder = blockStart;
        writeSize = (_winPos - writtenBorder) & kWindowMask;
      }
      if (blockSize <= writeSize)
      {
        UInt32 blockEnd = (blockStart + blockSize) & kWindowMask;
        if (blockStart < blockEnd || blockEnd == 0)
          _vm.SetMemory(0, _window + blockStart, blockSize);
        else
        {
          UInt32 tailSize = kWindowSize - blockStart;
          _vm.SetMemory(0, _window + blockStart, tailSize);
          _vm.SetMemory(tailSize, _window, blockEnd);
        }
        NVm::CBlockRef outBlockRef;
        ExecuteFilter(i, outBlockRef);
        while (i + 1 < _tempFilters.Size())
        {
          CTempFilter *nextFilter = _tempFilters[i + 1];
          if (!nextFilter
              || nextFilter->BlockStart != blockStart
              || nextFilter->BlockSize != outBlockRef.Size
              || nextFilter->NextWindow)
            break;
          _vm.SetMemory(0, _vm.GetDataPointer(outBlockRef.Offset), outBlockRef.Size);
          ExecuteFilter(++i, outBlockRef);
        }
        WriteDataToStream(_vm.GetDataPointer(outBlockRef.Offset), outBlockRef.Size);
        _writtenFileSize += outBlockRef.Size;
        writtenBorder = blockEnd;
        writeSize = (_winPos - writtenBorder) & kWindowMask;
      }
      else
      {
        for (unsigned j = i; j < _tempFilters.Size(); j++)
        {
          CTempFilter *filter2 = _tempFilters[j];
          if (filter2 && filter2->NextWindow)
            filter2->NextWindow = false;
        }
        _wrPtr = writtenBorder;
        return S_OK; // check it
      }
    }
  }
      
  _wrPtr = _winPos;
  return WriteArea(writtenBorder, _winPos);
}

void CDecoder::InitFilters()
{
  _lastFilter = 0;
  _numEmptyTempFilters = 0;
  unsigned i;
  for (i = 0; i < _tempFilters.Size(); i++)
    delete _tempFilters[i];
  _tempFilters.Clear();
  for (i = 0; i < _filters.Size(); i++)
    delete _filters[i];
  _filters.Clear();
}

static const unsigned MAX_UNPACK_FILTERS = 8192;

bool CDecoder::AddVmCode(UInt32 firstByte, UInt32 codeSize)
{
  CMemBitDecoder inp;
  inp.Init(_vmData, codeSize);

  UInt32 filterIndex;
  
  if (firstByte & 0x80)
  {
    filterIndex = inp.ReadEncodedUInt32();
    if (filterIndex == 0)
      InitFilters();
    else
      filterIndex--;
  }
  else
    filterIndex = _lastFilter;
  
  if (filterIndex > (UInt32)_filters.Size())
    return false;
  _lastFilter = filterIndex;
  bool newFilter = (filterIndex == (UInt32)_filters.Size());

  CFilter *filter;
  if (newFilter)
  {
    // check if too many filters
    if (filterIndex > MAX_UNPACK_FILTERS)
      return false;
    filter = new CFilter;
    _filters.Add(filter);
  }
  else
  {
    filter = _filters[filterIndex];
    filter->ExecCount++;
  }

  if (_numEmptyTempFilters != 0)
  {
    const unsigned num = _tempFilters.Size();
    CTempFilter **tempFilters = _tempFilters.NonConstData();
    
    unsigned w = 0;
    for (unsigned i = 0; i < num; i++)
    {
      CTempFilter *tf = tempFilters[i];
      if (tf)
        tempFilters[w++] = tf;
    }

    _tempFilters.DeleteFrom(w);
    _numEmptyTempFilters = 0;
  }
  
  if (_tempFilters.Size() > MAX_UNPACK_FILTERS)
    return false;
  CTempFilter *tempFilter = new CTempFilter;
  _tempFilters.Add(tempFilter);
  tempFilter->FilterIndex = filterIndex;
 
  UInt32 blockStart = inp.ReadEncodedUInt32();
  if (firstByte & 0x40)
    blockStart += 258;
  tempFilter->BlockStart = (blockStart + _winPos) & kWindowMask;
  if (firstByte & 0x20)
    filter->BlockSize = inp.ReadEncodedUInt32();
  tempFilter->BlockSize = filter->BlockSize;
  tempFilter->NextWindow = _wrPtr != _winPos && ((_wrPtr - _winPos) & kWindowMask) <= blockStart;

  memset(tempFilter->InitR, 0, sizeof(tempFilter->InitR));
  tempFilter->InitR[3] = NVm::kGlobalOffset;
  tempFilter->InitR[4] = tempFilter->BlockSize;
  tempFilter->InitR[5] = filter->ExecCount;
  if (firstByte & 0x10)
  {
    UInt32 initMask = inp.ReadBits(NVm::kNumGpRegs);
    for (unsigned i = 0; i < NVm::kNumGpRegs; i++)
      if (initMask & (1 << i))
        tempFilter->InitR[i] = inp.ReadEncodedUInt32();
  }

  bool isOK = true;
  if (newFilter)
  {
    UInt32 vmCodeSize = inp.ReadEncodedUInt32();
    if (vmCodeSize >= kVmCodeSizeMax || vmCodeSize == 0)
      return false;
    for (UInt32 i = 0; i < vmCodeSize; i++)
      _vmCode[i] = (Byte)inp.ReadBits(8);
    isOK = filter->PrepareProgram(_vmCode, vmCodeSize);
  }

  {
    Byte *globalData = &tempFilter->GlobalData[0];
    for (unsigned i = 0; i < NVm::kNumGpRegs; i++)
      NVm::SetValue32(&globalData[i * 4], tempFilter->InitR[i]);
    NVm::SetValue32(&globalData[NVm::NGlobalOffset::kBlockSize], tempFilter->BlockSize);
    NVm::SetValue32(&globalData[NVm::NGlobalOffset::kBlockPos], 0); // It was commented. why?
    NVm::SetValue32(&globalData[NVm::NGlobalOffset::kExecCount], filter->ExecCount);
  }

  if (firstByte & 8)
  {
    UInt32 dataSize = inp.ReadEncodedUInt32();
    if (dataSize > NVm::kGlobalSize - NVm::kFixedGlobalSize)
      return false;
    CRecordVector<Byte> &globalData = tempFilter->GlobalData;
    unsigned requiredSize = (unsigned)(dataSize + NVm::kFixedGlobalSize);
    if (globalData.Size() < requiredSize)
      globalData.ChangeSize_KeepData(requiredSize);
    Byte *dest = &globalData[NVm::kFixedGlobalSize];
    for (UInt32 i = 0; i < dataSize; i++)
      dest[i] = (Byte)inp.ReadBits(8);
  }
  
  return isOK;
}

bool CDecoder::ReadVmCodeLZ()
{
  UInt32 firstByte = ReadBits(8);
  UInt32 len = (firstByte & 7) + 1;
  if (len == 7)
    len = ReadBits(8) + 7;
  else if (len == 8)
    len = ReadBits(16);
  if (len > kVmDataSizeMax)
    return false;
  for (UInt32 i = 0; i < len; i++)
    _vmData[i] = (Byte)ReadBits(8);
  return AddVmCode(firstByte, len);
}


// int CDecoder::DecodePpmSymbol() { return Ppmd7a_DecodeSymbol(&_ppmd); }
#define DecodePpmSymbol() Ppmd7a_DecodeSymbol(&_ppmd)


bool CDecoder::ReadVmCodePPM()
{
  const int firstByte = DecodePpmSymbol();
  if (firstByte < 0)
    return false;
  UInt32 len = (firstByte & 7) + 1;
  if (len == 7)
  {
    const int b1 = DecodePpmSymbol();
    if (b1 < 0)
      return false;
    len = (unsigned)b1 + 7;
  }
  else if (len == 8)
  {
    const int b1 = DecodePpmSymbol();
    if (b1 < 0)
      return false;
    const int b2 = DecodePpmSymbol();
    if (b2 < 0)
      return false;
    len = (unsigned)b1 * 256 + (unsigned)b2;
  }
  if (len > kVmDataSizeMax)
    return false;
  if (InputEofError_Fast())
    return false;
  for (UInt32 i = 0; i < len; i++)
  {
    const int b = DecodePpmSymbol();
    if (b < 0)
      return false;
    _vmData[i] = (Byte)b;
  }
  return AddVmCode((unsigned)firstByte, len);
}

#define RIF(x) { if (!(x)) return S_FALSE; }

UInt32 CDecoder::ReadBits(unsigned numBits) { return m_InBitStream.BitDecoder.ReadBits(numBits); }

// ---------- PPM ----------

HRESULT CDecoder::InitPPM()
{
  unsigned maxOrder = (unsigned)ReadBits(7);

  const bool reset = ((maxOrder & 0x20) != 0);
  UInt32 maxMB = 0;
  if (reset)
    maxMB = (Byte)Wrap_ReadBits8(&m_InBitStream.IByteIn_obj);
  else
  {
    if (PpmError || !Ppmd7_WasAllocated(&_ppmd))
      return S_FALSE;
  }
  if (maxOrder & 0x40)
    PpmEscChar = (Byte)Wrap_ReadBits8(&m_InBitStream.IByteIn_obj);

  _ppmd.rc.dec.Stream = &m_InBitStream.IByteIn_obj;
  m_InBitStream.IByteIn_obj.Read = Wrap_ReadBits8;

  Ppmd7a_RangeDec_Init(&_ppmd.rc.dec);

  m_InBitStream.IByteIn_obj.Read = Wrap_ReadByte;

  if (reset)
  {
    PpmError = true;
    maxOrder = (maxOrder & 0x1F) + 1;
    if (maxOrder > 16)
      maxOrder = 16 + (maxOrder - 16) * 3;
    if (maxOrder == 1)
    {
      Ppmd7_Free(&_ppmd, &g_BigAlloc);
      return S_FALSE;
    }
    if (!Ppmd7_Alloc(&_ppmd, (maxMB + 1) << 20, &g_BigAlloc))
      return E_OUTOFMEMORY;
    Ppmd7_Init(&_ppmd, maxOrder);
    PpmError = false;
  }
  return S_OK;
}


HRESULT CDecoder::DecodePPM(Int32 num, bool &keepDecompressing)
{
  keepDecompressing = false;
  if (PpmError)
    return S_FALSE;
  do
  {
    if (((_wrPtr - _winPos) & kWindowMask) < 260 && _wrPtr != _winPos)
    {
      RINOK(WriteBuf())
      if (_writtenFileSize > _unpackSize)
      {
        keepDecompressing = false;
        return S_OK;
      }
    }
    if (InputEofError_Fast())
      return false;
    const int c = DecodePpmSymbol();
    if (c < 0)
    {
      PpmError = true;
      return S_FALSE;
    }
    if (c == PpmEscChar)
    {
      const int nextCh = DecodePpmSymbol();
      if (nextCh < 0)
      {
        PpmError = true;
        return S_FALSE;
      }
      if (nextCh == 0)
        return ReadTables(keepDecompressing);
      if (nextCh == 2 || nextCh == -1)
        return S_OK;
      if (nextCh == 3)
      {
        if (!ReadVmCodePPM())
        {
          PpmError = true;
          return S_FALSE;
        }
        continue;
      }
      if (nextCh == 4 || nextCh == 5)
      {
        UInt32 dist = 0;
        UInt32 len = 4;
        if (nextCh == 4)
        {
          for (int i = 0; i < 3; i++)
          {
            const int c2 = DecodePpmSymbol();
            if (c2 < 0)
            {
              PpmError = true;
              return S_FALSE;
            }
            dist = (dist << 8) + (Byte)c2;
          }
          dist++;
          len += 28;
        }
        const int c2 = DecodePpmSymbol();
        if (c2 < 0)
        {
          PpmError = true;
          return S_FALSE;
        }
        len += (unsigned)c2;
        if (dist >= _lzSize)
          return S_FALSE;
        CopyBlock(dist, len);
        num -= (Int32)len;
        continue;
      }
    }
    PutByte((Byte)c);
    num--;
  }
  while (num >= 0);
  keepDecompressing = true;
  return S_OK;
}

// ---------- LZ ----------

HRESULT CDecoder::ReadTables(bool &keepDecompressing)
{
  keepDecompressing = true;
  m_InBitStream.BitDecoder.AlignToByte();
  if (ReadBits(1) != 0)
  {
    _lzMode = false;
    return InitPPM();
  }

  TablesRead = false;
  TablesOK = false;

  _lzMode = true;
  PrevAlignBits = 0;
  PrevAlignCount = 0;

  const unsigned kLevelTableSize = 20;
  Byte levelLevels[kLevelTableSize];
  Byte lens[kTablesSizesSum];

  if (ReadBits(1) == 0)
    memset(m_LastLevels, 0, kTablesSizesSum);

  unsigned i;

  for (i = 0; i < kLevelTableSize; i++)
  {
    const UInt32 len = ReadBits(4);
    if (len == 15)
    {
      UInt32 zeroCount = ReadBits(4);
      if (zeroCount != 0)
      {
        zeroCount += 2;
        while (zeroCount-- > 0 && i < kLevelTableSize)
          levelLevels[i++]=0;
        i--;
        continue;
      }
    }
    levelLevels[i] = (Byte)len;
  }
  
  NHuffman::CDecoder256<kNumHuffmanBits, kLevelTableSize, 6> m_LevelDecoder;
  RIF(m_LevelDecoder.Build(levelLevels, NHuffman::k_BuildMode_Full))
  
  i = 0;
  
  do
  {
    const unsigned sym = m_LevelDecoder.DecodeFull(&m_InBitStream.BitDecoder);
    if (sym < 16)
    {
      lens[i] = Byte((sym + m_LastLevels[i]) & 15);
      i++;
    }
#if 0
    else if (sym > kLevelTableSize)
      return S_FALSE;
#endif
    else
    {
      unsigned num = ((sym /* - 16 */) & 1) * 4;
      num += num + 3 + (unsigned)ReadBits(num + 3);
      num += i;
      if (num > kTablesSizesSum)
        num = kTablesSizesSum;
      Byte v = 0;
      if (sym < 16 + 2)
      {
        if (i == 0)
          return S_FALSE;
        v = lens[(size_t)i - 1];
      }
      do
        lens[i++] = v;
      while (i < num);
    }
  }
  while (i < kTablesSizesSum);

  if (InputEofError())
    return S_FALSE;

  TablesRead = true;

  // original code has check here:
  /*
  if (InAddr > ReadTop)
  {
    keepDecompressing = false;
    return true;
  }
  */

  RIF(m_MainDecoder.Build(&lens[0]))
  RIF(m_DistDecoder.Build(&lens[kMainTableSize]))
  RIF(m_AlignDecoder.Build(&lens[kMainTableSize + kDistTableSize]))
  RIF(m_LenDecoder.Build(&lens[kMainTableSize + kDistTableSize + kAlignTableSize]))

  memcpy(m_LastLevels, lens, kTablesSizesSum);

  TablesOK = true;

  return S_OK;
}

/*
class CCoderReleaser
{
  CDecoder *m_Coder;
public:
  CCoderReleaser(CDecoder *coder): m_Coder(coder) {}
  ~CCoderReleaser()
  {
    m_Coder->ReleaseStreams();
  }
};
*/

HRESULT CDecoder::ReadEndOfBlock(bool &keepDecompressing)
{
  if (ReadBits(1) == 0)
  {
    // new file
    keepDecompressing = false;
    TablesRead = (ReadBits(1) == 0);
    return S_OK;
  }
  TablesRead = false;
  return ReadTables(keepDecompressing);
}


HRESULT CDecoder::DecodeLZ(bool &keepDecompressing)
{
  UInt32 rep0 = _reps[0];
  UInt32 rep1 = _reps[1];
  UInt32 rep2 = _reps[2];
  UInt32 rep3 = _reps[3];
  UInt32 len = _lastLength;
  for (;;)
  {
    if (((_wrPtr - _winPos) & kWindowMask) < 260 && _wrPtr != _winPos)
    {
      RINOK(WriteBuf())
      if (_writtenFileSize > _unpackSize)
      {
        keepDecompressing = false;
        return S_OK;
      }
    }
    
    if (InputEofError_Fast())
      return S_FALSE;

    unsigned sym = m_MainDecoder.Decode(&m_InBitStream.BitDecoder);
    if (sym < 256)
    {
      PutByte((Byte)sym);
      continue;
    }
    else if (sym == kSymbolReadTable)
    {
      RINOK(ReadEndOfBlock(keepDecompressing))
      break;
    }
    else if (sym == 257)
    {
      if (!ReadVmCodeLZ())
        return S_FALSE;
      continue;
    }
    else if (sym == 258)
    {
      if (len == 0)
        return S_FALSE;
    }
    else if (sym < kSymbolRep + 4)
    {
      if (sym != kSymbolRep)
      {
        UInt32 dist;
        if (sym == kSymbolRep + 1)
          dist = rep1;
        else
        {
          if (sym == kSymbolRep + 2)
            dist = rep2;
          else
          {
            dist = rep3;
            rep3 = rep2;
          }
          rep2 = rep1;
        }
        rep1 = rep0;
        rep0 = dist;
      }

      const unsigned sym2 = m_LenDecoder.Decode(&m_InBitStream.BitDecoder);
      if (sym2 >= kLenTableSize)
        return S_FALSE;
      len = 2 + sym2;
      if (sym2 >= 8)
      {
        const unsigned num = (sym2 >> 2) - 1;
        len = 2 + (UInt32)((4 + (sym2 & 3)) << num) + m_InBitStream.BitDecoder.ReadBits_upto8(num);
      }
    }
    else
    {
      rep3 = rep2;
      rep2 = rep1;
      rep1 = rep0;
      if (sym < 271)
      {
        sym -= 263;
        rep0 = kLen2DistStarts[sym] + m_InBitStream.BitDecoder.ReadBits_upto8(kLen2DistDirectBits[sym]);
        len = 2;
      }
      else if (sym < 299)
      {
        sym -= 271;
        len = kNormalMatchMinLen + sym;
        if (sym >= 8)
        {
          const unsigned num = (sym >> 2) - 1;
          len = kNormalMatchMinLen + (UInt32)((4 + (sym & 3)) << num) + m_InBitStream.BitDecoder.ReadBits_upto8(num);
        }
        const unsigned sym2 = m_DistDecoder.Decode(&m_InBitStream.BitDecoder);
        if (sym2 >= kDistTableSize)
          return S_FALSE;
        rep0 = kDistStart[sym2];
        unsigned numBits = kDistDirectBits[sym2];
        if (sym2 >= (kNumAlignBits * 2) + 2)
        {
          if (numBits > kNumAlignBits)
            rep0 += (m_InBitStream.BitDecoder.ReadBits(numBits - kNumAlignBits) << kNumAlignBits);
          if (PrevAlignCount > 0)
          {
            PrevAlignCount--;
            rep0 += PrevAlignBits;
          }
          else
          {
            const unsigned sym3 = m_AlignDecoder.Decode(&m_InBitStream.BitDecoder);
            if (sym3 < (1 << kNumAlignBits))
            {
              rep0 += sym3;
              PrevAlignBits = sym3;
            }
            else if (sym3 == (1 << kNumAlignBits))
            {
              PrevAlignCount = kNumAlignReps;
              rep0 += PrevAlignBits;
            }
            else
              return S_FALSE;
          }
        }
        else
          rep0 += m_InBitStream.BitDecoder.ReadBits_upto8(numBits);
        len += ((UInt32)(kDistLimit4 - rep0) >> 31) + ((UInt32)(kDistLimit3 - rep0) >> 31);
      }
      else
        return S_FALSE;
    }
    if (rep0 >= _lzSize)
      return S_FALSE;
    CopyBlock(rep0, len);
  }
  _reps[0] = rep0;
  _reps[1] = rep1;
  _reps[2] = rep2;
  _reps[3] = rep3;
  _lastLength = len;

  return S_OK;
}


HRESULT CDecoder::CodeReal(ICompressProgressInfo *progress)
{
  _writtenFileSize = 0;
  _unsupportedFilter = false;
  
  if (!_isSolid)
  {
    _lzSize = 0;
    _winPos = 0;
    _wrPtr = 0;
    for (unsigned i = 0; i < kNumReps; i++)
      _reps[i] = 0;
    _lastLength = 0;
    memset(m_LastLevels, 0, kTablesSizesSum);
    TablesRead = false;
    PpmEscChar = 2;
    PpmError = true;
    InitFilters();
    // _errorMode = false;
  }

  /*
  if (_errorMode)
    return S_FALSE;
  */

  if (!_isSolid || !TablesRead)
  {
    bool keepDecompressing;
    RINOK(ReadTables(keepDecompressing))
    if (!keepDecompressing)
    {
      _solidAllowed = true;
      return S_OK;
    }
  }

  for (;;)
  {
    bool keepDecompressing;
    if (_lzMode)
    {
      if (!TablesOK)
        return S_FALSE;
      RINOK(DecodeLZ(keepDecompressing))
    }
    else
    {
      RINOK(DecodePPM(1 << 18, keepDecompressing))
    }

    if (InputEofError())
      return S_FALSE;

    const UInt64 packSize = m_InBitStream.BitDecoder.GetProcessedSize();
    RINOK(progress->SetRatioInfo(&packSize, &_writtenFileSize))
    if (!keepDecompressing)
      break;
  }

  _solidAllowed = true;

  RINOK(WriteBuf())
  const UInt64 packSize = m_InBitStream.BitDecoder.GetProcessedSize();
  RINOK(progress->SetRatioInfo(&packSize, &_writtenFileSize))
  if (_writtenFileSize < _unpackSize)
    return S_FALSE;

  if (_unsupportedFilter)
    return E_NOTIMPL;

  return S_OK;
}

Z7_COM7F_IMF(CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
    const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress))
{
  try
  {
    if (!inSize)
      return E_INVALIDARG;

    if (_isSolid && !_solidAllowed)
      return S_FALSE;
    _solidAllowed = false;

    if (!_vmData)
    {
      _vmData = (Byte *)::MidAlloc(kVmDataSizeMax + kVmCodeSizeMax);
      if (!_vmData)
        return E_OUTOFMEMORY;
      _vmCode = _vmData + kVmDataSizeMax;
    }
    
    if (!_window)
    {
      _window = (Byte *)::MidAlloc(kWindowSize);
      if (!_window)
        return E_OUTOFMEMORY;
    }
    if (!m_InBitStream.BitDecoder.Create(1 << 20))
      return E_OUTOFMEMORY;
    if (!_vm.Create())
      return E_OUTOFMEMORY;

    
    m_InBitStream.BitDecoder.SetStream(inStream);
    m_InBitStream.BitDecoder.Init();
    _outStream = outStream;
   
    // CCoderReleaser coderReleaser(this);
    _unpackSize = outSize ? *outSize : (UInt64)(Int64)-1;
    return CodeReal(progress);
  }
  catch(const CInBufferException &e)  { /* _errorMode = true; */ return e.ErrorCode; }
  catch(...) { /* _errorMode = true; */ return S_FALSE; }
  // CNewException is possible here. But probably CNewException is caused
  // by error in data stream.
}

Z7_COM7F_IMF(CDecoder::SetDecoderProperties2(const Byte *data, UInt32 size))
{
  if (size < 1)
    return E_INVALIDARG;
  _isSolid = ((data[0] & 1) != 0);
  return S_OK;
}

}}
