// We use it instead of direct PPM.DecodeChar call to be sure that
// we reset PPM structures in case of corrupt data. It is important,
// because these structures can be invalid after PPM.DecodeChar returned -1.
        int SafePPMDecodeChar()
        {
            int Ch = PPM.DecodeChar();

            if (Ch == -1)                // Corrupt PPM data found.
            {
                PPM.CleanUp();           // Reset possibly corrupt PPM data structures.
                UnpBlockType = BLOCK_LZ; // Set faster and more fail proof LZ mode.
            }
            return(Ch);
        }
        void Unpack29(bool Solid)
        {
            uint Bits;

            if (DDecode[1] == 0)
            {
                int Dist = 0, BitLength = 0, Slot = 0;
                for (int I = 0; I < DBitLengthCounts.Length; I++, BitLength++)
                {
                    for (int J = 0; J < DBitLengthCounts[I]; J++, Slot++, Dist += (1 << BitLength))
                    {
                        DDecode[Slot] = Dist;
                        DBits[Slot]   = (byte)BitLength;
                    }
                }
            }

            FileExtracted = true;

            if (!Suspended)
            {
                UnpInitData(Solid);
                if (!UnpReadBuf30())
                {
                    return;
                }
                if ((!Solid || !TablesRead3) && !ReadTables30())
                {
                    return;
                }
            }

            while (true)
            {
                UnpPtr &= MaxWinMask;

                if (Inp.InAddr > ReadBorder)
                {
                    if (!UnpReadBuf30())
                    {
                        break;
                    }
                }
                if (((WrPtr - UnpPtr) & MaxWinMask) < 260 && WrPtr != UnpPtr)
                {
                    UnpWriteBuf30();
                    if (WrittenFileSize > DestUnpSize)
                    {
                        return;
                    }
                    if (Suspended)
                    {
                        FileExtracted = false;
                        return;
                    }
                }
                if (UnpBlockType == BLOCK_PPM)
                {
                    // Here speed is critical, so we do not use SafePPMDecodeChar,
                    // because sometimes even the inline function can introduce
                    // some additional penalty.
                    int Ch = PPM.DecodeChar();
                    if (Ch == -1)                // Corrupt PPM data found.
                    {
                        PPM.CleanUp();           // Reset possibly corrupt PPM data structures.
                        UnpBlockType = BLOCK_LZ; // Set faster and more fail proof LZ mode.
                        break;
                    }
                    if (Ch == PPMEscChar)
                    {
                        int NextCh = SafePPMDecodeChar();
                        if (NextCh == 0) // End of PPM encoding.
                        {
                            if (!ReadTables30())
                            {
                                break;
                            }
                            continue;
                        }
                        if (NextCh == -1) // Corrupt PPM data found.
                        {
                            break;
                        }
                        if (NextCh == 2) // End of file in PPM mode.
                        {
                            break;
                        }
                        if (NextCh == 3) // Read VM code.
                        {
                            if (!ReadVMCodePPM())
                            {
                                break;
                            }
                            continue;
                        }
                        if (NextCh == 4) // LZ inside of PPM.
                        {
                            uint Distance = 0, Length;
                            bool Failed = false;
                            for (int I = 0; I < 4 && !Failed; I++)
                            {
                                int _Ch = SafePPMDecodeChar();
                                if (_Ch == -1)
                                {
                                    Failed = true;
                                }
                                else
                                if (I == 3)
                                {
                                    Length = (byte)_Ch;
                                }
                                else
                                {
                                    Distance = (Distance << 8) + (byte)_Ch;
                                }
                            }
                            if (Failed)
                            {
                                break;
                            }

                            CopyString(Length + 32, Distance + 2);
                            continue;
                        }
                        if (NextCh == 5) // One byte distance match (RLE) inside of PPM.
                        {
                            int Length = SafePPMDecodeChar();
                            if (Length == -1)
                            {
                                break;
                            }
                            CopyString((uint)(Length + 4), 1);
                            continue;
                        }
                        // If we are here, NextCh must be 1, what means that current byte
                        // is equal to our 'escape' byte, so we just store it to Window.
                    }
                    Window[UnpPtr++] = (byte)Ch;
                    continue;
                }

                uint Number = DecodeNumber(Inp, BlockTables.LD);
                if (Number < 256)
                {
                    Window[UnpPtr++] = (byte)Number;
                    continue;
                }
                if (Number >= 271)
                {
                    uint Length = (uint)(LDecode[Number -= 271] + 3);
                    if ((Bits = LBits[Number]) > 0)
                    {
                        Length += Inp.getbits() >> (int)(16 - Bits);
                        Inp.addbits(Bits);
                    }

                    uint DistNumber = DecodeNumber(Inp, BlockTables.DD);
                    uint Distance   = (uint)(DDecode[DistNumber] + 1);
                    if ((Bits = DBits[DistNumber]) > 0)
                    {
                        if (DistNumber > 9)
                        {
                            if (Bits > 4)
                            {
                                Distance += ((Inp.getbits() >> (int)(20 - Bits)) << 4);
                                Inp.addbits(Bits - 4);
                            }
                            if (LowDistRepCount > 0)
                            {
                                LowDistRepCount--;
                                Distance += (uint)PrevLowDist;
                            }
                            else
                            {
                                uint LowDist = DecodeNumber(Inp, BlockTables.LDD);
                                if (LowDist == 16)
                                {
                                    LowDistRepCount = (int)(LOW_DIST_REP_COUNT - 1);
                                    Distance       += (uint)PrevLowDist;
                                }
                                else
                                {
                                    Distance   += LowDist;
                                    PrevLowDist = (int)LowDist;
                                }
                            }
                        }
                        else
                        {
                            Distance += Inp.getbits() >> (int)(16 - Bits);
                            Inp.addbits(Bits);
                        }
                    }

                    if (Distance >= 0x2000)
                    {
                        Length++;
                        if (Distance >= 0x40000)
                        {
                            Length++;
                        }
                    }

                    InsertOldDist(Distance);
                    LastLength = Length;
                    CopyString(Length, Distance);
                    continue;
                }
                if (Number == 256)
                {
                    if (!ReadEndOfBlock())
                    {
                        break;
                    }
                    continue;
                }
                if (Number == 257)
                {
                    if (!ReadVMCode())
                    {
                        break;
                    }
                    continue;
                }
                if (Number == 258)
                {
                    if (LastLength != 0)
                    {
                        CopyString(LastLength, OldDist[0]);
                    }
                    continue;
                }
                if (Number < 263)
                {
                    uint DistNum  = Number - 259;
                    uint Distance = OldDist[DistNum];
                    for (uint I = DistNum; I > 0; I--)
                    {
                        OldDist[I] = OldDist[I - 1];
                    }
                    OldDist[0] = Distance;

                    uint LengthNumber = DecodeNumber(Inp, BlockTables.RD);
                    int  Length       = LDecode[LengthNumber] + 2;
                    if ((Bits = LBits[LengthNumber]) > 0)
                    {
                        Length += (int)(Inp.getbits() >> (int)(16 - Bits));
                        Inp.addbits(Bits);
                    }
                    LastLength = (uint)Length;
                    CopyString((uint)Length, Distance);
                    continue;
                }
                if (Number < 272)
                {
                    uint Distance = (uint)(SDDecode[Number -= 263] + 1);
                    if ((Bits = SDBits[Number]) > 0)
                    {
                        Distance += Inp.getbits() >> (int)(16 - Bits);
                        Inp.addbits(Bits);
                    }
                    InsertOldDist(Distance);
                    LastLength = 2;
                    CopyString(2, Distance);
                    continue;
                }
            }
            UnpWriteBuf30();
        }