C++ 7Zip解压源码,获取解压进度等信息

版权声明:转载请声明出处,谢谢~ https://blog.csdn.net/what951006/article/details/80224713
// 7ZipDecode.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "7ZipDecode.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// The one and only application object
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
#endif

#ifndef EXTERN_C_BEGIN
#ifdef __cplusplus
#define EXTERN_C_BEGIN extern "C" {
#define EXTERN_C_END }
#else
#define EXTERN_C_BEGIN
#define EXTERN_C_END
#endif
#endif

EXTERN_C_BEGIN

#define SZ_OK 0

#define SZ_ERROR_DATA 1
#define SZ_ERROR_MEM 2
#define SZ_ERROR_CRC 3
#define SZ_ERROR_UNSUPPORTED 4
#define SZ_ERROR_PARAM 5
#define SZ_ERROR_INPUT_EOF 6
#define SZ_ERROR_OUTPUT_EOF 7
#define SZ_ERROR_READ 8
#define SZ_ERROR_WRITE 9
#define SZ_ERROR_PROGRESS 10
#define SZ_ERROR_FAIL 11
#define SZ_ERROR_THREAD 12

#define SZ_ERROR_ARCHIVE 16
#define SZ_ERROR_NO_ARCHIVE 17

typedef int SRes;

#ifdef _WIN32
typedef DWORD WRes;
#else
typedef int WRes;
#endif

#ifndef RINOK
#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; }
#endif

typedef unsigned char Byte;
typedef short Int16;
typedef unsigned short UInt16;

#ifdef _LZMA_UINT32_IS_ULONG
typedef long Int32;
typedef unsigned long UInt32;
#else
typedef int Int32;
typedef unsigned int UInt32;
#endif

#ifdef _SZ_NO_INT_64

/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers.
NOTES: Some code will work incorrectly in that case! */

typedef long Int64;
typedef unsigned long UInt64;

#else

#if defined(_MSC_VER) || defined(__BORLANDC__)
typedef __int64 Int64;
typedef unsigned __int64 UInt64;
#define UINT64_CONST(n) n
#else
typedef long long int Int64;
typedef unsigned long long int UInt64;
#define UINT64_CONST(n) n ## ULL
#endif

#endif

#ifdef _LZMA_NO_SYSTEM_SIZE_T
typedef UInt32 SizeT;
#else
typedef size_t SizeT;
#endif

typedef int Bool;
#define True 1
#define False 0


#ifdef _WIN32
#define MY_STD_CALL __stdcall
#else
#define MY_STD_CALL
#endif

#ifdef _MSC_VER

#if _MSC_VER >= 1300
#define MY_NO_INLINE __declspec(noinline)
#else
#define MY_NO_INLINE
#endif

#define MY_CDECL __cdecl
#define MY_FAST_CALL __fastcall

#else

#define MY_CDECL
#define MY_FAST_CALL

#endif


/* The following interfaces use first parameter as pointer to structure */

typedef struct
{
    Byte (*Read)(void *p); /* reads one byte, returns 0 in case of EOF or error */
} IByteIn;

typedef struct
{
    void (*Write)(void *p, Byte b);
} IByteOut;

typedef struct
{
    SRes (*Read)(void *p, void *buf, size_t *size);
    /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream.
    (output(*size) < input(*size)) is allowed */
} ISeqInStream;

/* it can return SZ_ERROR_INPUT_EOF */
SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size);
SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType);
SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf);

typedef struct
{
    size_t (*Write)(void *p, const void *buf, size_t size);
    /* Returns: result - the number of actually written bytes.
    (result < size) means error */
} ISeqOutStream;

typedef enum
{
    SZ_SEEK_SET = 0,
        SZ_SEEK_CUR = 1,
        SZ_SEEK_END = 2
} ESzSeek;

typedef struct
{
    SRes (*Read)(void *p, void *buf, size_t *size);  /* same as ISeqInStream::Read */
    SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin);
} ISeekInStream;

typedef struct
{
    SRes (*Look)(void *p, const void **buf, size_t *size);
    /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream.
    (output(*size) > input(*size)) is not allowed
    (output(*size) < input(*size)) is allowed */
    SRes (*Skip)(void *p, size_t offset);
    /* offset must be <= output(*size) of Look */

    SRes (*Read)(void *p, void *buf, size_t *size);
    /* reads directly (without buffer). It's same as ISeqInStream::Read */
    SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin);
} ILookInStream;

SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size);
SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset);

/* reads via ILookInStream::Read */
SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType);
SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size);

#define LookToRead_BUF_SIZE (1 << 14)

typedef struct
{
    ILookInStream s;
    ISeekInStream *realStream;
    size_t pos;
    size_t size;
    Byte buf[LookToRead_BUF_SIZE];
} CLookToRead;

void LookToRead_CreateVTable(CLookToRead *p, int lookahead);
void LookToRead_Init(CLookToRead *p);

typedef struct
{
    ISeqInStream s;
    ILookInStream *realStream;
} CSecToLook;

void SecToLook_CreateVTable(CSecToLook *p);

typedef struct
{
    ISeqInStream s;
    ILookInStream *realStream;
} CSecToRead;

void SecToRead_CreateVTable(CSecToRead *p);

typedef struct
{
    SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize);
    /* Returns: result. (result != SZ_OK) means break.
    Value (UInt64)(Int64)-1 for size means unknown value. */
} ICompressProgress;

typedef struct
{
    void *(*Alloc)(void *p, size_t size);
    void (*Free)(void *p, void *address); /* address can be 0 */
} ISzAlloc;

#define IAlloc_Alloc(p, size) (p)->Alloc((p), size)
#define IAlloc_Free(p, a) (p)->Free((p), a)

#ifdef _WIN32

#define CHAR_PATH_SEPARATOR '\\'
#define WCHAR_PATH_SEPARATOR L'\\'
#define STRING_PATH_SEPARATOR "\\"
#define WSTRING_PATH_SEPARATOR L"\\"

#else

#define CHAR_PATH_SEPARATOR '/'
#define WCHAR_PATH_SEPARATOR L'/'
#define STRING_PATH_SEPARATOR "/"
#define WSTRING_PATH_SEPARATOR L"/"

#endif

extern UInt32 g_CrcTable[];

/* Call CrcGenerateTable one time before other CRC functions */
void MY_FAST_CALL CrcGenerateTable(void);

#define CRC_INIT_VAL 0xFFFFFFFF
#define CRC_GET_DIGEST(crc) ((crc) ^ CRC_INIT_VAL)
#define CRC_UPDATE_BYTE(crc, b) (g_CrcTable[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8))

UInt32 MY_FAST_CALL CrcUpdate(UInt32 crc, const void *data, size_t size);
UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size);
typedef struct
{
#ifdef USE_WINDOWS_FILE
    HANDLE handle;
#else
    FILE *file;
#endif
} CSzFile;

void File_Construct(CSzFile *p);
#if !defined(UNDER_CE) || !defined(USE_WINDOWS_FILE)
WRes InFile_Open(CSzFile *p, const char *name);
WRes OutFile_Open(CSzFile *p, const char *name);
#endif
#ifdef USE_WINDOWS_FILE
WRes InFile_OpenW(CSzFile *p, const WCHAR *name);
WRes OutFile_OpenW(CSzFile *p, const WCHAR *name);
#endif
WRes File_Close(CSzFile *p);

/* reads max(*size, remain file's size) bytes */
WRes File_Read(CSzFile *p, void *data, size_t *size);

/* writes *size bytes */
WRes File_Write(CSzFile *p, const void *data, size_t *size);

WRes File_Seek(CSzFile *p, Int64 *pos, ESzSeek origin);
WRes File_GetLength(CSzFile *p, UInt64 *length);


/* ---------- FileInStream ---------- */

typedef struct
{
    ISeqInStream s;
    CSzFile file;
} CFileSeqInStream;

void FileSeqInStream_CreateVTable(CFileSeqInStream *p);


typedef struct
{
    ISeekInStream s;
    CSzFile file;
} CFileInStream;

void FileInStream_CreateVTable(CFileInStream *p);


typedef struct
{
    ISeqOutStream s;
    CSzFile file;
} CFileOutStream;

void FileOutStream_CreateVTable(CFileOutStream *p);
#if defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__)
#define MY_CPU_AMD64
#endif

#if defined(MY_CPU_AMD64) || defined(_M_IA64)
#define MY_CPU_64BIT
#endif

#if defined(_M_IX86) || defined(__i386__)
#define MY_CPU_X86
#endif

#if defined(MY_CPU_X86) || defined(MY_CPU_AMD64)
#define MY_CPU_X86_OR_AMD64
#endif

#if defined(MY_CPU_X86) || defined(_M_ARM)
#define MY_CPU_32BIT
#endif

#if defined(_WIN32) && defined(_M_ARM)
#define MY_CPU_ARM_LE
#endif

#if defined(_WIN32) && defined(_M_IA64)
#define MY_CPU_IA64_LE
#endif

#if defined(MY_CPU_X86_OR_AMD64)
#define MY_CPU_LE_UNALIGN
#endif

#if defined(MY_CPU_X86_OR_AMD64) || defined(MY_CPU_ARM_LE)  || defined(MY_CPU_IA64_LE) || defined(__ARMEL__) || defined(__MIPSEL__) || defined(__LITTLE_ENDIAN__)
#define MY_CPU_LE
#endif

#if defined(__BIG_ENDIAN__)
#define MY_CPU_BE
#endif

#if defined(MY_CPU_LE) && defined(MY_CPU_BE)
Stop_Compiling_Bad_Endian
#endif

#ifdef MY_CPU_LE_UNALIGN

#define GetUi16(p) (*(const UInt16 *)(p))
#define GetUi32(p) (*(const UInt32 *)(p))
#define GetUi64(p) (*(const UInt64 *)(p))
#define SetUi16(p, d) *(UInt16 *)(p) = (d);
#define SetUi32(p, d) *(UInt32 *)(p) = (d);
#define SetUi64(p, d) *(UInt64 *)(p) = (d);

#else

#define GetUi16(p) (((const Byte *)(p))[0] | ((UInt16)((const Byte *)(p))[1] << 8))

#define GetUi32(p) ( \
    ((const Byte *)(p))[0]        | \
    ((UInt32)((const Byte *)(p))[1] <<  8) | \
    ((UInt32)((const Byte *)(p))[2] << 16) | \
((UInt32)((const Byte *)(p))[3] << 24))

#define GetUi64(p) (GetUi32(p) | ((UInt64)GetUi32(((const Byte *)(p)) + 4) << 32))

#define SetUi16(p, d) { UInt32 _x_ = (d); \
    ((Byte *)(p))[0] = (Byte)_x_; \
((Byte *)(p))[1] = (Byte)(_x_ >> 8); }

#define SetUi32(p, d) { UInt32 _x_ = (d); \
    ((Byte *)(p))[0] = (Byte)_x_; \
    ((Byte *)(p))[1] = (Byte)(_x_ >> 8); \
    ((Byte *)(p))[2] = (Byte)(_x_ >> 16); \
((Byte *)(p))[3] = (Byte)(_x_ >> 24); }

#define SetUi64(p, d) { UInt64 _x64_ = (d); \
    SetUi32(p, (UInt32)_x64_); \
SetUi32(((Byte *)(p)) + 4, (UInt32)(_x64_ >> 32)); }

#endif

#if defined(MY_CPU_LE_UNALIGN) && defined(_WIN64) && (_MSC_VER >= 1300)

#pragma intrinsic(_byteswap_ulong)
#pragma intrinsic(_byteswap_uint64)
#define GetBe32(p) _byteswap_ulong(*(const UInt32 *)(const Byte *)(p))
#define GetBe64(p) _byteswap_uint64(*(const UInt64 *)(const Byte *)(p))

#else

#define GetBe32(p) ( \
    ((UInt32)((const Byte *)(p))[0] << 24) | \
    ((UInt32)((const Byte *)(p))[1] << 16) | \
    ((UInt32)((const Byte *)(p))[2] <<  8) | \
((const Byte *)(p))[3] )

#define GetBe64(p) (((UInt64)GetBe32(p) << 32) | GetBe32(((const Byte *)(p)) + 4))

#endif

#define GetBe16(p) (((UInt16)((const Byte *)(p))[0] << 8) | ((const Byte *)(p))[1])


#ifdef MY_CPU_X86_OR_AMD64

typedef struct
{
    UInt32 maxFunc;
    UInt32 vendor[3];
    UInt32 ver;
    UInt32 b;
    UInt32 c;
    UInt32 d;
} Cx86cpuid;

enum
{
    CPU_FIRM_INTEL,
        CPU_FIRM_AMD,
        CPU_FIRM_VIA
};

Bool x86cpuid_CheckAndRead(Cx86cpuid *p);
int x86cpuid_GetFirm(const Cx86cpuid *p);

#define x86cpuid_GetFamily(p) (((p)->ver >> 8) & 0xFF00F)
#define x86cpuid_GetModel(p) (((p)->ver >> 4) & 0xF00F)
#define x86cpuid_GetStepping(p) ((p)->ver & 0xF)

Bool CPU_Is_InOrder();
Bool CPU_Is_Aes_Supported();

#endif
#ifdef MY_CPU_32BIT
#define PPMD_32BIT
#endif

#define PPMD_INT_BITS 7
#define PPMD_PERIOD_BITS 7
#define PPMD_BIN_SCALE (1 << (PPMD_INT_BITS + PPMD_PERIOD_BITS))

#define PPMD_GET_MEAN_SPEC(summ, shift, round) (((summ) + (1 << ((shift) - (round)))) >> (shift))
#define PPMD_GET_MEAN(summ) PPMD_GET_MEAN_SPEC((summ), PPMD_PERIOD_BITS, 2)
#define PPMD_UPDATE_PROB_0(prob) ((prob) + (1 << PPMD_INT_BITS) - PPMD_GET_MEAN(prob))
#define PPMD_UPDATE_PROB_1(prob) ((prob) - PPMD_GET_MEAN(prob))

#define PPMD_N1 4
#define PPMD_N2 4
#define PPMD_N3 4
#define PPMD_N4 ((128 + 3 - 1 * PPMD_N1 - 2 * PPMD_N2 - 3 * PPMD_N3) / 4)
#define PPMD_NUM_INDEXES (PPMD_N1 + PPMD_N2 + PPMD_N3 + PPMD_N4)

/* SEE-contexts for PPM-contexts with masked symbols */
typedef struct
{
    UInt16 Summ; /* Freq */
    Byte Shift;  /* Speed of Freq change; low Shift is for fast change */
    Byte Count;  /* Count to next change of Shift */
} CPpmd_See;

#define Ppmd_See_Update(p)  if ((p)->Shift < PPMD_PERIOD_BITS && --(p)->Count == 0) \
{ (p)->Summ <<= 1; (p)->Count = (Byte)(3 << (p)->Shift++); }

typedef struct
{
    Byte Symbol;
    Byte Freq;
    UInt16 SuccessorLow;
    UInt16 SuccessorHigh;
} CPpmd_State;

typedef
#ifdef PPMD_32BIT
CPpmd_State *
#else
UInt32
#endif
CPpmd_State_Ref;

typedef
#ifdef PPMD_32BIT
void *
#else
UInt32
#endif
CPpmd_Void_Ref;

typedef
#ifdef PPMD_32BIT
Byte *
#else
UInt32
#endif
CPpmd_Byte_Ref;

#define PPMD_SetAllBitsIn256Bytes(p) \
{ unsigned i; for (i = 0; i < 256 / sizeof(p[0]); i += 8) { \
p[i+7] = p[i+6] = p[i+5] = p[i+4] = p[i+3] = p[i+2] = p[i+1] = p[i+0] = ~(size_t)0; }}
#define PPMD7_MIN_ORDER 2
#define PPMD7_MAX_ORDER 64

#define PPMD7_MIN_MEM_SIZE (1 << 11)
#define PPMD7_MAX_MEM_SIZE (0xFFFFFFFF - 12 * 3)

struct CPpmd7_Context_;

typedef
#ifdef PPMD_32BIT
struct CPpmd7_Context_ *
#else
UInt32
#endif
CPpmd7_Context_Ref;

typedef struct CPpmd7_Context_
{
    UInt16 NumStats;
    UInt16 SummFreq;
    CPpmd_State_Ref Stats;
    CPpmd7_Context_Ref Suffix;
} CPpmd7_Context;

#define Ppmd7Context_OneState(p) ((CPpmd_State *)&(p)->SummFreq)

typedef struct
{
    CPpmd7_Context *MinContext, *MaxContext;
    CPpmd_State *FoundState;
    unsigned OrderFall, InitEsc, PrevSuccess, MaxOrder, HiBitsFlag;
    Int32 RunLength, InitRL; /* must be 32-bit at least */

    UInt32 Size;
    UInt32 GlueCount;
    Byte *Base, *LoUnit, *HiUnit, *Text, *UnitsStart;
    UInt32 AlignOffset;

    Byte Indx2Units[PPMD_NUM_INDEXES];
    Byte Units2Indx[128];
    CPpmd_Void_Ref FreeList[PPMD_NUM_INDEXES];
    Byte NS2Indx[256], NS2BSIndx[256], HB2Flag[256];
    CPpmd_See DummySee, See[25][16];
    UInt16 BinSumm[128][64];
} CPpmd7;

void Ppmd7_Construct(CPpmd7 *p);
Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size, ISzAlloc *alloc);
void Ppmd7_Free(CPpmd7 *p, ISzAlloc *alloc);
void Ppmd7_Init(CPpmd7 *p, unsigned maxOrder);
#define Ppmd7_WasAllocated(p) ((p)->Base != NULL)


/* ---------- Internal Functions ---------- */

extern const Byte PPMD7_kExpEscape[16];

#ifdef PPMD_32BIT
#define Ppmd7_GetPtr(p, ptr) (ptr)
#define Ppmd7_GetContext(p, ptr) (ptr)
#define Ppmd7_GetStats(p, ctx) ((ctx)->Stats)
#else
#define Ppmd7_GetPtr(p, offs) ((void *)((p)->Base + (offs)))
#define Ppmd7_GetContext(p, offs) ((CPpmd7_Context *)Ppmd7_GetPtr((p), (offs)))
#define Ppmd7_GetStats(p, ctx) ((CPpmd_State *)Ppmd7_GetPtr((p), ((ctx)->Stats)))
#endif

void Ppmd7_Update1(CPpmd7 *p);
void Ppmd7_Update1_0(CPpmd7 *p);
void Ppmd7_Update2(CPpmd7 *p);
void Ppmd7_UpdateBin(CPpmd7 *p);

#define Ppmd7_GetBinSumm(p) \
    &p->BinSumm[Ppmd7Context_OneState(p->MinContext)->Freq - 1][p->PrevSuccess + \
    p->NS2BSIndx[Ppmd7_GetContext(p, p->MinContext->Suffix)->NumStats - 1] + \
    (p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol]) + \
    2 * p->HB2Flag[Ppmd7Context_OneState(p->MinContext)->Symbol] + \
((p->RunLength >> 26) & 0x20)]

CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *scale);


/* ---------- Decode ---------- */

typedef struct
{
    UInt32 (*GetThreshold)(void *p, UInt32 total);
    void (*Decode)(void *p, UInt32 start, UInt32 size);
    UInt32 (*DecodeBit)(void *p, UInt32 size0);
} IPpmd7_RangeDec;

typedef struct
{
    IPpmd7_RangeDec p;
    UInt32 Range;
    UInt32 Code;
    IByteIn *Stream;
} CPpmd7z_RangeDec;

void Ppmd7z_RangeDec_CreateVTable(CPpmd7z_RangeDec *p);
Bool Ppmd7z_RangeDec_Init(CPpmd7z_RangeDec *p);
#define Ppmd7z_RangeDec_IsFinishedOK(p) ((p)->Code == 0)

int Ppmd7_DecodeSymbol(CPpmd7 *p, IPpmd7_RangeDec *rc);


/* ---------- Encode ---------- */

typedef struct
{
    UInt64 Low;
    UInt32 Range;
    Byte Cache;
    UInt64 CacheSize;
    IByteOut *Stream;
} CPpmd7z_RangeEnc;

void Ppmd7z_RangeEnc_Init(CPpmd7z_RangeEnc *p);
void Ppmd7z_RangeEnc_FlushData(CPpmd7z_RangeEnc *p);

void Ppmd7_EncodeSymbol(CPpmd7 *p, CPpmd7z_RangeEnc *rc, int symbol);

EXTERN_C_END

#ifdef __cplusplus
extern "C" {
#endif

    typedef struct
    {
        Byte *data;
        size_t size;
    } CBuf;

    void Buf_Init(CBuf *p);
    int Buf_Create(CBuf *p, size_t size, ISzAlloc *alloc);
    void Buf_Free(CBuf *p, ISzAlloc *alloc);

    typedef struct
    {
        Byte *data;
        size_t size;
        size_t pos;
    } CDynBuf;

    void DynBuf_Construct(CDynBuf *p);
    void DynBuf_SeekToBeg(CDynBuf *p);
    int DynBuf_Write(CDynBuf *p, const Byte *buf, size_t size, ISzAlloc *alloc);
    void DynBuf_Free(CDynBuf *p, ISzAlloc *alloc);

#ifdef __cplusplus
}
#endif
#include <stdlib.h>

void *SzAlloc(void *p, size_t size);
void SzFree(void *p, void *address);

void *SzAllocTemp(void *p, size_t size);
void SzFreeTemp(void *p, void *address);
#ifdef _SZ_ALLOC_DEBUG

#ifdef _WIN32
#include <windows.h>
#endif


int g_allocCount = 0;
int g_allocCountTemp = 0;

#endif

void *SzAlloc(void *p, size_t size)
{
    p = p;
    if (size == 0)
        return 0;
#ifdef _SZ_ALLOC_DEBUG
    fprintf(stderr, "\nAlloc %10d bytes; count = %10d", size, g_allocCount);
    g_allocCount++;
#endif
    return malloc(size);
}

void SzFree(void *p, void *address)
{
    p = p;
#ifdef _SZ_ALLOC_DEBUG
    if (address != 0)
    {
        g_allocCount--;
        fprintf(stderr, "\nFree; count = %10d", g_allocCount);
    }
#endif
    free(address);
}

void *SzAllocTemp(void *p, size_t size)
{
    p = p;
    if (size == 0)
        return 0;
#ifdef _SZ_ALLOC_DEBUG
    fprintf(stderr, "\nAlloc_temp %10d bytes;  count = %10d", size, g_allocCountTemp);
    g_allocCountTemp++;
#ifdef _WIN32
    return HeapAlloc(GetProcessHeap(), 0, size);
#endif
#endif
    return malloc(size);
}

void SzFreeTemp(void *p, void *address)
{
    p = p;
#ifdef _SZ_ALLOC_DEBUG
    if (address != 0)
    {
        g_allocCountTemp--;
        fprintf(stderr, "\nFree_temp; count = %10d", g_allocCountTemp);
    }
#ifdef _WIN32
    HeapFree(GetProcessHeap(), 0, address);
    return;
#endif
#endif
    free(address);
}
void Buf_Init(CBuf *p)
{
    p->data = 0;
    p->size = 0;
}

int Buf_Create(CBuf *p, size_t size, ISzAlloc *alloc)
{
    p->size = 0;
    if (size == 0)
    {
        p->data = 0;
        return 1;
    }
    p->data = (Byte *)alloc->Alloc(alloc, size);
    if (p->data != 0)
    {
        p->size = size;
        return 1;
    }
    return 0;
}

void Buf_Free(CBuf *p, ISzAlloc *alloc)
{
    alloc->Free(alloc, p->data);
    p->data = 0;
    p->size = 0;
}
#ifndef USE_WINDOWS_FILE

#ifndef UNDER_CE
#include <errno.h>
#endif

#else

/*
ReadFile and WriteFile functions in Windows have BUG:
If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1)
from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES
(Insufficient system resources exist to complete the requested service).
Probably in some version of Windows there are problems with other sizes:
for 32 MB (maybe also for 16 MB).
And message can be "Network connection was lost"
*/

#define kChunkSizeMax (1 << 22)

#endif

void File_Construct(CSzFile *p)
{
#ifdef USE_WINDOWS_FILE
    p->handle = INVALID_HANDLE_VALUE;
#else
    p->file = NULL;
#endif
}

#if !defined(UNDER_CE) || !defined(USE_WINDOWS_FILE)
static WRes File_Open(CSzFile *p, const char *name, int writeMode)
{
#ifdef USE_WINDOWS_FILE
    p->handle = CreateFileA(name,
        writeMode ? GENERIC_WRITE : GENERIC_READ,
        FILE_SHARE_READ, NULL,
        writeMode ? CREATE_ALWAYS : OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL, NULL);
    return (p->handle != INVALID_HANDLE_VALUE) ? 0 : GetLastError();
#else
    p->file = fopen(name, writeMode ? "wb+" : "rb");
    return (p->file != 0) ? 0 :
#ifdef UNDER_CE
    2; /* ENOENT */
#else
    errno;
#endif
#endif
}

WRes InFile_Open(CSzFile *p, const char *name) { return File_Open(p, name, 0); }
WRes OutFile_Open(CSzFile *p, const char *name) { return File_Open(p, name, 1); }
#endif

#ifdef USE_WINDOWS_FILE
static WRes File_OpenW(CSzFile *p, const WCHAR *name, int writeMode)
{
    p->handle = CreateFileW(name,
        writeMode ? GENERIC_WRITE : GENERIC_READ,
        FILE_SHARE_READ, NULL,
        writeMode ? CREATE_ALWAYS : OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL, NULL);
    return (p->handle != INVALID_HANDLE_VALUE) ? 0 : GetLastError();
}
WRes InFile_OpenW(CSzFile *p, const WCHAR *name) { return File_OpenW(p, name, 0); }
WRes OutFile_OpenW(CSzFile *p, const WCHAR *name) { return File_OpenW(p, name, 1); }
#endif

WRes File_Close(CSzFile *p)
{
#ifdef USE_WINDOWS_FILE
    if (p->handle != INVALID_HANDLE_VALUE)
    {
        if (!CloseHandle(p->handle))
            return GetLastError();
        p->handle = INVALID_HANDLE_VALUE;
    }
#else
    if (p->file != NULL)
    {
        int res = fclose(p->file);
        if (res != 0)
            return res;
        p->file = NULL;
    }
#endif
    return 0;
}

WRes File_Read(CSzFile *p, void *data, size_t *size)
{
    size_t originalSize = *size;
    if (originalSize == 0)
        return 0;

#ifdef USE_WINDOWS_FILE

    *size = 0;
    do
    {
        DWORD curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : (DWORD)originalSize;
        DWORD processed = 0;
        BOOL res = ReadFile(p->handle, data, curSize, &processed, NULL);
        data = (void *)((Byte *)data + processed);
        originalSize -= processed;
        *size += processed;
        if (!res)
            return GetLastError();
        if (processed == 0)
            break;
    }
    while (originalSize > 0);
    return 0;

#else

    *size = fread(data, 1, originalSize, p->file);
    if (*size == originalSize)
        return 0;
    return ferror(p->file);

#endif
}

WRes File_Write(CSzFile *p, const void *data, size_t *size)
{
    size_t originalSize = *size;
    if (originalSize == 0)
        return 0;

#ifdef USE_WINDOWS_FILE

    *size = 0;
    do
    {
        DWORD curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : (DWORD)originalSize;
        DWORD processed = 0;
        BOOL res = WriteFile(p->handle, data, curSize, &processed, NULL);
        data = (void *)((Byte *)data + processed);
        originalSize -= processed;
        *size += processed;
        if (!res)
            return GetLastError();
        if (processed == 0)
            break;
    }
    while (originalSize > 0);
    return 0;

#else

    *size = fwrite(data, 1, originalSize, p->file);
    if (*size == originalSize)
        return 0;
    return ferror(p->file);

#endif
}

WRes File_Seek(CSzFile *p, Int64 *pos, ESzSeek origin)
{
#ifdef USE_WINDOWS_FILE

    LARGE_INTEGER value;
    DWORD moveMethod;
    value.LowPart = (DWORD)*pos;
    value.HighPart = (LONG)((UInt64)*pos >> 16 >> 16); /* for case when UInt64 is 32-bit only */
    switch (origin)
    {
    case SZ_SEEK_SET: moveMethod = FILE_BEGIN; break;
    case SZ_SEEK_CUR: moveMethod = FILE_CURRENT; break;
    case SZ_SEEK_END: moveMethod = FILE_END; break;
    default: return ERROR_INVALID_PARAMETER;
    }
    value.LowPart = SetFilePointer(p->handle, value.LowPart, &value.HighPart, moveMethod);
    if (value.LowPart == 0xFFFFFFFF)
    {
        WRes res = GetLastError();
        if (res != NO_ERROR)
            return res;
    }
    *pos = ((Int64)value.HighPart << 32) | value.LowPart;
    return 0;

#else

    int moveMethod;
    int res;
    switch (origin)
    {
    case SZ_SEEK_SET: moveMethod = SEEK_SET; break;
    case SZ_SEEK_CUR: moveMethod = SEEK_CUR; break;
    case SZ_SEEK_END: moveMethod = SEEK_END; break;
    default: return 1;
    }
    res = fseek(p->file, (long)*pos, moveMethod);
    *pos = ftell(p->file);
    return res;

#endif
}

WRes File_GetLength(CSzFile *p, UInt64 *length)
{
#ifdef USE_WINDOWS_FILE

    DWORD sizeHigh;
    DWORD sizeLow = GetFileSize(p->handle, &sizeHigh);
    if (sizeLow == 0xFFFFFFFF)
    {
        DWORD res = GetLastError();
        if (res != NO_ERROR)
            return res;
    }
    *length = (((UInt64)sizeHigh) << 32) + sizeLow;
    return 0;

#else

    long pos = ftell(p->file);
    int res = fseek(p->file, 0, SEEK_END);
    *length = ftell(p->file);
    fseek(p->file, pos, SEEK_SET);
    return res;

#endif
}


/* ---------- FileSeqInStream ---------- */

static SRes FileSeqInStream_Read(void *pp, void *buf, size_t *size)
{
    CFileSeqInStream *p = (CFileSeqInStream *)pp;
    return File_Read(&p->file, buf, size) == 0 ? SZ_OK : SZ_ERROR_READ;
}

void FileSeqInStream_CreateVTable(CFileSeqInStream *p)
{
    p->s.Read = FileSeqInStream_Read;
}


/* ---------- FileInStream ---------- */

static SRes FileInStream_Read(void *pp, void *buf, size_t *size)
{
    CFileInStream *p = (CFileInStream *)pp;
    return (File_Read(&p->file, buf, size) == 0) ? SZ_OK : SZ_ERROR_READ;
}

static SRes FileInStream_Seek(void *pp, Int64 *pos, ESzSeek origin)
{
    CFileInStream *p = (CFileInStream *)pp;
    return File_Seek(&p->file, pos, origin);
}

void FileInStream_CreateVTable(CFileInStream *p)
{
    p->s.Read = FileInStream_Read;
    p->s.Seek = FileInStream_Seek;
}


/* ---------- FileOutStream ---------- */

static size_t FileOutStream_Write(void *pp, const void *data, size_t size)
{
    CFileOutStream *p = (CFileOutStream *)pp;
    File_Write(&p->file, data, &size);
    return size;
}

void FileOutStream_CreateVTable(CFileOutStream *p)
{
    p->s.Write = FileOutStream_Write;
}
SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType)
{
    while (size != 0)
    {
        size_t processed = size;
        RINOK(stream->Read(stream, buf, &processed));
        if (processed == 0)
            return errorType;
        buf = (void *)((Byte *)buf + processed);
        size -= processed;
    }
    return SZ_OK;
}

SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size)
{
    return SeqInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF);
}

SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf)
{
    size_t processed = 1;
    RINOK(stream->Read(stream, buf, &processed));
    return (processed == 1) ? SZ_OK : SZ_ERROR_INPUT_EOF;
}

SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset)
{
    Int64 t = offset;
    return stream->Seek(stream, &t, SZ_SEEK_SET);
}

SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size)
{
    const void *lookBuf;
    if (*size == 0)
        return SZ_OK;
    RINOK(stream->Look(stream, &lookBuf, size));
    memcpy(buf, lookBuf, *size);
    return stream->Skip(stream, *size);
}

SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType)
{
    while (size != 0)
    {
        size_t processed = size;
        RINOK(stream->Read(stream, buf, &processed));
        if (processed == 0)
            return errorType;
        buf = (void *)((Byte *)buf + processed);
        size -= processed;
    }
    return SZ_OK;
}

SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size)
{
    return LookInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF);
}

static SRes LookToRead_Look_Lookahead(void *pp, const void **buf, size_t *size)
{
    SRes res = SZ_OK;
    CLookToRead *p = (CLookToRead *)pp;
    size_t size2 = p->size - p->pos;
    if (size2 == 0 && *size > 0)
    {
        p->pos = 0;
        size2 = LookToRead_BUF_SIZE;
        res = p->realStream->Read(p->realStream, p->buf, &size2);
        p->size = size2;
    }
    if (size2 < *size)
        *size = size2;
    *buf = p->buf + p->pos;
    return res;
}

static SRes LookToRead_Look_Exact(void *pp, const void **buf, size_t *size)
{
    SRes res = SZ_OK;
    CLookToRead *p = (CLookToRead *)pp;
    size_t size2 = p->size - p->pos;
    if (size2 == 0 && *size > 0)
    {
        p->pos = 0;
        if (*size > LookToRead_BUF_SIZE)
            *size = LookToRead_BUF_SIZE;
        res = p->realStream->Read(p->realStream, p->buf, size);
        size2 = p->size = *size;
    }
    if (size2 < *size)
        *size = size2;
    *buf = p->buf + p->pos;
    return res;
}

static SRes LookToRead_Skip(void *pp, size_t offset)
{
    CLookToRead *p = (CLookToRead *)pp;
    p->pos += offset;
    return SZ_OK;
}

static SRes LookToRead_Read(void *pp, void *buf, size_t *size)
{
    CLookToRead *p = (CLookToRead *)pp;
    size_t rem = p->size - p->pos;
    if (rem == 0)
        return p->realStream->Read(p->realStream, buf, size);
    if (rem > *size)
        rem = *size;
    memcpy(buf, p->buf + p->pos, rem);
    p->pos += rem;
    *size = rem;
    return SZ_OK;
}

static SRes LookToRead_Seek(void *pp, Int64 *pos, ESzSeek origin)
{
    CLookToRead *p = (CLookToRead *)pp;
    p->pos = p->size = 0;
    return p->realStream->Seek(p->realStream, pos, origin);
}

void LookToRead_CreateVTable(CLookToRead *p, int lookahead)
{
    p->s.Look = lookahead ?
LookToRead_Look_Lookahead :
    LookToRead_Look_Exact;
    p->s.Skip = LookToRead_Skip;
    p->s.Read = LookToRead_Read;
    p->s.Seek = LookToRead_Seek;
}

void LookToRead_Init(CLookToRead *p)
{
    p->pos = p->size = 0;
}

static SRes SecToLook_Read(void *pp, void *buf, size_t *size)
{
    CSecToLook *p = (CSecToLook *)pp;
    return LookInStream_LookRead(p->realStream, buf, size);
}

void SecToLook_CreateVTable(CSecToLook *p)
{
    p->s.Read = SecToLook_Read;
}

static SRes SecToRead_Read(void *pp, void *buf, size_t *size)
{
    CSecToRead *p = (CSecToRead *)pp;
    return p->realStream->Read(p->realStream, buf, size);
}

void SecToRead_CreateVTable(CSecToRead *p)
{
    p->s.Read = SecToRead_Read;
}
#ifdef __cplusplus
extern "C" {
#endif

/*
Conditions:
outSize <= FullOutputSize,
where FullOutputSize is full size of output stream of x86_2 filter.

  If buf0 overlaps outBuf, there are two required conditions:
  1) (buf0 >= outBuf)
  2) (buf0 + size0 >= outBuf + FullOutputSize).

    Returns:
    SZ_OK
    SZ_ERROR_DATA - Data error
    */

    int Bcj2_Decode(
        const Byte *buf0, SizeT size0,
        const Byte *buf1, SizeT size1,
        const Byte *buf2, SizeT size2,
        const Byte *buf3, SizeT size3,
        Byte *outBuf, SizeT outSize);

#ifdef __cplusplus
}
#endif
#ifdef _LZMA_PROB32
#define CProb UInt32
#else
#define CProb UInt16
#endif

#define IsJcc(b0, b1) ((b0) == 0x0F && ((b1) & 0xF0) == 0x80)
#define IsJ(b0, b1) ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1))

#define kNumTopBits 24
#define kTopValue ((UInt32)1 << kNumTopBits)

#define kNumBitModelTotalBits 11
#define kBitModelTotal (1 << kNumBitModelTotalBits)
#define kNumMoveBits 5

#define RC_READ_BYTE (*buffer++)
#define RC_TEST { if (buffer == bufferLim) return SZ_ERROR_DATA; }
#define RC_INIT2 code = 0; range = 0xFFFFFFFF; \
{ int i; for (i = 0; i < 5; i++) { RC_TEST; code = (code << 8) | RC_READ_BYTE; }}

#define NORMALIZE if (range < kTopValue) { RC_TEST; range <<= 8; code = (code << 8) | RC_READ_BYTE; }

#define IF_BIT_0(p) ttt = *(p); bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
#define UPDATE_0(p) range = bound; *(p) = (CProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); NORMALIZE;
#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CProb)(ttt - (ttt >> kNumMoveBits)); NORMALIZE;

int Bcj2_Decode(
                const Byte *buf0, SizeT size0,
                const Byte *buf1, SizeT size1,
                const Byte *buf2, SizeT size2,
                const Byte *buf3, SizeT size3,
                Byte *outBuf, SizeT outSize)
{
    CProb p[256 + 2];
    SizeT inPos = 0, outPos = 0;

    const Byte *buffer, *bufferLim;
    UInt32 range, code;
    Byte prevByte = 0;

    unsigned int i;
    for (i = 0; i < sizeof(p) / sizeof(p[0]); i++)
        p[i] = kBitModelTotal >> 1;

    buffer = buf3;
    bufferLim = buffer + size3;
    RC_INIT2

        if (outSize == 0)
            return SZ_OK;

        for (;;)
        {
            Byte b;
            CProb *prob;
            UInt32 bound;
            UInt32 ttt;

            SizeT limit = size0 - inPos;
            if (outSize - outPos < limit)
                limit = outSize - outPos;
            while (limit != 0)
            {
                Byte b = buf0[inPos];
                outBuf[outPos++] = b;
                if (IsJ(prevByte, b))
                    break;
                inPos++;
                prevByte = b;
                limit--;
            }

            if (limit == 0 || outPos == outSize)
                break;

            b = buf0[inPos++];

            if (b == 0xE8)
                prob = p + prevByte;
            else if (b == 0xE9)
                prob = p + 256;
            else
                prob = p + 257;

            IF_BIT_0(prob)
            {
                UPDATE_0(prob)
                    prevByte = b;
            }
            else
            {
                UInt32 dest;
                const Byte *v;
                UPDATE_1(prob)
                    if (b == 0xE8)
                    {
                        v = buf1;
                        if (size1 < 4)
                            return SZ_ERROR_DATA;
                        buf1 += 4;
                        size1 -= 4;
                    }
                    else
                    {
                        v = buf2;
                        if (size2 < 4)
                            return SZ_ERROR_DATA;
                        buf2 += 4;
                        size2 -= 4;
                    }
                    dest = (((UInt32)v[0] << 24) | ((UInt32)v[1] << 16) |
                        ((UInt32)v[2] << 8) | ((UInt32)v[3])) - ((UInt32)outPos + 4);
                    outBuf[outPos++] = (Byte)dest;
                    if (outPos == outSize)
                        break;
                    outBuf[outPos++] = (Byte)(dest >> 8);
                    if (outPos == outSize)
                        break;
                    outBuf[outPos++] = (Byte)(dest >> 16);
                    if (outPos == outSize)
                        break;
                    outBuf[outPos++] = prevByte = (Byte)(dest >> 24);
            }
        }
        return (outPos == outSize) ? SZ_OK : SZ_ERROR_DATA;
}
#ifdef __cplusplus
                extern "C" {
#endif

                /*
                These functions convert relative addresses to absolute addresses
                in CALL instructions to increase the compression ratio.

                  In:
                  data     - data buffer
                  size     - size of data
                  ip       - current virtual Instruction Pinter (IP) value
                  state    - state variable for x86 converter
                  encoding - 0 (for decoding), 1 (for encoding)

                    Out:
                    state    - state variable for x86 converter

                      Returns:
                      The number of processed bytes. If you call these functions with multiple calls,
                      you must start next call with first byte after block of processed bytes.

                        Type   Endian  Alignment  LookAhead

                          x86    little      1          4
                          ARMT   little      2          2
                          ARM    little      4          0
                          PPC     big        4          0
                          SPARC   big        4          0
                          IA64   little     16          0

                            size must be >= Alignment + LookAhead, if it's not last block.
                            If (size < Alignment + LookAhead), converter returns 0.

                              Example:

                                UInt32 ip = 0;
                                for ()
                                {
                                ; size must be >= Alignment + LookAhead, if it's not last block
                                SizeT processed = Convert(data, size, ip, 1);
                                data += processed;
                                size -= processed;
                                ip += processed;
                                }
                    */

#define x86_Convert_Init(state) { state = 0; }
                    SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding);
                    SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding);
                    SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding);
                    SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding);
                    SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding);
                    SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding);

#ifdef __cplusplus
                }
#endif
                SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding)
                {
                    SizeT i;
                    if (size < 4)
                        return 0;
                    size -= 4;
                    ip += 8;
                    for (i = 0; i <= size; i += 4)
                    {
                        if (data[i + 3] == 0xEB)
                        {
                            UInt32 dest;
                            UInt32 src = ((UInt32)data[i + 2] << 16) | ((UInt32)data[i + 1] << 8) | (data[i + 0]);
                            src <<= 2;
                            if (encoding)
                                dest = ip + (UInt32)i + src;
                            else
                                dest = src - (ip + (UInt32)i);
                            dest >>= 2;
                            data[i + 2] = (Byte)(dest >> 16);
                            data[i + 1] = (Byte)(dest >> 8);
                            data[i + 0] = (Byte)dest;
                        }
                    }
                    return i;
                }

                SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding)
                {
                    SizeT i;
                    if (size < 4)
                        return 0;
                    size -= 4;
                    ip += 4;
                    for (i = 0; i <= size; i += 2)
                    {
                        if ((data[i + 1] & 0xF8) == 0xF0 &&
                            (data[i + 3] & 0xF8) == 0xF8)
                        {
                            UInt32 dest;
                            UInt32 src =
                                (((UInt32)data[i + 1] & 0x7) << 19) |
                                ((UInt32)data[i + 0] << 11) |
                                (((UInt32)data[i + 3] & 0x7) << 8) |
                                (data[i + 2]);

                            src <<= 1;
                            if (encoding)
                                dest = ip + (UInt32)i + src;
                            else
                                dest = src - (ip + (UInt32)i);
                            dest >>= 1;

                            data[i + 1] = (Byte)(0xF0 | ((dest >> 19) & 0x7));
                            data[i + 0] = (Byte)(dest >> 11);
                            data[i + 3] = (Byte)(0xF8 | ((dest >> 8) & 0x7));
                            data[i + 2] = (Byte)dest;
                            i += 2;
                        }
                    }
                    return i;
                }

                SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding)
                {
                    SizeT i;
                    if (size < 4)
                        return 0;
                    size -= 4;
                    for (i = 0; i <= size; i += 4)
                    {
                        if ((data[i] >> 2) == 0x12 && (data[i + 3] & 3) == 1)
                        {
                            UInt32 src = ((UInt32)(data[i + 0] & 3) << 24) |
                                ((UInt32)data[i + 1] << 16) |
                                ((UInt32)data[i + 2] << 8) |
                                ((UInt32)data[i + 3] & (~3));

                            UInt32 dest;
                            if (encoding)
                                dest = ip + (UInt32)i + src;
                            else
                                dest = src - (ip + (UInt32)i);
                            data[i + 0] = (Byte)(0x48 | ((dest >> 24) &  0x3));
                            data[i + 1] = (Byte)(dest >> 16);
                            data[i + 2] = (Byte)(dest >> 8);
                            data[i + 3] &= 0x3;
                            data[i + 3] |= dest;
                        }
                    }
                    return i;
                }

                SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding)
                {
                    UInt32 i;
                    if (size < 4)
                        return 0;
                    size -= 4;
                    for (i = 0; i <= size; i += 4)
                    {
                        if ((data[i] == 0x40 && (data[i + 1] & 0xC0) == 0x00) ||
                            (data[i] == 0x7F && (data[i + 1] & 0xC0) == 0xC0))
                        {
                            UInt32 src =
                                ((UInt32)data[i + 0] << 24) |
                                ((UInt32)data[i + 1] << 16) |
                                ((UInt32)data[i + 2] << 8) |
                                ((UInt32)data[i + 3]);
                            UInt32 dest;

                            src <<= 2;
                            if (encoding)
                                dest = ip + i + src;
                            else
                                dest = src - (ip + i);
                            dest >>= 2;

                            dest = (((0 - ((dest >> 22) & 1)) << 22) & 0x3FFFFFFF) | (dest & 0x3FFFFF) | 0x40000000;

                            data[i + 0] = (Byte)(dest >> 24);
                            data[i + 1] = (Byte)(dest >> 16);
                            data[i + 2] = (Byte)(dest >> 8);
                            data[i + 3] = (Byte)dest;
                        }
                    }
                    return i;
                }
#define Test86MSByte(b) ((b) == 0 || (b) == 0xFF)

                const Byte kMaskToAllowedStatus[8] = {1, 1, 1, 0, 1, 0, 0, 0};
                const Byte kMaskToBitNumber[8] = {0, 1, 2, 2, 3, 3, 3, 3};

                SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding)
                {
                    SizeT bufferPos = 0, prevPosT;
                    UInt32 prevMask = *state & 0x7;
                    if (size < 5)
                        return 0;
                    ip += 5;
                    prevPosT = (SizeT)0 - 1;

                    for (;;)
                    {
                        Byte *p = data + bufferPos;
                        Byte *limit = data + size - 4;
                        for (; p < limit; p++)
                            if ((*p & 0xFE) == 0xE8)
                                break;
                            bufferPos = (SizeT)(p - data);
                            if (p >= limit)
                                break;
                            prevPosT = bufferPos - prevPosT;
                            if (prevPosT > 3)
                                prevMask = 0;
                            else
                            {
                                prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7;
                                if (prevMask != 0)
                                {
                                    Byte b = p[4 - kMaskToBitNumber[prevMask]];
                                    if (!kMaskToAllowedStatus[prevMask] || Test86MSByte(b))
                                    {
                                        prevPosT = bufferPos;
                                        prevMask = ((prevMask << 1) & 0x7) | 1;
                                        bufferPos++;
                                        continue;
                                    }
                                }
                            }
                            prevPosT = bufferPos;

                            if (Test86MSByte(p[4]))
                            {
                                UInt32 src = ((UInt32)p[4] << 24) | ((UInt32)p[3] << 16) | ((UInt32)p[2] << 8) | ((UInt32)p[1]);
                                UInt32 dest;
                                for (;;)
                                {
                                    Byte b;
                                    int index;
                                    if (encoding)
                                        dest = (ip + (UInt32)bufferPos) + src;
                                    else
                                        dest = src - (ip + (UInt32)bufferPos);
                                    if (prevMask == 0)
                                        break;
                                    index = kMaskToBitNumber[prevMask] * 8;
                                    b = (Byte)(dest >> (24 - index));
                                    if (!Test86MSByte(b))
                                        break;
                                    src = dest ^ ((1 << (32 - index)) - 1);
                                }
                                p[4] = (Byte)(~(((dest >> 24) & 1) - 1));
                                p[3] = (Byte)(dest >> 16);
                                p[2] = (Byte)(dest >> 8);
                                p[1] = (Byte)dest;
                                bufferPos += 5;
                            }
                            else
                            {
                                prevMask = ((prevMask << 1) & 0x7) | 1;
                                bufferPos++;
                            }
                    }
                    prevPosT = bufferPos - prevPosT;
                    *state = ((prevPosT > 3) ? 0 : ((prevMask << ((int)prevPosT - 1)) & 0x7));
                    return bufferPos;
                }
#ifdef MY_CPU_X86_OR_AMD64

#if (defined(_MSC_VER) && !defined(MY_CPU_AMD64)) || defined(__GNUC__)
#define USE_ASM
#endif

#if defined(USE_ASM) && !defined(MY_CPU_AMD64)
                static UInt32 CheckFlag(UInt32 flag)
                {
#ifdef _MSC_VER
                    __asm pushfd;
                    __asm pop EAX;
                    __asm mov EDX, EAX;
                    __asm xor EAX, flag;
                    __asm push EAX;
                    __asm popfd;
                    __asm pushfd;
                    __asm pop EAX;
                    __asm xor EAX, EDX;
                    __asm push EDX;
                    __asm popfd;
                    __asm and flag, EAX;
#else
                    __asm__ __volatile__ (
                        "pushf\n\t"
                        "pop  %%EAX\n\t"
                        "movl %%EAX,%%EDX\n\t"
                        "xorl %0,%%EAX\n\t"
                        "push %%EAX\n\t"
                        "popf\n\t"
                        "pushf\n\t"
                        "pop  %%EAX\n\t"
                        "xorl %%EDX,%%EAX\n\t"
                        "push %%EDX\n\t"
                        "popf\n\t"
                        "andl %%EAX, %0\n\t":
                    "=c" (flag) : "c" (flag));
#endif
                    return flag;
                }
#define CHECK_CPUID_IS_SUPPORTED if (CheckFlag(1 << 18) == 0 || CheckFlag(1 << 21) == 0) return False;
#else
#define CHECK_CPUID_IS_SUPPORTED
#endif

                static void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d)
                {
#ifdef USE_ASM

#ifdef _MSC_VER

                    UInt32 a2, b2, c2, d2;
                    __asm xor EBX, EBX;
                    __asm xor ECX, ECX;
                    __asm xor EDX, EDX;
                    __asm mov EAX, function;
                    __asm cpuid;
                    __asm mov a2, EAX;
                    __asm mov b2, EBX;
                    __asm mov c2, ECX;
                    __asm mov d2, EDX;

                    *a = a2;
                    *b = b2;
                    *c = c2;
                    *d = d2;

#else

                    __asm__ __volatile__ (
                        "cpuid"
                        : "=a" (*a) ,
                        "=b" (*b) ,
                        "=c" (*c) ,
                        "=d" (*d)
                        : "0" (function)) ;

#endif

#else

                    int CPUInfo[4];
                    __cpuid(CPUInfo, function);
                    *a = CPUInfo[0];
                    *b = CPUInfo[1];
                    *c = CPUInfo[2];
                    *d = CPUInfo[3];

#endif
                }

                Bool x86cpuid_CheckAndRead(Cx86cpuid *p)
                {
                    CHECK_CPUID_IS_SUPPORTED
                        MyCPUID(0, &p->maxFunc, &p->vendor[0], &p->vendor[2], &p->vendor[1]);
                    MyCPUID(1, &p->ver, &p->b, &p->c, &p->d);
                    return True;
                }

                static UInt32 kVendors[][3] =
                {
                    { 0x756E6547, 0x49656E69, 0x6C65746E},
                    { 0x68747541, 0x69746E65, 0x444D4163},
                    { 0x746E6543, 0x48727561, 0x736C7561}
                };

                int x86cpuid_GetFirm(const Cx86cpuid *p)
                {
                    unsigned i;
                    for (i = 0; i < sizeof(kVendors) / sizeof(kVendors[i]); i++)
                    {
                        const UInt32 *v = kVendors[i];
                        if (v[0] == p->vendor[0] &&
                            v[1] == p->vendor[1] &&
                            v[2] == p->vendor[2])
                            return (int)i;
                    }
                    return -1;
                }

                Bool CPU_Is_InOrder()
                {
                    Cx86cpuid p;
                    int firm;
                    UInt32 family, model;
                    if (!x86cpuid_CheckAndRead(&p))
                        return True;
                    family = x86cpuid_GetFamily(&p);
                    model = x86cpuid_GetModel(&p);
                    firm = x86cpuid_GetFirm(&p);
                    switch (firm)
                    {
                    case CPU_FIRM_INTEL: return (family < 6 || (family == 6 && model == 0x100C));
                    case CPU_FIRM_AMD: return (family < 5 || (family == 5 && (model < 6 || model == 0xA)));
                    case CPU_FIRM_VIA: return (family < 6 || (family == 6 && model < 0xF));
                    }
                    return True;
                }

#if !defined(MY_CPU_AMD64) && defined(_WIN32)
                static Bool CPU_Sys_Is_SSE_Supported()
                {
                    OSVERSIONINFO vi;
                    vi.dwOSVersionInfoSize = sizeof(vi);
                    if (!GetVersionEx(&vi))
                        return False;
                    return (vi.dwMajorVersion >= 5);
                }
#define CHECK_SYS_SSE_SUPPORT if (!CPU_Sys_Is_SSE_Supported()) return False;
#else
#define CHECK_SYS_SSE_SUPPORT
#endif

                Bool CPU_Is_Aes_Supported()
                {
                    Cx86cpuid p;
                    CHECK_SYS_SSE_SUPPORT
                        if (!x86cpuid_CheckAndRead(&p))
                            return False;
                        return (p.c >> 25) & 1;
                }

#endif
#ifdef __cplusplus
                extern "C" {
#endif

                    /* #define _LZMA_PROB32 */
                    /* _LZMA_PROB32 can increase the speed on some CPUs,
                    but memory usage for CLzmaDec::probs will be doubled in that case */

#ifdef _LZMA_PROB32
#define CLzmaProb UInt32
#else
#define CLzmaProb UInt16
#endif


                    /* ---------- LZMA Properties ---------- */

#define LZMA_PROPS_SIZE 5

                    typedef struct _CLzmaProps
                    {
                        unsigned lc, lp, pb;
                        UInt32 dicSize;
                    } CLzmaProps;

                    /* LzmaProps_Decode - decodes properties
                    Returns:
                    SZ_OK
                    SZ_ERROR_UNSUPPORTED - Unsupported properties
                    */

                    SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size);


                    /* ---------- LZMA Decoder state ---------- */

                    /* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case.
                    Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */

#define LZMA_REQUIRED_INPUT_MAX 20

                    typedef struct
                    {
                        CLzmaProps prop;
                        CLzmaProb *probs;
                        Byte *dic;
                        const Byte *buf;
                        UInt32 range, code;
                        SizeT dicPos;
                        SizeT dicBufSize;
                        UInt32 processedPos;
                        UInt32 checkDicSize;
                        unsigned state;
                        UInt32 reps[4];
                        unsigned remainLen;
                        int needFlush;
                        int needInitState;
                        UInt32 numProbs;
                        unsigned tempBufSize;
                        Byte tempBuf[LZMA_REQUIRED_INPUT_MAX];
                    } CLzmaDec;

#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; }

                    void LzmaDec_Init(CLzmaDec *p);

                    /* There are two types of LZMA streams:
                    0) Stream with end mark. That end mark adds about 6 bytes to compressed size.
                    1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */

                    typedef enum
                    {
                        LZMA_FINISH_ANY,   /* finish at any point */
                            LZMA_FINISH_END    /* block must be finished at the end */
                    } ELzmaFinishMode;

                    /* ELzmaFinishMode has meaning only if the decoding reaches output limit !!!

                      You must use LZMA_FINISH_END, when you know that current output buffer
                      covers last bytes of block. In other cases you must use LZMA_FINISH_ANY.

                        If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK,
                        and output value of destLen will be less than output buffer size limit.
                        You can check status result also.

                          You can use multiple checks to test data integrity after full decompression:
                          1) Check Result and "status" variable.
                          2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize.
                          3) Check that output(srcLen) = compressedSize, if you know real compressedSize.
                    You must use correct finish mode in that case. */

                    typedef enum
                    {
                        LZMA_STATUS_NOT_SPECIFIED,               /* use main error code instead */
                            LZMA_STATUS_FINISHED_WITH_MARK,          /* stream was finished with end mark. */
                            LZMA_STATUS_NOT_FINISHED,                /* stream was not finished */
                            LZMA_STATUS_NEEDS_MORE_INPUT,            /* you must provide more input bytes */
                            LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK  /* there is probability that stream was finished without end mark */
                    } ELzmaStatus;

                    /* ELzmaStatus is used only as output value for function call */


                    /* ---------- Interfaces ---------- */

                    /* There are 3 levels of interfaces:
                    1) Dictionary Interface
                    2) Buffer Interface
                    3) One Call Interface
                    You can select any of these interfaces, but don't mix functions from different
                    groups for same object. */


                    /* There are two variants to allocate state for Dictionary Interface:
                    1) LzmaDec_Allocate / LzmaDec_Free
                    2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs
                    You can use variant 2, if you set dictionary buffer manually.
                    For Buffer Interface you must always use variant 1.

                      LzmaDec_Allocate* can return:
                      SZ_OK
                      SZ_ERROR_MEM         - Memory allocation error
                      SZ_ERROR_UNSUPPORTED - Unsupported properties
                    */

                    SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc);
                    void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc);

                    SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc);
                    void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc);

                    /* ---------- Dictionary Interface ---------- */

                    /* You can use it, if you want to eliminate the overhead for data copying from
                    dictionary to some other external buffer.
                    You must work with CLzmaDec variables directly in this interface.

                      STEPS:
                      LzmaDec_Constr()
                      LzmaDec_Allocate()
                      for (each new stream)
                      {
                      LzmaDec_Init()
                      while (it needs more decompression)
                      {
                      LzmaDec_DecodeToDic()
                      use data from CLzmaDec::dic and update CLzmaDec::dicPos
                      }
                      }
                      LzmaDec_Free()
                    */

                    /* LzmaDec_DecodeToDic

                      The decoding to internal dictionary buffer (CLzmaDec::dic).
                      You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!!

                        finishMode:
                        It has meaning only if the decoding reaches output limit (dicLimit).
                        LZMA_FINISH_ANY - Decode just dicLimit bytes.
                        LZMA_FINISH_END - Stream must be finished after dicLimit.

                          Returns:
                          SZ_OK
                          status:
                          LZMA_STATUS_FINISHED_WITH_MARK
                          LZMA_STATUS_NOT_FINISHED
                          LZMA_STATUS_NEEDS_MORE_INPUT
                          LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
                          SZ_ERROR_DATA - Data error
                    */

                    SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit,
                        const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);


                    /* ---------- Buffer Interface ---------- */

                    /* It's zlib-like interface.
                    See LzmaDec_DecodeToDic description for information about STEPS and return results,
                    but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need
                    to work with CLzmaDec variables manually.

                      finishMode:
                      It has meaning only if the decoding reaches output limit (*destLen).
                      LZMA_FINISH_ANY - Decode just destLen bytes.
                      LZMA_FINISH_END - Stream must be finished after (*destLen).
                    */

                    SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen,
                        const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);


                    /* ---------- One Call Interface ---------- */

                    /* LzmaDecode

                      finishMode:
                      It has meaning only if the decoding reaches output limit (*destLen).
                      LZMA_FINISH_ANY - Decode just destLen bytes.
                      LZMA_FINISH_END - Stream must be finished after (*destLen).

                        Returns:
                        SZ_OK
                        status:
                        LZMA_STATUS_FINISHED_WITH_MARK
                        LZMA_STATUS_NOT_FINISHED
                        LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
                        SZ_ERROR_DATA - Data error
                        SZ_ERROR_MEM  - Memory allocation error
                        SZ_ERROR_UNSUPPORTED - Unsupported properties
                        SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src).
                    */

                    SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
                        const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode,
                        ELzmaStatus *status, ISzAlloc *alloc);

#ifdef __cplusplus
}
#endif
#include <memory.h>

const Byte PPMD7_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };
static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051};

#define MAX_FREQ 124
#define UNIT_SIZE 12

#define U2B(nu) ((UInt32)(nu) * UNIT_SIZE)
#define U2I(nu) (p->Units2Indx[(nu) - 1])
#define I2U(indx) (p->Indx2Units[indx])

#ifdef PPMD_32BIT
#define REF(ptr) (ptr)
#else
#define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base))
#endif

#define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr))

#define CTX(ref) ((CPpmd7_Context *)Ppmd7_GetContext(p, ref))
#define STATS(ctx) Ppmd7_GetStats(p, ctx)
#define ONE_STATE(ctx) Ppmd7Context_OneState(ctx)
#define SUFFIX(ctx) CTX((ctx)->Suffix)

typedef CPpmd7_Context * CTX_PTR;

struct CPpmd7_Node_;

typedef
#ifdef PPMD_32BIT
struct CPpmd7_Node_ *
#else
UInt32
#endif
CPpmd7_Node_Ref;

typedef struct CPpmd7_Node_
{
    UInt16 Stamp; /* must be at offset 0 as CPpmd7_Context::NumStats. Stamp=0 means free */
    UInt16 NU;
    CPpmd7_Node_Ref Next; /* must be at offset >= 4 */
    CPpmd7_Node_Ref Prev;
} CPpmd7_Node;

#ifdef PPMD_32BIT
#define NODE(ptr) (ptr)
#else
#define NODE(offs) ((CPpmd7_Node *)(p->Base + (offs)))
#endif

void Ppmd7_Construct(CPpmd7 *p)
{
    unsigned i, k, m;

    p->Base = 0;

    for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++)
    {
        unsigned step = (i >= 12 ? 4 : (i >> 2) + 1);
        do { p->Units2Indx[k++] = (Byte)i; } while(--step);
        p->Indx2Units[i] = (Byte)k;
    }

    p->NS2BSIndx[0] = (0 << 1);
    p->NS2BSIndx[1] = (1 << 1);
    memset(p->NS2BSIndx + 2, (2 << 1), 9);
    memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11);

    for (i = 0; i < 3; i++)
        p->NS2Indx[i] = (Byte)i;
    for (m = i, k = 1; i < 256; i++)
    {
        p->NS2Indx[i] = (Byte)m;
        if (--k == 0)
            k = (++m) - 2;
    }

    memset(p->HB2Flag, 0, 0x40);
    memset(p->HB2Flag + 0x40, 8, 0x100 - 0x40);
}

void Ppmd7_Free(CPpmd7 *p, ISzAlloc *alloc)
{
    alloc->Free(alloc, p->Base);
    p->Size = 0;
    p->Base = 0;
}

Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size, ISzAlloc *alloc)
{
    if (p->Base == 0 || p->Size != size)
    {
        Ppmd7_Free(p, alloc);
        p->AlignOffset =
#ifdef PPMD_32BIT
            (4 - size) & 3;
#else
        4 - (size & 3);
#endif
        if ((p->Base = (Byte *)alloc->Alloc(alloc, p->AlignOffset + size
#ifndef PPMD_32BIT
            + UNIT_SIZE
#endif
            )) == 0)
            return False;
        p->Size = size;
    }
    return True;
}

static void InsertNode(CPpmd7 *p, void *node, unsigned indx)
{
    *((CPpmd_Void_Ref *)node) = p->FreeList[indx];
    p->FreeList[indx] = REF(node);
}

static void *RemoveNode(CPpmd7 *p, unsigned indx)
{
    CPpmd_Void_Ref *node = (CPpmd_Void_Ref *)Ppmd7_GetPtr(p, p->FreeList[indx]);
    p->FreeList[indx] = *node;
    return node;
}

static void SplitBlock(CPpmd7 *p, void *ptr, unsigned oldIndx, unsigned newIndx)
{
    unsigned i, nu = I2U(oldIndx) - I2U(newIndx);
    ptr = (Byte *)ptr + U2B(I2U(newIndx));
    if (I2U(i = U2I(nu)) != nu)
    {
        unsigned k = I2U(--i);
        InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1);
    }
    InsertNode(p, ptr, i);
}

static void GlueFreeBlocks(CPpmd7 *p)
{
#ifdef PPMD_32BIT
    CPpmd7_Node headItem;
    CPpmd7_Node_Ref head = &headItem;
#else
    CPpmd7_Node_Ref head = p->AlignOffset + p->Size;
#endif

    CPpmd7_Node_Ref n = head;
    unsigned i;

    p->GlueCount = 255;

    /* create doubly-linked list of free blocks */
    for (i = 0; i < PPMD_NUM_INDEXES; i++)
    {
        UInt16 nu = I2U(i);
        CPpmd7_Node_Ref next = (CPpmd7_Node_Ref)p->FreeList[i];
        p->FreeList[i] = 0;
        while (next != 0)
        {
            CPpmd7_Node *node = NODE(next);
            node->Next = n;
            n = NODE(n)->Prev = next;
            next = *(const CPpmd7_Node_Ref *)node;
            node->Stamp = 0;
            node->NU = (UInt16)nu;
        }
    }
    NODE(head)->Stamp = 1;
    NODE(head)->Next = n;
    NODE(n)->Prev = head;
    if (p->LoUnit != p->HiUnit)
        ((CPpmd7_Node *)p->LoUnit)->Stamp = 1;

    /* Glue free blocks */
    while (n != head)
    {
        CPpmd7_Node *node = NODE(n);
        UInt32 nu = (UInt32)node->NU;
        for (;;)
        {
            CPpmd7_Node *node2 = NODE(n) + nu;
            nu += node2->NU;
            if (node2->Stamp != 0 || nu >= 0x10000)
                break;
            NODE(node2->Prev)->Next = node2->Next;
            NODE(node2->Next)->Prev = node2->Prev;
            node->NU = (UInt16)nu;
        }
        n = node->Next;
    }

    /* Fill lists of free blocks */
    for (n = NODE(head)->Next; n != head;)
    {
        CPpmd7_Node *node = NODE(n);
        unsigned nu;
        CPpmd7_Node_Ref next = node->Next;
        for (nu = node->NU; nu > 128; nu -= 128, node += 128)
            InsertNode(p, node, PPMD_NUM_INDEXES - 1);
        if (I2U(i = U2I(nu)) != nu)
        {
            unsigned k = I2U(--i);
            InsertNode(p, node + k, nu - k - 1);
        }
        InsertNode(p, node, i);
        n = next;
    }
}

static void *AllocUnitsRare(CPpmd7 *p, unsigned indx)
{
    unsigned i;
    void *retVal;
    if (p->GlueCount == 0)
    {
        GlueFreeBlocks(p);
        if (p->FreeList[indx] != 0)
            return RemoveNode(p, indx);
    }
    i = indx;
    do
    {
        if (++i == PPMD_NUM_INDEXES)
        {
            UInt32 numBytes = U2B(I2U(indx));
            p->GlueCount--;
            return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL);
        }
    }
    while (p->FreeList[i] == 0);
    retVal = RemoveNode(p, i);
    SplitBlock(p, retVal, i, indx);
    return retVal;
}

static void *AllocUnits(CPpmd7 *p, unsigned indx)
{
    UInt32 numBytes;
    if (p->FreeList[indx] != 0)
        return RemoveNode(p, indx);
    numBytes = U2B(I2U(indx));
    if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit))
    {
        void *retVal = p->LoUnit;
        p->LoUnit += numBytes;
        return retVal;
    }
    return AllocUnitsRare(p, indx);
}

#define MyMem12Cpy(dest, src, num) \
{ UInt32 *d = (UInt32 *)dest; const UInt32 *s = (const UInt32 *)src; UInt32 n = num; \
do { d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; s += 3; d += 3; } while(--n); }

static void *ShrinkUnits(CPpmd7 *p, void *oldPtr, unsigned oldNU, unsigned newNU)
{
    unsigned i0 = U2I(oldNU);
    unsigned i1 = U2I(newNU);
    if (i0 == i1)
        return oldPtr;
    if (p->FreeList[i1] != 0)
    {
        void *ptr = RemoveNode(p, i1);
        MyMem12Cpy(ptr, oldPtr, newNU);
        InsertNode(p, oldPtr, i0);
        return ptr;
    }
    SplitBlock(p, oldPtr, i0, i1);
    return oldPtr;
}

#define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16)))

static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v)
{
    (p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF);
    (p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF);
}

static void RestartModel(CPpmd7 *p)
{
    unsigned i, k, m;

    memset(p->FreeList, 0, sizeof(p->FreeList));
    p->Text = p->Base + p->AlignOffset;
    p->HiUnit = p->Text + p->Size;
    p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE;
    p->GlueCount = 0;

    p->OrderFall = p->MaxOrder;
    p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1;
    p->PrevSuccess = 0;

    p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */
    p->MinContext->Suffix = 0;
    p->MinContext->NumStats = 256;
    p->MinContext->SummFreq = 256 + 1;
    p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */
    p->LoUnit += U2B(256 / 2);
    p->MinContext->Stats = REF(p->FoundState);
    for (i = 0; i < 256; i++)
    {
        CPpmd_State *s = &p->FoundState[i];
        s->Symbol = (Byte)i;
        s->Freq = 1;
        SetSuccessor(s, 0);
    }

    for (i = 0; i < 128; i++)
        for (k = 0; k < 8; k++)
        {
            UInt16 *dest = p->BinSumm[i] + k;
            UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 2));
            for (m = 0; m < 64; m += 8)
                dest[m] = val;
        }

        for (i = 0; i < 25; i++)
            for (k = 0; k < 16; k++)
            {
                CPpmd_See *s = &p->See[i][k];
                s->Summ = (UInt16)((5 * i + 10) << (s->Shift = PPMD_PERIOD_BITS - 4));
                s->Count = 4;
            }
}

void Ppmd7_Init(CPpmd7 *p, unsigned maxOrder)
{
    p->MaxOrder = maxOrder;
    RestartModel(p);
    p->DummySee.Shift = PPMD_PERIOD_BITS;
    p->DummySee.Summ = 0; /* unused */
    p->DummySee.Count = 64; /* unused */
}

static CTX_PTR CreateSuccessors(CPpmd7 *p, Bool skip)
{
    CPpmd_State upState;
    CTX_PTR c = p->MinContext;
    CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState);
    CPpmd_State *ps[PPMD7_MAX_ORDER];
    unsigned numPs = 0;

    if (!skip)
        ps[numPs++] = p->FoundState;

    while (c->Suffix)
    {
        CPpmd_Void_Ref successor;
        CPpmd_State *s;
        c = SUFFIX(c);
        if (c->NumStats != 1)
        {
            for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++);
        }
        else
            s = ONE_STATE(c);
        successor = SUCCESSOR(s);
        if (successor != upBranch)
        {
            c = CTX(successor);
            if (numPs == 0)
                return c;
            break;
        }
        ps[numPs++] = s;
    }

    upState.Symbol = *(const Byte *)Ppmd7_GetPtr(p, upBranch);
    SetSuccessor(&upState, upBranch + 1);

    if (c->NumStats == 1)
        upState.Freq = ONE_STATE(c)->Freq;
    else
    {
        UInt32 cf, s0;
        CPpmd_State *s;
        for (s = STATS(c); s->Symbol != upState.Symbol; s++);
        cf = s->Freq - 1;
        s0 = c->SummFreq - c->NumStats - cf;
        upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((2 * cf + 3 * s0 - 1) / (2 * s0))));
    }

    do
    {
        /* Create Child */
        CTX_PTR c1; /* = AllocContext(p); */
        if (p->HiUnit != p->LoUnit)
            c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE);
        else if (p->FreeList[0] != 0)
            c1 = (CTX_PTR)RemoveNode(p, 0);
        else
        {
            c1 = (CTX_PTR)AllocUnitsRare(p, 0);
            if (!c1)
                return NULL;
        }
        c1->NumStats = 1;
        *ONE_STATE(c1) = upState;
        c1->Suffix = REF(c);
        SetSuccessor(ps[--numPs], REF(c1));
        c = c1;
    }
    while (numPs != 0);

    return c;
}

static void SwapStates(CPpmd_State *t1, CPpmd_State *t2)
{
    CPpmd_State tmp = *t1;
    *t1 = *t2;
    *t2 = tmp;
}

static void UpdateModel(CPpmd7 *p)
{
    CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState);
    CTX_PTR c;
    unsigned s0, ns;

    if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0)
    {
        c = SUFFIX(p->MinContext);

        if (c->NumStats == 1)
        {
            CPpmd_State *s = ONE_STATE(c);
            if (s->Freq < 32)
                s->Freq++;
        }
        else
        {
            CPpmd_State *s = STATS(c);
            if (s->Symbol != p->FoundState->Symbol)
            {
                do { s++; } while (s->Symbol != p->FoundState->Symbol);
                if (s[0].Freq >= s[-1].Freq)
                {
                    SwapStates(&s[0], &s[-1]);
                    s--;
                }
            }
            if (s->Freq < MAX_FREQ - 9)
            {
                s->Freq += 2;
                c->SummFreq += 2;
            }
        }
    }

    if (p->OrderFall == 0)
    {
        p->MinContext = p->MaxContext = CreateSuccessors(p, True);
        if (p->MinContext == 0)
        {
            RestartModel(p);
            return;
        }
        SetSuccessor(p->FoundState, REF(p->MinContext));
        return;
    }

    *p->Text++ = p->FoundState->Symbol;
    successor = REF(p->Text);
    if (p->Text >= p->UnitsStart)
    {
        RestartModel(p);
        return;
    }

    if (fSuccessor)
    {
        if (fSuccessor <= successor)
        {
            CTX_PTR cs = CreateSuccessors(p, False);
            if (cs == NULL)
            {
                RestartModel(p);
                return;
            }
            fSuccessor = REF(cs);
        }
        if (--p->OrderFall == 0)
        {
            successor = fSuccessor;
            p->Text -= (p->MaxContext != p->MinContext);
        }
    }
    else
    {
        SetSuccessor(p->FoundState, successor);
        fSuccessor = REF(p->MinContext);
    }

    s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - (p->FoundState->Freq - 1);

    for (c = p->MaxContext; c != p->MinContext; c = SUFFIX(c))
    {
        unsigned ns1;
        UInt32 cf, sf;
        if ((ns1 = c->NumStats) != 1)
        {
            if ((ns1 & 1) == 0)
            {
                /* Expand for one UNIT */
                unsigned oldNU = ns1 >> 1;
                unsigned i = U2I(oldNU);
                if (i != U2I(oldNU + 1))
                {
                    void *ptr = AllocUnits(p, i + 1);
                    void *oldPtr;
                    if (!ptr)
                    {
                        RestartModel(p);
                        return;
                    }
                    oldPtr = STATS(c);
                    MyMem12Cpy(ptr, oldPtr, oldNU);
                    InsertNode(p, oldPtr, i);
                    c->Stats = STATS_REF(ptr);
                }
            }
            c->SummFreq = (UInt16)(c->SummFreq + (2 * ns1 < ns) + 2 * ((4 * ns1 <= ns) & (c->SummFreq <= 8 * ns1)));
        }
        else
        {
            CPpmd_State *s = (CPpmd_State*)AllocUnits(p, 0);
            if (!s)
            {
                RestartModel(p);
                return;
            }
            *s = *ONE_STATE(c);
            c->Stats = REF(s);
            if (s->Freq < MAX_FREQ / 4 - 1)
                s->Freq <<= 1;
            else
                s->Freq = MAX_FREQ - 4;
            c->SummFreq = (UInt16)(s->Freq + p->InitEsc + (ns > 3));
        }
        cf = 2 * (UInt32)p->FoundState->Freq * (c->SummFreq + 6);
        sf = (UInt32)s0 + c->SummFreq;
        if (cf < 6 * sf)
        {
            cf = 1 + (cf > sf) + (cf >= 4 * sf);
            c->SummFreq += 3;
        }
        else
        {
            cf = 4 + (cf >= 9 * sf) + (cf >= 12 * sf) + (cf >= 15 * sf);
            c->SummFreq = (UInt16)(c->SummFreq + cf);
        }
        {
            CPpmd_State *s = STATS(c) + ns1;
            SetSuccessor(s, successor);
            s->Symbol = p->FoundState->Symbol;
            s->Freq = (Byte)cf;
            c->NumStats = (UInt16)(ns1 + 1);
        }
    }
    p->MaxContext = p->MinContext = CTX(fSuccessor);
}

static void Rescale(CPpmd7 *p)
{
    unsigned i, adder, sumFreq, escFreq;
    CPpmd_State *stats = STATS(p->MinContext);
    CPpmd_State *s = p->FoundState;
    {
        CPpmd_State tmp = *s;
        for (; s != stats; s--)
            s[0] = s[-1];
        *s = tmp;
    }
    escFreq = p->MinContext->SummFreq - s->Freq;
    s->Freq += 4;
    adder = (p->OrderFall != 0);
    s->Freq = (Byte)((s->Freq + adder) >> 1);
    sumFreq = s->Freq;

    i = p->MinContext->NumStats - 1;
    do
    {
        escFreq -= (++s)->Freq;
        s->Freq = (Byte)((s->Freq + adder) >> 1);
        sumFreq += s->Freq;
        if (s[0].Freq > s[-1].Freq)
        {
            CPpmd_State *s1 = s;
            CPpmd_State tmp = *s1;
            do
            s1[0] = s1[-1];
            while (--s1 != stats && tmp.Freq > s1[-1].Freq);
            *s1 = tmp;
        }
    }
    while (--i);

    if (s->Freq == 0)
    {
        unsigned numStats = p->MinContext->NumStats;
        unsigned n0, n1;
        do { i++; } while ((--s)->Freq == 0);
        escFreq += i;
        p->MinContext->NumStats = (UInt16)(p->MinContext->NumStats - i);
        if (p->MinContext->NumStats == 1)
        {
            CPpmd_State tmp = *stats;
            do
            {
                tmp.Freq = (Byte)(tmp.Freq - (tmp.Freq >> 1));
                escFreq >>= 1;
            }
            while (escFreq > 1);
            InsertNode(p, stats, U2I(((numStats + 1) >> 1)));
            *(p->FoundState = ONE_STATE(p->MinContext)) = tmp;
            return;
        }
        n0 = (numStats + 1) >> 1;
        n1 = (p->MinContext->NumStats + 1) >> 1;
        if (n0 != n1)
            p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1));
    }
    p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1));
    p->FoundState = STATS(p->MinContext);
}

CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *escFreq)
{
    CPpmd_See *see;
    unsigned nonMasked = p->MinContext->NumStats - numMasked;
    if (p->MinContext->NumStats != 256)
    {
        see = p->See[p->NS2Indx[nonMasked - 1]] +
            (nonMasked < (unsigned)SUFFIX(p->MinContext)->NumStats - p->MinContext->NumStats) +
            2 * (p->MinContext->SummFreq < 11 * p->MinContext->NumStats) +
            4 * (numMasked > nonMasked) +
            p->HiBitsFlag;
        {
            unsigned r = (see->Summ >> see->Shift);
            see->Summ = (UInt16)(see->Summ - r);
            *escFreq = r + (r == 0);
        }
    }
    else
    {
        see = &p->DummySee;
        *escFreq = 1;
    }
    return see;
}

static void NextContext(CPpmd7 *p)
{
    CTX_PTR c = CTX(SUCCESSOR(p->FoundState));
    if (p->OrderFall == 0 && (Byte *)c > p->Text)
        p->MinContext = p->MaxContext = c;
    else
        UpdateModel(p);
}

void Ppmd7_Update1(CPpmd7 *p)
{
    CPpmd_State *s = p->FoundState;
    s->Freq += 4;
    p->MinContext->SummFreq += 4;
    if (s[0].Freq > s[-1].Freq)
    {
        SwapStates(&s[0], &s[-1]);
        p->FoundState = --s;
        if (s->Freq > MAX_FREQ)
            Rescale(p);
    }
    NextContext(p);
}

void Ppmd7_Update1_0(CPpmd7 *p)
{
    p->PrevSuccess = (2 * p->FoundState->Freq > p->MinContext->SummFreq);
    p->RunLength += p->PrevSuccess;
    p->MinContext->SummFreq += 4;
    if ((p->FoundState->Freq += 4) > MAX_FREQ)
        Rescale(p);
    NextContext(p);
}

void Ppmd7_UpdateBin(CPpmd7 *p)
{
    p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 128 ? 1: 0));
    p->PrevSuccess = 1;
    p->RunLength++;
    NextContext(p);
}

void Ppmd7_Update2(CPpmd7 *p)
{
    p->MinContext->SummFreq += 4;
    if ((p->FoundState->Freq += 4) > MAX_FREQ)
        Rescale(p);
    p->RunLength = p->InitRL;
    UpdateModel(p);
}
Bool Ppmd7z_RangeDec_Init(CPpmd7z_RangeDec *p)
{
    unsigned i;
    p->Code = 0;
    p->Range = 0xFFFFFFFF;
    if (p->Stream->Read((void *)p->Stream) != 0)
        return False;
    for (i = 0; i < 4; i++)
        p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream);
    return (p->Code < 0xFFFFFFFF);
}

static UInt32 Range_GetThreshold(void *pp, UInt32 total)
{
    CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
    return (p->Code) / (p->Range /= total);
}

static void Range_Normalize(CPpmd7z_RangeDec *p)
{
    if (p->Range < (1 << 24))
    {
        p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream);
        p->Range <<= 8;
        if (p->Range < (1 << 24))
        {
            p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream);
            p->Range <<= 8;
        }
    }
}

static void Range_Decode(void *pp, UInt32 start, UInt32 size)
{
    CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
    p->Code -= start * p->Range;
    p->Range *= size;
    Range_Normalize(p);
}

static UInt32 Range_DecodeBit(void *pp, UInt32 size0)
{
    CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
    UInt32 newBound = (p->Range >> 14) * size0;
    UInt32 symbol;
    if (p->Code < newBound)
    {
        symbol = 0;
        p->Range = newBound;
    }
    else
    {
        symbol = 1;
        p->Code -= newBound;
        p->Range -= newBound;
    }
    Range_Normalize(p);
    return symbol;
}

void Ppmd7z_RangeDec_CreateVTable(CPpmd7z_RangeDec *p)
{
    p->p.GetThreshold = Range_GetThreshold;
    p->p.Decode = Range_Decode;
    p->p.DecodeBit = Range_DecodeBit;
}


#define MASK(sym) ((signed char *)charMask)[sym]

int Ppmd7_DecodeSymbol(CPpmd7 *p, IPpmd7_RangeDec *rc)
{
    size_t charMask[256 / sizeof(size_t)];
    if (p->MinContext->NumStats != 1)
    {
        CPpmd_State *s = Ppmd7_GetStats(p, p->MinContext);
        unsigned i;
        UInt32 count, hiCnt;
        if ((count = rc->GetThreshold(rc, p->MinContext->SummFreq)) < (hiCnt = s->Freq))
        {
            Byte symbol;
            rc->Decode(rc, 0, s->Freq);
            p->FoundState = s;
            symbol = s->Symbol;
            Ppmd7_Update1_0(p);
            return symbol;
        }
        p->PrevSuccess = 0;
        i = p->MinContext->NumStats - 1;
        do
        {
            if ((hiCnt += (++s)->Freq) > count)
            {
                Byte symbol;
                rc->Decode(rc, hiCnt - s->Freq, s->Freq);
                p->FoundState = s;
                symbol = s->Symbol;
                Ppmd7_Update1(p);
                return symbol;
            }
        }
        while (--i);
        if (count >= p->MinContext->SummFreq)
            return -2;
        p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol];
        rc->Decode(rc, hiCnt, p->MinContext->SummFreq - hiCnt);
        PPMD_SetAllBitsIn256Bytes(charMask);
        MASK(s->Symbol) = 0;
        i = p->MinContext->NumStats - 1;
        do { MASK((--s)->Symbol) = 0; } while (--i);
    }
    else
    {
        UInt16 *prob = Ppmd7_GetBinSumm(p);
        if (rc->DecodeBit(rc, *prob) == 0)
        {
            Byte symbol;
            *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob);
            symbol = (p->FoundState = Ppmd7Context_OneState(p->MinContext))->Symbol;
            Ppmd7_UpdateBin(p);
            return symbol;
        }
        *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob);
        p->InitEsc = PPMD7_kExpEscape[*prob >> 10];
        PPMD_SetAllBitsIn256Bytes(charMask);
        MASK(Ppmd7Context_OneState(p->MinContext)->Symbol) = 0;
        p->PrevSuccess = 0;
    }
    for (;;)
    {
        CPpmd_State *ps[256], *s;
        UInt32 freqSum, count, hiCnt;
        CPpmd_See *see;
        unsigned i, num, numMasked = p->MinContext->NumStats;
        do
        {
            p->OrderFall++;
            if (!p->MinContext->Suffix)
                return -1;
            p->MinContext = Ppmd7_GetContext(p, p->MinContext->Suffix);
        }
        while (p->MinContext->NumStats == numMasked);
        hiCnt = 0;
        s = Ppmd7_GetStats(p, p->MinContext);
        i = 0;
        num = p->MinContext->NumStats - numMasked;
        do
        {
            int k = (int)(MASK(s->Symbol));
            hiCnt += (s->Freq & k);
            ps[i] = s++;
            i -= k;
        }
        while (i != num);

        see = Ppmd7_MakeEscFreq(p, numMasked, &freqSum);
        freqSum += hiCnt;
        count = rc->GetThreshold(rc, freqSum);

        if (count < hiCnt)
        {
            Byte symbol;
            CPpmd_State **pps = ps;
            for (hiCnt = 0; (hiCnt += (*pps)->Freq) <= count; pps++);
            s = *pps;
            rc->Decode(rc, hiCnt - s->Freq, s->Freq);
            Ppmd_See_Update(see);
            p->FoundState = s;
            symbol = s->Symbol;
            Ppmd7_Update2(p);
            return symbol;
        }
        if (count >= freqSum)
            return -2;
        rc->Decode(rc, hiCnt, freqSum - hiCnt);
        see->Summ = (UInt16)(see->Summ + freqSum);
        do { MASK(ps[--i]->Symbol) = 0; } while (i != 0);
    }
}
EXTERN_C_BEGIN

#define k7zStartHeaderSize 0x20
#define k7zSignatureSize 6
extern Byte k7zSignature[k7zSignatureSize];
#define k7zMajorVersion 0

enum EIdEnum
{
    k7zIdEnd,
        k7zIdHeader,
        k7zIdArchiveProperties,
        k7zIdAdditionalStreamsInfo,
        k7zIdMainStreamsInfo,
        k7zIdFilesInfo,
        k7zIdPackInfo,
        k7zIdUnpackInfo,
        k7zIdSubStreamsInfo,
        k7zIdSize,
        k7zIdCRC,
        k7zIdFolder,
        k7zIdCodersUnpackSize,
        k7zIdNumUnpackStream,
        k7zIdEmptyStream,
        k7zIdEmptyFile,
        k7zIdAnti,
        k7zIdName,
        k7zIdCTime,
        k7zIdATime,
        k7zIdMTime,
        k7zIdWinAttributes,
        k7zIdComment,
        k7zIdEncodedHeader,
        k7zIdStartPos,
        k7zIdDummy
};

typedef struct
{
    UInt32 NumInStreams;
    UInt32 NumOutStreams;
    UInt64 MethodID;
    CBuf Props;
} CSzCoderInfo;

void SzCoderInfo_Init(CSzCoderInfo *p);
void SzCoderInfo_Free(CSzCoderInfo *p, ISzAlloc *alloc);

typedef struct
{
    UInt32 InIndex;
    UInt32 OutIndex;
} CSzBindPair;

typedef struct
{
    CSzCoderInfo *Coders;
    CSzBindPair *BindPairs;
    UInt32 *PackStreams;
    UInt64 *UnpackSizes;
    UInt32 NumCoders;
    UInt32 NumBindPairs;
    UInt32 NumPackStreams;
    int UnpackCRCDefined;
    UInt32 UnpackCRC;

    UInt32 NumUnpackStreams;
} CSzFolder;

void SzFolder_Init(CSzFolder *p);
UInt64 SzFolder_GetUnpackSize(CSzFolder *p);
int SzFolder_FindBindPairForInStream(CSzFolder *p, UInt32 inStreamIndex);
UInt32 SzFolder_GetNumOutStreams(CSzFolder *p);
UInt64 SzFolder_GetUnpackSize(CSzFolder *p);

SRes SzFolder_Decode(const CSzFolder *folder, const UInt64 *packSizes,
                     ILookInStream *stream, UInt64 startPos,
                     Byte *outBuffer, size_t outSize, ISzAlloc *allocMain);

typedef struct
{
    UInt32 Low;
    UInt32 High;
} CNtfsFileTime;

typedef struct
{
    CNtfsFileTime MTime;
    UInt64 Size;
    UInt32 Crc;
    UInt32 Attrib;
    Byte HasStream;
    Byte IsDir;
    Byte IsAnti;
    Byte CrcDefined;
    Byte MTimeDefined;
    Byte AttribDefined;
} CSzFileItem;

void SzFile_Init(CSzFileItem *p);

typedef struct
{
    UInt64 *PackSizes;
    Byte *PackCRCsDefined;
    UInt32 *PackCRCs;
    CSzFolder *Folders;
    CSzFileItem *Files;
    UInt32 NumPackStreams;
    UInt32 NumFolders;
    UInt32 NumFiles;
} CSzAr;

void SzAr_Init(CSzAr *p);
void SzAr_Free(CSzAr *p, ISzAlloc *alloc);


/*
SzExtract extracts file from archive

  *outBuffer must be 0 before first call for each new archive.

    Extracting cache:
    If you need to decompress more than one file, you can send
    these values from previous call:
    *blockIndex,
    *outBuffer,
    *outBufferSize
    You can consider "*outBuffer" as cache of solid block. If your archive is solid,
    it will increase decompression speed.

      If you use external function, you can declare these 3 cache variables
      (blockIndex, outBuffer, outBufferSize) as static in that external function.

        Free *outBuffer and set *outBuffer to 0, if you want to flush cache.
*/

typedef struct
{
    CSzAr db;

    UInt64 startPosAfterHeader;
    UInt64 dataPos;

    UInt32 *FolderStartPackStreamIndex;
    UInt64 *PackStreamStartPositions;
    UInt32 *FolderStartFileIndex;
    UInt32 *FileIndexToFolderIndexMap;

    size_t *FileNameOffsets; /* in 2-byte steps */
    CBuf FileNames;  /* UTF-16-LE */
} CSzArEx;

void SzArEx_Init(CSzArEx *p);
void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc);
UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder);
int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize);

/*
if dest == NULL, the return value specifies the required size of the buffer,
in 16-bit characters, including the null-terminating character.
if dest != NULL, the return value specifies the number of 16-bit characters that
are written to the dest, including the null-terminating character. */

size_t SzArEx_GetFileNameUtf16(const CSzArEx *p, size_t fileIndex, UInt16 *dest);

SRes SzArEx_Extract(
                    const CSzArEx *db,
                    ILookInStream *inStream,
                    UInt32 fileIndex,         /* index of file */
                    UInt32 *blockIndex,       /* index of solid block */
                    Byte **outBuffer,         /* pointer to pointer to output buffer (allocated with allocMain) */
                    size_t *outBufferSize,    /* buffer size for output buffer */
                    size_t *offset,           /* offset of stream for required file in *outBuffer */
                    size_t *outSizeProcessed, /* size of file in *outBuffer */
                    ISzAlloc *allocMain,
                    ISzAlloc *allocTemp);


                    /*
                    SzArEx_Open Errors:
                    SZ_ERROR_NO_ARCHIVE
                    SZ_ERROR_ARCHIVE
                    SZ_ERROR_UNSUPPORTED
                    SZ_ERROR_MEM
                    SZ_ERROR_CRC
                    SZ_ERROR_INPUT_EOF
                    SZ_ERROR_FAIL
*/

SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, ISzAlloc *allocMain, ISzAlloc *allocTemp);

EXTERN_C_END
#define kCrcPoly 0xEDB88320

#ifdef MY_CPU_LE
#define CRC_NUM_TABLES 8
#else
#define CRC_NUM_TABLES 1
#endif

typedef UInt32 (MY_FAST_CALL *CRC_FUNC)(UInt32 v, const void *data, size_t size, const UInt32 *table);

static CRC_FUNC g_CrcUpdate;
UInt32 g_CrcTable[256 * CRC_NUM_TABLES];

#if CRC_NUM_TABLES == 1

#define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8))

static UInt32 MY_FAST_CALL CrcUpdateT1(UInt32 v, const void *data, size_t size, const UInt32 *table)
{
    const Byte *p = (const Byte *)data;
    for (; size > 0; size--, p++)
        v = CRC_UPDATE_BYTE_2(v, *p);
    return v;
}

#else

UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table);
UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table);

#endif

UInt32 MY_FAST_CALL CrcUpdate(UInt32 v, const void *data, size_t size)
{
    return g_CrcUpdate(v, data, size, g_CrcTable);
}

UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size)
{
    return g_CrcUpdate(CRC_INIT_VAL, data, size, g_CrcTable) ^ CRC_INIT_VAL;
}

void MY_FAST_CALL CrcGenerateTable()
{
    UInt32 i;
    for (i = 0; i < 256; i++)
    {
        UInt32 r = i;
        unsigned j;
        for (j = 0; j < 8; j++)
            r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1));
        g_CrcTable[i] = r;
    }
#if CRC_NUM_TABLES == 1
    g_CrcUpdate = CrcUpdateT1;
#else
    for (; i < 256 * CRC_NUM_TABLES; i++)
    {
        UInt32 r = g_CrcTable[i - 256];
        g_CrcTable[i] = g_CrcTable[r & 0xFF] ^ (r >> 8);
    }
    g_CrcUpdate = CrcUpdateT4;
#ifdef MY_CPU_X86_OR_AMD64
    if (!CPU_Is_InOrder())
        g_CrcUpdate = CrcUpdateT8;
#endif
#endif
}
#ifdef MY_CPU_LE

#define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8))

UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table)
{
    const Byte *p = (const Byte *)data;
    for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++)
        v = CRC_UPDATE_BYTE_2(v, *p);
    for (; size >= 4; size -= 4, p += 4)
    {
        v ^= *(const UInt32 *)p;
        v =
            table[0x300 + (v & 0xFF)] ^
            table[0x200 + ((v >> 8) & 0xFF)] ^
            table[0x100 + ((v >> 16) & 0xFF)] ^
            table[0x000 + ((v >> 24))];
    }
    for (; size > 0; size--, p++)
        v = CRC_UPDATE_BYTE_2(v, *p);
    return v;
}

UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table)
{
    return CrcUpdateT4(v, data, size, table);
}

#endif

Byte k7zSignature[k7zSignatureSize] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C};

#define RINOM(x) { if ((x) == 0) return SZ_ERROR_MEM; }

#define NUM_FOLDER_CODERS_MAX 32
#define NUM_CODER_STREAMS_MAX 32

void SzCoderInfo_Init(CSzCoderInfo *p)
{
    Buf_Init(&p->Props);
}

void SzCoderInfo_Free(CSzCoderInfo *p, ISzAlloc *alloc)
{
    Buf_Free(&p->Props, alloc);
    SzCoderInfo_Init(p);
}

void SzFolder_Init(CSzFolder *p)
{
    p->Coders = 0;
    p->BindPairs = 0;
    p->PackStreams = 0;
    p->UnpackSizes = 0;
    p->NumCoders = 0;
    p->NumBindPairs = 0;
    p->NumPackStreams = 0;
    p->UnpackCRCDefined = 0;
    p->UnpackCRC = 0;
    p->NumUnpackStreams = 0;
}

void SzFolder_Free(CSzFolder *p, ISzAlloc *alloc)
{
    UInt32 i;
    if (p->Coders)
        for (i = 0; i < p->NumCoders; i++)
            SzCoderInfo_Free(&p->Coders[i], alloc);
        IAlloc_Free(alloc, p->Coders);
        IAlloc_Free(alloc, p->BindPairs);
        IAlloc_Free(alloc, p->PackStreams);
        IAlloc_Free(alloc, p->UnpackSizes);
        SzFolder_Init(p);
}

UInt32 SzFolder_GetNumOutStreams(CSzFolder *p)
{
    UInt32 result = 0;
    UInt32 i;
    for (i = 0; i < p->NumCoders; i++)
        result += p->Coders[i].NumOutStreams;
    return result;
}

int SzFolder_FindBindPairForInStream(CSzFolder *p, UInt32 inStreamIndex)
{
    UInt32 i;
    for (i = 0; i < p->NumBindPairs; i++)
        if (p->BindPairs[i].InIndex == inStreamIndex)
            return i;
        return -1;
}


int SzFolder_FindBindPairForOutStream(CSzFolder *p, UInt32 outStreamIndex)
{
    UInt32 i;
    for (i = 0; i < p->NumBindPairs; i++)
        if (p->BindPairs[i].OutIndex == outStreamIndex)
            return i;
        return -1;
}

UInt64 SzFolder_GetUnpackSize(CSzFolder *p)
{
    int i = (int)SzFolder_GetNumOutStreams(p);
    if (i == 0)
        return 0;
    for (i--; i >= 0; i--)
        if (SzFolder_FindBindPairForOutStream(p, i) < 0)
            return p->UnpackSizes[i];
        /* throw 1; */
        return 0;
}

void SzFile_Init(CSzFileItem *p)
{
    p->HasStream = 1;
    p->IsDir = 0;
    p->IsAnti = 0;
    p->CrcDefined = 0;
    p->MTimeDefined = 0;
}

void SzAr_Init(CSzAr *p)
{
    p->PackSizes = 0;
    p->PackCRCsDefined = 0;
    p->PackCRCs = 0;
    p->Folders = 0;
    p->Files = 0;
    p->NumPackStreams = 0;
    p->NumFolders = 0;
    p->NumFiles = 0;
}

void SzAr_Free(CSzAr *p, ISzAlloc *alloc)
{
    UInt32 i;
    if (p->Folders)
        for (i = 0; i < p->NumFolders; i++)
            SzFolder_Free(&p->Folders[i], alloc);

        IAlloc_Free(alloc, p->PackSizes);
        IAlloc_Free(alloc, p->PackCRCsDefined);
        IAlloc_Free(alloc, p->PackCRCs);
        IAlloc_Free(alloc, p->Folders);
        IAlloc_Free(alloc, p->Files);
        SzAr_Init(p);
}


void SzArEx_Init(CSzArEx *p)
{
    SzAr_Init(&p->db);
    p->FolderStartPackStreamIndex = 0;
    p->PackStreamStartPositions = 0;
    p->FolderStartFileIndex = 0;
    p->FileIndexToFolderIndexMap = 0;
    p->FileNameOffsets = 0;
    Buf_Init(&p->FileNames);
}

void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc)
{
    IAlloc_Free(alloc, p->FolderStartPackStreamIndex);
    IAlloc_Free(alloc, p->PackStreamStartPositions);
    IAlloc_Free(alloc, p->FolderStartFileIndex);
    IAlloc_Free(alloc, p->FileIndexToFolderIndexMap);

    IAlloc_Free(alloc, p->FileNameOffsets);
    Buf_Free(&p->FileNames, alloc);

    SzAr_Free(&p->db, alloc);
    SzArEx_Init(p);
}

/*
UInt64 GetFolderPackStreamSize(int folderIndex, int streamIndex) const
{
return PackSizes[FolderStartPackStreamIndex[folderIndex] + streamIndex];
}

  UInt64 GetFilePackSize(int fileIndex) const
  {
  int folderIndex = FileIndexToFolderIndexMap[fileIndex];
  if (folderIndex >= 0)
  {
  const CSzFolder &folderInfo = Folders[folderIndex];
  if (FolderStartFileIndex[folderIndex] == fileIndex)
  return GetFolderFullPackSize(folderIndex);
  }
  return 0;
  }
*/

#define MY_ALLOC(T, p, size, alloc) { if ((size) == 0) p = 0; else \
if ((p = (T *)IAlloc_Alloc(alloc, (size) * sizeof(T))) == 0) return SZ_ERROR_MEM; }

static SRes SzArEx_Fill(CSzArEx *p, ISzAlloc *alloc)
{
    UInt32 startPos = 0;
    UInt64 startPosSize = 0;
    UInt32 i;
    UInt32 folderIndex = 0;
    UInt32 indexInFolder = 0;
    MY_ALLOC(UInt32, p->FolderStartPackStreamIndex, p->db.NumFolders, alloc);
    for (i = 0; i < p->db.NumFolders; i++)
    {
        p->FolderStartPackStreamIndex[i] = startPos;
        startPos += p->db.Folders[i].NumPackStreams;
    }

    MY_ALLOC(UInt64, p->PackStreamStartPositions, p->db.NumPackStreams, alloc);

    for (i = 0; i < p->db.NumPackStreams; i++)
    {
        p->PackStreamStartPositions[i] = startPosSize;
        startPosSize += p->db.PackSizes[i];
    }

    MY_ALLOC(UInt32, p->FolderStartFileIndex, p->db.NumFolders, alloc);
    MY_ALLOC(UInt32, p->FileIndexToFolderIndexMap, p->db.NumFiles, alloc);

    for (i = 0; i < p->db.NumFiles; i++)
    {
        CSzFileItem *file = p->db.Files + i;
        int emptyStream = !file->HasStream;
        if (emptyStream && indexInFolder == 0)
        {
            p->FileIndexToFolderIndexMap[i] = (UInt32)-1;
            continue;
        }
        if (indexInFolder == 0)
        {
        /*
        v3.13 incorrectly worked with empty folders
        v4.07: Loop for skipping empty folders
            */
            for (;;)
            {
                if (folderIndex >= p->db.NumFolders)
                    return SZ_ERROR_ARCHIVE;
                p->FolderStartFileIndex[folderIndex] = i;
                if (p->db.Folders[folderIndex].NumUnpackStreams != 0)
                    break;
                folderIndex++;
            }
        }
        p->FileIndexToFolderIndexMap[i] = folderIndex;
        if (emptyStream)
            continue;
        indexInFolder++;
        if (indexInFolder >= p->db.Folders[folderIndex].NumUnpackStreams)
        {
            folderIndex++;
            indexInFolder = 0;
        }
    }
    return SZ_OK;
}


UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder)
{
    return p->dataPos +
        p->PackStreamStartPositions[p->FolderStartPackStreamIndex[folderIndex] + indexInFolder];
}

int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize)
{
    UInt32 packStreamIndex = p->FolderStartPackStreamIndex[folderIndex];
    CSzFolder *folder = p->db.Folders + folderIndex;
    UInt64 size = 0;
    UInt32 i;
    for (i = 0; i < folder->NumPackStreams; i++)
    {
        UInt64 t = size + p->db.PackSizes[packStreamIndex + i];
        if (t < size) /* check it */
            return SZ_ERROR_FAIL;
        size = t;
    }
    *resSize = size;
    return SZ_OK;
}


/*
SRes SzReadTime(const CObjectVector<CBuf> &dataVector,
CObjectVector<CSzFileItem> &files, UInt64 type)
{
CBoolVector boolVector;
RINOK(ReadBoolVector2(files.Size(), boolVector))

  CStreamSwitch streamSwitch;
  RINOK(streamSwitch.Set(this, &dataVector));

    for (int i = 0; i < files.Size(); i++)
    {
    CSzFileItem &file = files[i];
    CArchiveFileTime fileTime;
    bool defined = boolVector[i];
    if (defined)
    {
    UInt32 low, high;
    RINOK(SzReadUInt32(low));
    RINOK(SzReadUInt32(high));
    fileTime.dwLowDateTime = low;
    fileTime.dwHighDateTime = high;
    }
    switch(type)
    {
    case k7zIdCTime: file.IsCTimeDefined = defined; if (defined) file.CTime = fileTime; break;
    case k7zIdATime: file.IsATimeDefined = defined; if (defined) file.ATime = fileTime; break;
    case k7zIdMTime: file.IsMTimeDefined = defined; if (defined) file.MTime = fileTime; break;
    }
    }
    return SZ_OK;
    }
*/

static int TestSignatureCandidate(Byte *testBytes)
{
    size_t i;
    for (i = 0; i < k7zSignatureSize; i++)
        if (testBytes[i] != k7zSignature[i])
            return 0;
        return 1;
}

typedef struct _CSzState
{
    Byte *Data;
    size_t Size;
}CSzData;

static SRes SzReadByte(CSzData *sd, Byte *b)
{
    if (sd->Size == 0)
        return SZ_ERROR_ARCHIVE;
    sd->Size--;
    *b = *sd->Data++;
    return SZ_OK;
}

static SRes SzReadBytes(CSzData *sd, Byte *data, size_t size)
{
    size_t i;
    for (i = 0; i < size; i++)
    {
        RINOK(SzReadByte(sd, data + i));
    }
    return SZ_OK;
}

static SRes SzReadUInt32(CSzData *sd, UInt32 *value)
{
    int i;
    *value = 0;
    for (i = 0; i < 4; i++)
    {
        Byte b;
        RINOK(SzReadByte(sd, &b));
        *value |= ((UInt32)(b) << (8 * i));
    }
    return SZ_OK;
}

static SRes SzReadNumber(CSzData *sd, UInt64 *value)
{
    Byte firstByte;
    Byte mask = 0x80;
    int i;
    RINOK(SzReadByte(sd, &firstByte));
    *value = 0;
    for (i = 0; i < 8; i++)
    {
        Byte b;
        if ((firstByte & mask) == 0)
        {
            UInt64 highPart = firstByte & (mask - 1);
            *value += (highPart << (8 * i));
            return SZ_OK;
        }
        RINOK(SzReadByte(sd, &b));
        *value |= ((UInt64)b << (8 * i));
        mask >>= 1;
    }
    return SZ_OK;
}

static SRes SzReadNumber32(CSzData *sd, UInt32 *value)
{
    UInt64 value64;
    RINOK(SzReadNumber(sd, &value64));
    if (value64 >= 0x80000000)
        return SZ_ERROR_UNSUPPORTED;
    if (value64 >= ((UInt64)(1) << ((sizeof(size_t) - 1) * 8 + 2)))
        return SZ_ERROR_UNSUPPORTED;
    *value = (UInt32)value64;
    return SZ_OK;
}

static SRes SzReadID(CSzData *sd, UInt64 *value)
{
    return SzReadNumber(sd, value);
}

static SRes SzSkeepDataSize(CSzData *sd, UInt64 size)
{
    if (size > sd->Size)
        return SZ_ERROR_ARCHIVE;
    sd->Size -= (size_t)size;
    sd->Data += (size_t)size;
    return SZ_OK;
}

static SRes SzSkeepData(CSzData *sd)
{
    UInt64 size;
    RINOK(SzReadNumber(sd, &size));
    return SzSkeepDataSize(sd, size);
}

static SRes SzReadArchiveProperties(CSzData *sd)
{
    for (;;)
    {
        UInt64 type;
        RINOK(SzReadID(sd, &type));
        if (type == k7zIdEnd)
            break;
        SzSkeepData(sd);
    }
    return SZ_OK;
}

static SRes SzWaitAttribute(CSzData *sd, UInt64 attribute)
{
    for (;;)
    {
        UInt64 type;
        RINOK(SzReadID(sd, &type));
        if (type == attribute)
            return SZ_OK;
        if (type == k7zIdEnd)
            return SZ_ERROR_ARCHIVE;
        RINOK(SzSkeepData(sd));
    }
}

static SRes SzReadBoolVector(CSzData *sd, size_t numItems, Byte **v, ISzAlloc *alloc)
{
    Byte b = 0;
    Byte mask = 0;
    size_t i;
    MY_ALLOC(Byte, *v, numItems, alloc);
    for (i = 0; i < numItems; i++)
    {
        if (mask == 0)
        {
            RINOK(SzReadByte(sd, &b));
            mask = 0x80;
        }
        (*v)[i] = (Byte)(((b & mask) != 0) ? 1 : 0);
        mask >>= 1;
    }
    return SZ_OK;
}

static SRes SzReadBoolVector2(CSzData *sd, size_t numItems, Byte **v, ISzAlloc *alloc)
{
    Byte allAreDefined;
    size_t i;
    RINOK(SzReadByte(sd, &allAreDefined));
    if (allAreDefined == 0)
        return SzReadBoolVector(sd, numItems, v, alloc);
    MY_ALLOC(Byte, *v, numItems, alloc);
    for (i = 0; i < numItems; i++)
        (*v)[i] = 1;
    return SZ_OK;
}

static SRes SzReadHashDigests(
                              CSzData *sd,
                              size_t numItems,
                              Byte **digestsDefined,
                              UInt32 **digests,
                              ISzAlloc *alloc)
{
    size_t i;
    RINOK(SzReadBoolVector2(sd, numItems, digestsDefined, alloc));
    MY_ALLOC(UInt32, *digests, numItems, alloc);
    for (i = 0; i < numItems; i++)
        if ((*digestsDefined)[i])
        {
            RINOK(SzReadUInt32(sd, (*digests) + i));
        }
        return SZ_OK;
}

static SRes SzReadPackInfo(
                           CSzData *sd,
                           UInt64 *dataOffset,
                           UInt32 *numPackStreams,
                           UInt64 **packSizes,
                           Byte **packCRCsDefined,
                           UInt32 **packCRCs,
                           ISzAlloc *alloc)
{
    UInt32 i;
    RINOK(SzReadNumber(sd, dataOffset));
    RINOK(SzReadNumber32(sd, numPackStreams));

    RINOK(SzWaitAttribute(sd, k7zIdSize));

    MY_ALLOC(UInt64, *packSizes, (size_t)*numPackStreams, alloc);

    for (i = 0; i < *numPackStreams; i++)
    {
        RINOK(SzReadNumber(sd, (*packSizes) + i));
    }

    for (;;)
    {
        UInt64 type;
        RINOK(SzReadID(sd, &type));
        if (type == k7zIdEnd)
            break;
        if (type == k7zIdCRC)
        {
            RINOK(SzReadHashDigests(sd, (size_t)*numPackStreams, packCRCsDefined, packCRCs, alloc));
            continue;
        }
        RINOK(SzSkeepData(sd));
    }
    if (*packCRCsDefined == 0)
    {
        MY_ALLOC(Byte, *packCRCsDefined, (size_t)*numPackStreams, alloc);
        MY_ALLOC(UInt32, *packCRCs, (size_t)*numPackStreams, alloc);
        for (i = 0; i < *numPackStreams; i++)
        {
            (*packCRCsDefined)[i] = 0;
            (*packCRCs)[i] = 0;
        }
    }
    return SZ_OK;
}

static SRes SzReadSwitch(CSzData *sd)
{
    Byte external;
    RINOK(SzReadByte(sd, &external));
    return (external == 0) ? SZ_OK: SZ_ERROR_UNSUPPORTED;
}

static SRes SzGetNextFolderItem(CSzData *sd, CSzFolder *folder, ISzAlloc *alloc)
{
    UInt32 numCoders, numBindPairs, numPackStreams, i;
    UInt32 numInStreams = 0, numOutStreams = 0;

    RINOK(SzReadNumber32(sd, &numCoders));
    if (numCoders > NUM_FOLDER_CODERS_MAX)
        return SZ_ERROR_UNSUPPORTED;
    folder->NumCoders = numCoders;

    MY_ALLOC(CSzCoderInfo, folder->Coders, (size_t)numCoders, alloc);

    for (i = 0; i < numCoders; i++)
        SzCoderInfo_Init(folder->Coders + i);

    for (i = 0; i < numCoders; i++)
    {
        Byte mainByte;
        CSzCoderInfo *coder = folder->Coders + i;
        {
            unsigned idSize, j;
            Byte longID[15];
            RINOK(SzReadByte(sd, &mainByte));
            idSize = (unsigned)(mainByte & 0xF);
            RINOK(SzReadBytes(sd, longID, idSize));
            if (idSize > sizeof(coder->MethodID))
                return SZ_ERROR_UNSUPPORTED;
            coder->MethodID = 0;
            for (j = 0; j < idSize; j++)
                coder->MethodID |= (UInt64)longID[idSize - 1 - j] << (8 * j);

            if ((mainByte & 0x10) != 0)
            {
                RINOK(SzReadNumber32(sd, &coder->NumInStreams));
                RINOK(SzReadNumber32(sd, &coder->NumOutStreams));
                if (coder->NumInStreams > NUM_CODER_STREAMS_MAX ||
                    coder->NumOutStreams > NUM_CODER_STREAMS_MAX)
                    return SZ_ERROR_UNSUPPORTED;
            }
            else
            {
                coder->NumInStreams = 1;
                coder->NumOutStreams = 1;
            }
            if ((mainByte & 0x20) != 0)
            {
                UInt64 propertiesSize = 0;
                RINOK(SzReadNumber(sd, &propertiesSize));
                if (!Buf_Create(&coder->Props, (size_t)propertiesSize, alloc))
                    return SZ_ERROR_MEM;
                RINOK(SzReadBytes(sd, coder->Props.data, (size_t)propertiesSize));
            }
        }
        while ((mainByte & 0x80) != 0)
        {
            RINOK(SzReadByte(sd, &mainByte));
            RINOK(SzSkeepDataSize(sd, (mainByte & 0xF)));
            if ((mainByte & 0x10) != 0)
            {
                UInt32 n;
                RINOK(SzReadNumber32(sd, &n));
                RINOK(SzReadNumber32(sd, &n));
            }
            if ((mainByte & 0x20) != 0)
            {
                UInt64 propertiesSize = 0;
                RINOK(SzReadNumber(sd, &propertiesSize));
                RINOK(SzSkeepDataSize(sd, propertiesSize));
            }
        }
        numInStreams += coder->NumInStreams;
        numOutStreams += coder->NumOutStreams;
    }

    if (numOutStreams == 0)
        return SZ_ERROR_UNSUPPORTED;

    folder->NumBindPairs = numBindPairs = numOutStreams - 1;
    MY_ALLOC(CSzBindPair, folder->BindPairs, (size_t)numBindPairs, alloc);

    for (i = 0; i < numBindPairs; i++)
    {
        CSzBindPair *bp = folder->BindPairs + i;
        RINOK(SzReadNumber32(sd, &bp->InIndex));
        RINOK(SzReadNumber32(sd, &bp->OutIndex));
    }

    if (numInStreams < numBindPairs)
        return SZ_ERROR_UNSUPPORTED;

    folder->NumPackStreams = numPackStreams = numInStreams - numBindPairs;
    MY_ALLOC(UInt32, folder->PackStreams, (size_t)numPackStreams, alloc);

    if (numPackStreams == 1)
    {
        for (i = 0; i < numInStreams ; i++)
            if (SzFolder_FindBindPairForInStream(folder, i) < 0)
                break;
            if (i == numInStreams)
                return SZ_ERROR_UNSUPPORTED;
            folder->PackStreams[0] = i;
    }
    else
        for (i = 0; i < numPackStreams; i++)
        {
            RINOK(SzReadNumber32(sd, folder->PackStreams + i));
        }
        return SZ_OK;
}

static SRes SzReadUnpackInfo(
                             CSzData *sd,
                             UInt32 *numFolders,
                             CSzFolder **folders,  /* for alloc */
                             ISzAlloc *alloc,
                             ISzAlloc *allocTemp)
{
    UInt32 i;
    RINOK(SzWaitAttribute(sd, k7zIdFolder));
    RINOK(SzReadNumber32(sd, numFolders));
    {
        RINOK(SzReadSwitch(sd));

        MY_ALLOC(CSzFolder, *folders, (size_t)*numFolders, alloc);

        for (i = 0; i < *numFolders; i++)
            SzFolder_Init((*folders) + i);

        for (i = 0; i < *numFolders; i++)
        {
            RINOK(SzGetNextFolderItem(sd, (*folders) + i, alloc));
        }
    }

    RINOK(SzWaitAttribute(sd, k7zIdCodersUnpackSize));

    for (i = 0; i < *numFolders; i++)
    {
        UInt32 j;
        CSzFolder *folder = (*folders) + i;
        UInt32 numOutStreams = SzFolder_GetNumOutStreams(folder);

        MY_ALLOC(UInt64, folder->UnpackSizes, (size_t)numOutStreams, alloc);

        for (j = 0; j < numOutStreams; j++)
        {
            RINOK(SzReadNumber(sd, folder->UnpackSizes + j));
        }
    }

    for (;;)
    {
        UInt64 type;
        RINOK(SzReadID(sd, &type));
        if (type == k7zIdEnd)
            return SZ_OK;
        if (type == k7zIdCRC)
        {
            SRes res;
            Byte *crcsDefined = 0;
            UInt32 *crcs = 0;
            res = SzReadHashDigests(sd, *numFolders, &crcsDefined, &crcs, allocTemp);
            if (res == SZ_OK)
            {
                for (i = 0; i < *numFolders; i++)
                {
                    CSzFolder *folder = (*folders) + i;
                    folder->UnpackCRCDefined = crcsDefined[i];
                    folder->UnpackCRC = crcs[i];
                }
            }
            IAlloc_Free(allocTemp, crcs);
            IAlloc_Free(allocTemp, crcsDefined);
            RINOK(res);
            continue;
        }
        RINOK(SzSkeepData(sd));
    }
}

static SRes SzReadSubStreamsInfo(
                                 CSzData *sd,
                                 UInt32 numFolders,
                                 CSzFolder *folders,
                                 UInt32 *numUnpackStreams,
                                 UInt64 **unpackSizes,
                                 Byte **digestsDefined,
                                 UInt32 **digests,
                                 ISzAlloc *allocTemp)
{
    UInt64 type = 0;
    UInt32 i;
    UInt32 si = 0;
    UInt32 numDigests = 0;

    for (i = 0; i < numFolders; i++)
        folders[i].NumUnpackStreams = 1;
    *numUnpackStreams = numFolders;

    for (;;)
    {
        RINOK(SzReadID(sd, &type));
        if (type == k7zIdNumUnpackStream)
        {
            *numUnpackStreams = 0;
            for (i = 0; i < numFolders; i++)
            {
                UInt32 numStreams;
                RINOK(SzReadNumber32(sd, &numStreams));
                folders[i].NumUnpackStreams = numStreams;
                *numUnpackStreams += numStreams;
            }
            continue;
        }
        if (type == k7zIdCRC || type == k7zIdSize)
            break;
        if (type == k7zIdEnd)
            break;
        RINOK(SzSkeepData(sd));
    }

    if (*numUnpackStreams == 0)
    {
        *unpackSizes = 0;
        *digestsDefined = 0;
        *digests = 0;
    }
    else
    {
        *unpackSizes = (UInt64 *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(UInt64));
        RINOM(*unpackSizes);
        *digestsDefined = (Byte *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(Byte));
        RINOM(*digestsDefined);
        *digests = (UInt32 *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(UInt32));
        RINOM(*digests);
    }

    for (i = 0; i < numFolders; i++)
    {
    /*
    v3.13 incorrectly worked with empty folders
    v4.07: we check that folder is empty
        */
        UInt64 sum = 0;
        UInt32 j;
        UInt32 numSubstreams = folders[i].NumUnpackStreams;
        if (numSubstreams == 0)
            continue;
        if (type == k7zIdSize)
            for (j = 1; j < numSubstreams; j++)
            {
                UInt64 size;
                RINOK(SzReadNumber(sd, &size));
                (*unpackSizes)[si++] = size;
                sum += size;
            }
            (*unpackSizes)[si++] = SzFolder_GetUnpackSize(folders + i) - sum;
    }
    if (type == k7zIdSize)
    {
        RINOK(SzReadID(sd, &type));
    }

    for (i = 0; i < *numUnpackStreams; i++)
    {
        (*digestsDefined)[i] = 0;
        (*digests)[i] = 0;
    }


    for (i = 0; i < numFolders; i++)
    {
        UInt32 numSubstreams = folders[i].NumUnpackStreams;
        if (numSubstreams != 1 || !folders[i].UnpackCRCDefined)
            numDigests += numSubstreams;
    }


    si = 0;
    for (;;)
    {
        if (type == k7zIdCRC)
        {
            int digestIndex = 0;
            Byte *digestsDefined2 = 0;
            UInt32 *digests2 = 0;
            SRes res = SzReadHashDigests(sd, numDigests, &digestsDefined2, &digests2, allocTemp);
            if (res == SZ_OK)
            {
                for (i = 0; i < numFolders; i++)
                {
                    CSzFolder *folder = folders + i;
                    UInt32 numSubstreams = folder->NumUnpackStreams;
                    if (numSubstreams == 1 && folder->UnpackCRCDefined)
                    {
                        (*digestsDefined)[si] = 1;
                        (*digests)[si] = folder->UnpackCRC;
                        si++;
                    }
                    else
                    {
                        UInt32 j;
                        for (j = 0; j < numSubstreams; j++, digestIndex++)
                        {
                            (*digestsDefined)[si] = digestsDefined2[digestIndex];
                            (*digests)[si] = digests2[digestIndex];
                            si++;
                        }
                    }
                }
            }
            IAlloc_Free(allocTemp, digestsDefined2);
            IAlloc_Free(allocTemp, digests2);
            RINOK(res);
        }
        else if (type == k7zIdEnd)
            return SZ_OK;
        else
        {
            RINOK(SzSkeepData(sd));
        }
        RINOK(SzReadID(sd, &type));
    }
}


static SRes SzReadStreamsInfo(
                              CSzData *sd,
                              UInt64 *dataOffset,
                              CSzAr *p,
                              UInt32 *numUnpackStreams,
                              UInt64 **unpackSizes, /* allocTemp */
                              Byte **digestsDefined,   /* allocTemp */
                              UInt32 **digests,        /* allocTemp */
                              ISzAlloc *alloc,
                              ISzAlloc *allocTemp)
{
    for (;;)
    {
        UInt64 type;
        RINOK(SzReadID(sd, &type));
        if ((UInt64)(int)type != type)
            return SZ_ERROR_UNSUPPORTED;
        switch((int)type)
        {
        case k7zIdEnd:
            return SZ_OK;
        case k7zIdPackInfo:
            {
                RINOK(SzReadPackInfo(sd, dataOffset, &p->NumPackStreams,
                    &p->PackSizes, &p->PackCRCsDefined, &p->PackCRCs, alloc));
                break;
            }
        case k7zIdUnpackInfo:
            {
                RINOK(SzReadUnpackInfo(sd, &p->NumFolders, &p->Folders, alloc, allocTemp));
                break;
            }
        case k7zIdSubStreamsInfo:
            {
                RINOK(SzReadSubStreamsInfo(sd, p->NumFolders, p->Folders,
                    numUnpackStreams, unpackSizes, digestsDefined, digests, allocTemp));
                break;
            }
        default:
            return SZ_ERROR_UNSUPPORTED;
        }
    }
}

size_t SzArEx_GetFileNameUtf16(const CSzArEx *p, size_t fileIndex, UInt16 *dest)
{
    size_t len = p->FileNameOffsets[fileIndex + 1] - p->FileNameOffsets[fileIndex];
    if (dest != 0)
    {
        size_t i;
        const Byte *src = p->FileNames.data + (p->FileNameOffsets[fileIndex] * 2);
        for (i = 0; i < len; i++)
            dest[i] = GetUi16(src + i * 2);
    }
    return len;
}

static SRes SzReadFileNames(const Byte *p, size_t size, UInt32 numFiles, size_t *sizes)
{
    UInt32 i;
    size_t pos = 0;
    for (i = 0; i < numFiles; i++)
    {
        sizes[i] = pos;
        for (;;)
        {
            if (pos >= size)
                return SZ_ERROR_ARCHIVE;
            if (p[pos * 2] == 0 && p[pos * 2 + 1] == 0)
                break;
            pos++;
        }
        pos++;
    }
    sizes[i] = pos;
    return (pos == size) ? SZ_OK : SZ_ERROR_ARCHIVE;
}

static SRes SzReadHeader2(
                          CSzArEx *p,   /* allocMain */
                          CSzData *sd,
                          UInt64 **unpackSizes,  /* allocTemp */
                          Byte **digestsDefined,    /* allocTemp */
                          UInt32 **digests,         /* allocTemp */
                          Byte **emptyStreamVector, /* allocTemp */
                          Byte **emptyFileVector,   /* allocTemp */
                          Byte **lwtVector,         /* allocTemp */
                          ISzAlloc *allocMain,
                          ISzAlloc *allocTemp)
{
    UInt64 type;
    UInt32 numUnpackStreams = 0;
    UInt32 numFiles = 0;
    CSzFileItem *files = 0;
    UInt32 numEmptyStreams = 0;
    UInt32 i;

    RINOK(SzReadID(sd, &type));

    if (type == k7zIdArchiveProperties)
    {
        RINOK(SzReadArchiveProperties(sd));
        RINOK(SzReadID(sd, &type));
    }


    if (type == k7zIdMainStreamsInfo)
    {
        RINOK(SzReadStreamsInfo(sd,
            &p->dataPos,
            &p->db,
            &numUnpackStreams,
            unpackSizes,
            digestsDefined,
            digests, allocMain, allocTemp));
        p->dataPos += p->startPosAfterHeader;
        RINOK(SzReadID(sd, &type));
    }

    if (type == k7zIdEnd)
        return SZ_OK;
    if (type != k7zIdFilesInfo)
        return SZ_ERROR_ARCHIVE;

    RINOK(SzReadNumber32(sd, &numFiles));
    p->db.NumFiles = numFiles;

    MY_ALLOC(CSzFileItem, files, (size_t)numFiles, allocMain);

    p->db.Files = files;
    for (i = 0; i < numFiles; i++)
        SzFile_Init(files + i);

    for (;;)
    {
        UInt64 type;
        UInt64 size;
        RINOK(SzReadID(sd, &type));
        if (type == k7zIdEnd)
            break;
        RINOK(SzReadNumber(sd, &size));
        if (size > sd->Size)
            return SZ_ERROR_ARCHIVE;
        if ((UInt64)(int)type != type)
        {
            RINOK(SzSkeepDataSize(sd, size));
        }
        else
            switch((int)type)
        {
      case k7zIdName:
          {
              size_t namesSize;
              RINOK(SzReadSwitch(sd));
              namesSize = (size_t)size - 1;
              if ((namesSize & 1) != 0)
                  return SZ_ERROR_ARCHIVE;
              if (!Buf_Create(&p->FileNames, namesSize, allocMain))
                  return SZ_ERROR_MEM;
              MY_ALLOC(size_t, p->FileNameOffsets, numFiles + 1, allocMain);
              memcpy(p->FileNames.data, sd->Data, namesSize);
              RINOK(SzReadFileNames(sd->Data, namesSize >> 1, numFiles, p->FileNameOffsets))
                  RINOK(SzSkeepDataSize(sd, namesSize));
              break;
          }
      case k7zIdEmptyStream:
          {
              RINOK(SzReadBoolVector(sd, numFiles, emptyStreamVector, allocTemp));
              numEmptyStreams = 0;
              for (i = 0; i < numFiles; i++)
                  if ((*emptyStreamVector)[i])
                      numEmptyStreams++;
                  break;
          }
      case k7zIdEmptyFile:
          {
              RINOK(SzReadBoolVector(sd, numEmptyStreams, emptyFileVector, allocTemp));
              break;
          }
      case k7zIdWinAttributes:
          {
              RINOK(SzReadBoolVector2(sd, numFiles, lwtVector, allocTemp));
              RINOK(SzReadSwitch(sd));
              for (i = 0; i < numFiles; i++)
              {
                  CSzFileItem *f = &files[i];
                  Byte defined = (*lwtVector)[i];
                  f->AttribDefined = defined;
                  f->Attrib = 0;
                  if (defined)
                  {
                      RINOK(SzReadUInt32(sd, &f->Attrib));
                  }
              }
              IAlloc_Free(allocTemp, *lwtVector);
              *lwtVector = NULL;
              break;
          }
      case k7zIdMTime:
          {
              RINOK(SzReadBoolVector2(sd, numFiles, lwtVector, allocTemp));
              RINOK(SzReadSwitch(sd));
              for (i = 0; i < numFiles; i++)
              {
                  CSzFileItem *f = &files[i];
                  Byte defined = (*lwtVector)[i];
                  f->MTimeDefined = defined;
                  f->MTime.Low = f->MTime.High = 0;
                  if (defined)
                  {
                      RINOK(SzReadUInt32(sd, &f->MTime.Low));
                      RINOK(SzReadUInt32(sd, &f->MTime.High));
                  }
              }
              IAlloc_Free(allocTemp, *lwtVector);
              *lwtVector = NULL;
              break;
          }
      default:
          {
              RINOK(SzSkeepDataSize(sd, size));
          }
        }
    }

    {
        UInt32 emptyFileIndex = 0;
        UInt32 sizeIndex = 0;
        for (i = 0; i < numFiles; i++)
        {
            CSzFileItem *file = files + i;
            file->IsAnti = 0;
            if (*emptyStreamVector == 0)
                file->HasStream = 1;
            else
                file->HasStream = (Byte)((*emptyStreamVector)[i] ? 0 : 1);
            if (file->HasStream)
            {
                file->IsDir = 0;
                file->Size = (*unpackSizes)[sizeIndex];
                file->Crc = (*digests)[sizeIndex];
                file->CrcDefined = (Byte)(*digestsDefined)[sizeIndex];
                sizeIndex++;
            }
            else
            {
                if (*emptyFileVector == 0)
                    file->IsDir = 1;
                else
                    file->IsDir = (Byte)((*emptyFileVector)[emptyFileIndex] ? 0 : 1);
                emptyFileIndex++;
                file->Size = 0;
                file->Crc = 0;
                file->CrcDefined = 0;
            }
        }
    }
    return SzArEx_Fill(p, allocMain);
}

static SRes SzReadHeader(
                         CSzArEx *p,
                         CSzData *sd,
                         ISzAlloc *allocMain,
                         ISzAlloc *allocTemp)
{
    UInt64 *unpackSizes = 0;
    Byte *digestsDefined = 0;
    UInt32 *digests = 0;
    Byte *emptyStreamVector = 0;
    Byte *emptyFileVector = 0;
    Byte *lwtVector = 0;
    SRes res = SzReadHeader2(p, sd,
        &unpackSizes, &digestsDefined, &digests,
        &emptyStreamVector, &emptyFileVector, &lwtVector,
        allocMain, allocTemp);
    IAlloc_Free(allocTemp, unpackSizes);
    IAlloc_Free(allocTemp, digestsDefined);
    IAlloc_Free(allocTemp, digests);
    IAlloc_Free(allocTemp, emptyStreamVector);
    IAlloc_Free(allocTemp, emptyFileVector);
    IAlloc_Free(allocTemp, lwtVector);
    return res;
}

static SRes SzReadAndDecodePackedStreams2(
                                          ILookInStream *inStream,
                                          CSzData *sd,
                                          CBuf *outBuffer,
                                          UInt64 baseOffset,
                                          CSzAr *p,
                                          UInt64 **unpackSizes,
                                          Byte **digestsDefined,
                                          UInt32 **digests,
                                          ISzAlloc *allocTemp)
{

    UInt32 numUnpackStreams = 0;
    UInt64 dataStartPos;
    CSzFolder *folder;
    UInt64 unpackSize;
    SRes res;

    RINOK(SzReadStreamsInfo(sd, &dataStartPos, p,
        &numUnpackStreams,  unpackSizes, digestsDefined, digests,
        allocTemp, allocTemp));

    dataStartPos += baseOffset;
    if (p->NumFolders != 1)
        return SZ_ERROR_ARCHIVE;

    folder = p->Folders;
    unpackSize = SzFolder_GetUnpackSize(folder);

    RINOK(LookInStream_SeekTo(inStream, dataStartPos));

    if (!Buf_Create(outBuffer, (size_t)unpackSize, allocTemp))
        return SZ_ERROR_MEM;

    res = SzFolder_Decode(folder, p->PackSizes,
        inStream, dataStartPos,
        outBuffer->data, (size_t)unpackSize, allocTemp);
    RINOK(res);
    if (folder->UnpackCRCDefined)
        if (CrcCalc(outBuffer->data, (size_t)unpackSize) != folder->UnpackCRC)
            return SZ_ERROR_CRC;
        return SZ_OK;
}

static SRes SzReadAndDecodePackedStreams(
                                         ILookInStream *inStream,
                                         CSzData *sd,
                                         CBuf *outBuffer,
                                         UInt64 baseOffset,
                                         ISzAlloc *allocTemp)
{
    CSzAr p;
    UInt64 *unpackSizes = 0;
    Byte *digestsDefined = 0;
    UInt32 *digests = 0;
    SRes res;
    SzAr_Init(&p);
    res = SzReadAndDecodePackedStreams2(inStream, sd, outBuffer, baseOffset,
        &p, &unpackSizes, &digestsDefined, &digests,
        allocTemp);
    SzAr_Free(&p, allocTemp);
    IAlloc_Free(allocTemp, unpackSizes);
    IAlloc_Free(allocTemp, digestsDefined);
    IAlloc_Free(allocTemp, digests);
    return res;
}

static SRes SzArEx_Open2(
                         CSzArEx *p,
                         ILookInStream *inStream,
                         ISzAlloc *allocMain,
                         ISzAlloc *allocTemp)
{
    Byte header[k7zStartHeaderSize];
    Int64 startArcPos;
    UInt64 nextHeaderOffset, nextHeaderSize;
    size_t nextHeaderSizeT;
    UInt32 nextHeaderCRC;
    CBuf buffer;
    SRes res;

    startArcPos = 0;
    RINOK(inStream->Seek(inStream, &startArcPos, SZ_SEEK_CUR));

    RINOK(LookInStream_Read2(inStream, header, k7zStartHeaderSize, SZ_ERROR_NO_ARCHIVE));

    if (!TestSignatureCandidate(header))
        return SZ_ERROR_NO_ARCHIVE;
    if (header[6] != k7zMajorVersion)
        return SZ_ERROR_UNSUPPORTED;

    nextHeaderOffset = GetUi64(header + 12);
    nextHeaderSize = GetUi64(header + 20);
    nextHeaderCRC = GetUi32(header + 28);

    p->startPosAfterHeader = startArcPos + k7zStartHeaderSize;

    if (CrcCalc(header + 12, 20) != GetUi32(header + 8))
        return SZ_ERROR_CRC;

    nextHeaderSizeT = (size_t)nextHeaderSize;
    if (nextHeaderSizeT != nextHeaderSize)
        return SZ_ERROR_MEM;
    if (nextHeaderSizeT == 0)
        return SZ_OK;
    if (nextHeaderOffset > nextHeaderOffset + nextHeaderSize ||
        nextHeaderOffset > nextHeaderOffset + nextHeaderSize + k7zStartHeaderSize)
        return SZ_ERROR_NO_ARCHIVE;

    {
        Int64 pos = 0;
        RINOK(inStream->Seek(inStream, &pos, SZ_SEEK_END));
        if ((UInt64)pos < startArcPos + nextHeaderOffset ||
            (UInt64)pos < startArcPos + k7zStartHeaderSize + nextHeaderOffset ||
            (UInt64)pos < startArcPos + k7zStartHeaderSize + nextHeaderOffset + nextHeaderSize)
            return SZ_ERROR_INPUT_EOF;
    }

    RINOK(LookInStream_SeekTo(inStream, startArcPos + k7zStartHeaderSize + nextHeaderOffset));

    if (!Buf_Create(&buffer, nextHeaderSizeT, allocTemp))
        return SZ_ERROR_MEM;

    res = LookInStream_Read(inStream, buffer.data, nextHeaderSizeT);
    if (res == SZ_OK)
    {
        res = SZ_ERROR_ARCHIVE;
        if (CrcCalc(buffer.data, nextHeaderSizeT) == nextHeaderCRC)
        {
            CSzData sd;
            UInt64 type;
            sd.Data = buffer.data;
            sd.Size = buffer.size;
            res = SzReadID(&sd, &type);
            if (res == SZ_OK)
            {
                if (type == k7zIdEncodedHeader)
                {
                    CBuf outBuffer;
                    Buf_Init(&outBuffer);
                    res = SzReadAndDecodePackedStreams(inStream, &sd, &outBuffer, p->startPosAfterHeader, allocTemp);
                    if (res != SZ_OK)
                        Buf_Free(&outBuffer, allocTemp);
                    else
                    {
                        Buf_Free(&buffer, allocTemp);
                        buffer.data = outBuffer.data;
                        buffer.size = outBuffer.size;
                        sd.Data = buffer.data;
                        sd.Size = buffer.size;
                        res = SzReadID(&sd, &type);
                    }
                }
            }
            if (res == SZ_OK)
            {
                if (type == k7zIdHeader)
                    res = SzReadHeader(p, &sd, allocMain, allocTemp);
                else
                    res = SZ_ERROR_UNSUPPORTED;
            }
        }
    }
    Buf_Free(&buffer, allocTemp);
    return res;
}

SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, ISzAlloc *allocMain, ISzAlloc *allocTemp)
{
    SRes res = SzArEx_Open2(p, inStream, allocMain, allocTemp);
    if (res != SZ_OK)
        SzArEx_Free(p, allocMain);
    return res;
}

SRes SzArEx_Extract(
                    const CSzArEx *p,
                    ILookInStream *inStream,
                    UInt32 fileIndex,
                    UInt32 *blockIndex,
                    Byte **outBuffer,
                    size_t *outBufferSize,
                    size_t *offset,
                    size_t *outSizeProcessed,
                    ISzAlloc *allocMain,
                    ISzAlloc *allocTemp)
{
    UInt32 folderIndex = p->FileIndexToFolderIndexMap[fileIndex];
    SRes res = SZ_OK;
    *offset = 0;
    *outSizeProcessed = 0;
    if (folderIndex == (UInt32)-1)
    {
        IAlloc_Free(allocMain, *outBuffer);
        *blockIndex = folderIndex;
        *outBuffer = 0;
        *outBufferSize = 0;
        return SZ_OK;
    }

    if (*outBuffer == 0 || *blockIndex != folderIndex)
    {
        CSzFolder *folder = p->db.Folders + folderIndex;
        UInt64 unpackSizeSpec = SzFolder_GetUnpackSize(folder);
        size_t unpackSize = (size_t)unpackSizeSpec;
        UInt64 startOffset = SzArEx_GetFolderStreamPos(p, folderIndex, 0);

        if (unpackSize != unpackSizeSpec)
            return SZ_ERROR_MEM;
        *blockIndex = folderIndex;
        IAlloc_Free(allocMain, *outBuffer);
        *outBuffer = 0;

        RINOK(LookInStream_SeekTo(inStream, startOffset));

        if (res == SZ_OK)
        {
            *outBufferSize = unpackSize;
            if (unpackSize != 0)
            {
                *outBuffer = (Byte *)IAlloc_Alloc(allocMain, unpackSize);
                if (*outBuffer == 0)
                    res = SZ_ERROR_MEM;
            }
            if (res == SZ_OK)
            {
                res = SzFolder_Decode(folder,
                    p->db.PackSizes + p->FolderStartPackStreamIndex[folderIndex],
                    inStream, startOffset,
                    *outBuffer, unpackSize, allocTemp);
                if (res == SZ_OK)
                {
                    if (folder->UnpackCRCDefined)
                    {
                        if (CrcCalc(*outBuffer, unpackSize) != folder->UnpackCRC)
                            res = SZ_ERROR_CRC;
                    }
                }
            }
        }
    }
    if (res == SZ_OK)
    {
        UInt32 i;
        CSzFileItem *fileItem = p->db.Files + fileIndex;
        *offset = 0;
        for (i = p->FolderStartFileIndex[folderIndex]; i < fileIndex; i++)
            *offset += (UInt32)p->db.Files[i].Size;
        *outSizeProcessed = (size_t)fileItem->Size;
        if (*offset + *outSizeProcessed > *outBufferSize)
            return SZ_ERROR_FAIL;
        if (fileItem->CrcDefined && CrcCalc(*outBuffer + *offset, *outSizeProcessed) != fileItem->Crc)
            res = SZ_ERROR_CRC;
    }
    return res;
}
#ifdef __cplusplus
extern "C" {
#endif

    /* ---------- State Interface ---------- */

    typedef struct
    {
        CLzmaDec decoder;
        UInt32 packSize;
        UInt32 unpackSize;
        int state;
        Byte control;
        Bool needInitDic;
        Bool needInitState;
        Bool needInitProp;
    } CLzma2Dec;

#define Lzma2Dec_Construct(p) LzmaDec_Construct(&(p)->decoder)
#define Lzma2Dec_FreeProbs(p, alloc) LzmaDec_FreeProbs(&(p)->decoder, alloc);
#define Lzma2Dec_Free(p, alloc) LzmaDec_Free(&(p)->decoder, alloc);

    SRes Lzma2Dec_AllocateProbs(CLzma2Dec *p, Byte prop, ISzAlloc *alloc);
    SRes Lzma2Dec_Allocate(CLzma2Dec *p, Byte prop, ISzAlloc *alloc);
    void Lzma2Dec_Init(CLzma2Dec *p);


    /*
    finishMode:
    It has meaning only if the decoding reaches output limit (*destLen or dicLimit).
    LZMA_FINISH_ANY - use smallest number of input bytes
    LZMA_FINISH_END - read EndOfStream marker after decoding

      Returns:
      SZ_OK
      status:
      LZMA_STATUS_FINISHED_WITH_MARK
      LZMA_STATUS_NOT_FINISHED
      LZMA_STATUS_NEEDS_MORE_INPUT
      SZ_ERROR_DATA - Data error
    */

    SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit,
        const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);

    SRes Lzma2Dec_DecodeToBuf(CLzma2Dec *p, Byte *dest, SizeT *destLen,
        const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);


    /* ---------- One Call Interface ---------- */

    /*
    finishMode:
    It has meaning only if the decoding reaches output limit (*destLen).
    LZMA_FINISH_ANY - use smallest number of input bytes
    LZMA_FINISH_END - read EndOfStream marker after decoding

      Returns:
      SZ_OK
      status:
      LZMA_STATUS_FINISHED_WITH_MARK
      LZMA_STATUS_NOT_FINISHED
      SZ_ERROR_DATA - Data error
      SZ_ERROR_MEM  - Memory allocation error
      SZ_ERROR_UNSUPPORTED - Unsupported properties
      SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src).
    */

    SRes Lzma2Decode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
        Byte prop, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc);

#ifdef __cplusplus
}
#endif
#ifdef SHOW_DEBUG_INFO
#include <stdio.h>
#endif

/*
00000000  -  EOS
00000001 U U  -  Uncompressed Reset Dic
00000010 U U  -  Uncompressed No Reset
100uuuuu U U P P  -  LZMA no reset
101uuuuu U U P P  -  LZMA reset state
110uuuuu U U P P S  -  LZMA reset state + new prop
111uuuuu U U P P S  -  LZMA reset state + new prop + reset dic

  u, U - Unpack Size
  P - Pack Size
  S - Props
*/

#define LZMA2_CONTROL_LZMA (1 << 7)
#define LZMA2_CONTROL_COPY_NO_RESET 2
#define LZMA2_CONTROL_COPY_RESET_DIC 1
#define LZMA2_CONTROL_EOF 0

#define LZMA2_IS_UNCOMPRESSED_STATE(p) (((p)->control & LZMA2_CONTROL_LZMA) == 0)

#define LZMA2_GET_LZMA_MODE(p) (((p)->control >> 5) & 3)
#define LZMA2_IS_THERE_PROP(mode) ((mode) >= 2)

#define LZMA2_LCLP_MAX 4
#define LZMA2_DIC_SIZE_FROM_PROP(p) (((UInt32)2 | ((p) & 1)) << ((p) / 2 + 11))

#ifdef SHOW_DEBUG_INFO
#define PRF(x) x
#else
#define PRF(x)
#endif

typedef enum
{
    LZMA2_STATE_CONTROL,
        LZMA2_STATE_UNPACK0,
        LZMA2_STATE_UNPACK1,
        LZMA2_STATE_PACK0,
        LZMA2_STATE_PACK1,
        LZMA2_STATE_PROP,
        LZMA2_STATE_DATA,
        LZMA2_STATE_DATA_CONT,
        LZMA2_STATE_FINISHED,
        LZMA2_STATE_ERROR
} ELzma2State;

static SRes Lzma2Dec_GetOldProps(Byte prop, Byte *props)
{
    UInt32 dicSize;
    if (prop > 40)
        return SZ_ERROR_UNSUPPORTED;
    dicSize = (prop == 40) ? 0xFFFFFFFF : LZMA2_DIC_SIZE_FROM_PROP(prop);
    props[0] = (Byte)LZMA2_LCLP_MAX;
    props[1] = (Byte)(dicSize);
    props[2] = (Byte)(dicSize >> 8);
    props[3] = (Byte)(dicSize >> 16);
    props[4] = (Byte)(dicSize >> 24);
    return SZ_OK;
}

SRes Lzma2Dec_AllocateProbs(CLzma2Dec *p, Byte prop, ISzAlloc *alloc)
{
    Byte props[LZMA_PROPS_SIZE];
    RINOK(Lzma2Dec_GetOldProps(prop, props));
    return LzmaDec_AllocateProbs(&p->decoder, props, LZMA_PROPS_SIZE, alloc);
}

SRes Lzma2Dec_Allocate(CLzma2Dec *p, Byte prop, ISzAlloc *alloc)
{
    Byte props[LZMA_PROPS_SIZE];
    RINOK(Lzma2Dec_GetOldProps(prop, props));
    return LzmaDec_Allocate(&p->decoder, props, LZMA_PROPS_SIZE, alloc);
}

void Lzma2Dec_Init(CLzma2Dec *p)
{
    p->state = LZMA2_STATE_CONTROL;
    p->needInitDic = True;
    p->needInitState = True;
    p->needInitProp = True;
    LzmaDec_Init(&p->decoder);
}

static ELzma2State Lzma2Dec_UpdateState(CLzma2Dec *p, Byte b)
{
    switch(p->state)
    {
    case LZMA2_STATE_CONTROL:
        p->control = b;
        PRF(printf("\n %4X ", p->decoder.dicPos));
        PRF(printf(" %2X", b));
        if (p->control == 0)
            return LZMA2_STATE_FINISHED;
        if (LZMA2_IS_UNCOMPRESSED_STATE(p))
        {
            if ((p->control & 0x7F) > 2)
                return LZMA2_STATE_ERROR;
            p->unpackSize = 0;
        }
        else
            p->unpackSize = (UInt32)(p->control & 0x1F) << 16;
        return LZMA2_STATE_UNPACK0;

    case LZMA2_STATE_UNPACK0:
        p->unpackSize |= (UInt32)b << 8;
        return LZMA2_STATE_UNPACK1;

    case LZMA2_STATE_UNPACK1:
        p->unpackSize |= (UInt32)b;
        p->unpackSize++;
        PRF(printf(" %8d", p->unpackSize));
        return (LZMA2_IS_UNCOMPRESSED_STATE(p)) ? LZMA2_STATE_DATA : LZMA2_STATE_PACK0;

    case LZMA2_STATE_PACK0:
        p->packSize = (UInt32)b << 8;
        return LZMA2_STATE_PACK1;

    case LZMA2_STATE_PACK1:
        p->packSize |= (UInt32)b;
        p->packSize++;
        PRF(printf(" %8d", p->packSize));
        return LZMA2_IS_THERE_PROP(LZMA2_GET_LZMA_MODE(p)) ? LZMA2_STATE_PROP:
        (p->needInitProp ? LZMA2_STATE_ERROR : LZMA2_STATE_DATA);

    case LZMA2_STATE_PROP:
        {
            int lc, lp;
            if (b >= (9 * 5 * 5))
                return LZMA2_STATE_ERROR;
            lc = b % 9;
            b /= 9;
            p->decoder.prop.pb = b / 5;
            lp = b % 5;
            if (lc + lp > LZMA2_LCLP_MAX)
                return LZMA2_STATE_ERROR;
            p->decoder.prop.lc = lc;
            p->decoder.prop.lp = lp;
            p->needInitProp = False;
            return LZMA2_STATE_DATA;
        }
    }
    return LZMA2_STATE_ERROR;
}

static void LzmaDec_UpdateWithUncompressed(CLzmaDec *p, const Byte *src, SizeT size)
{
    memcpy(p->dic + p->dicPos, src, size);
    p->dicPos += size;
    if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= size)
        p->checkDicSize = p->prop.dicSize;
    p->processedPos += (UInt32)size;
}

void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState);

SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit,
                          const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status)
{
    SizeT inSize = *srcLen;
    *srcLen = 0;
    *status = LZMA_STATUS_NOT_SPECIFIED;

    while (p->state != LZMA2_STATE_FINISHED)
    {
        SizeT dicPos = p->decoder.dicPos;
        if (p->state == LZMA2_STATE_ERROR)
            return SZ_ERROR_DATA;
        if (dicPos == dicLimit && finishMode == LZMA_FINISH_ANY)
        {
            *status = LZMA_STATUS_NOT_FINISHED;
            return SZ_OK;
        }
        if (p->state != LZMA2_STATE_DATA && p->state != LZMA2_STATE_DATA_CONT)
        {
            if (*srcLen == inSize)
            {
                *status = LZMA_STATUS_NEEDS_MORE_INPUT;
                return SZ_OK;
            }
            (*srcLen)++;
            p->state = Lzma2Dec_UpdateState(p, *src++);
            continue;
        }
        {
            SizeT destSizeCur = dicLimit - dicPos;
            SizeT srcSizeCur = inSize - *srcLen;
            ELzmaFinishMode curFinishMode = LZMA_FINISH_ANY;

            if (p->unpackSize <= destSizeCur)
            {
                destSizeCur = (SizeT)p->unpackSize;
                curFinishMode = LZMA_FINISH_END;
            }

            if (LZMA2_IS_UNCOMPRESSED_STATE(p))
            {
                if (*srcLen == inSize)
                {
                    *status = LZMA_STATUS_NEEDS_MORE_INPUT;
                    return SZ_OK;
                }

                if (p->state == LZMA2_STATE_DATA)
                {
                    Bool initDic = (p->control == LZMA2_CONTROL_COPY_RESET_DIC);
                    if (initDic)
                        p->needInitProp = p->needInitState = True;
                    else if (p->needInitDic)
                        return SZ_ERROR_DATA;
                    p->needInitDic = False;
                    LzmaDec_InitDicAndState(&p->decoder, initDic, False);
                }

                if (srcSizeCur > destSizeCur)
                    srcSizeCur = destSizeCur;

                if (srcSizeCur == 0)
                    return SZ_ERROR_DATA;

                LzmaDec_UpdateWithUncompressed(&p->decoder, src, srcSizeCur);

                src += srcSizeCur;
                *srcLen += srcSizeCur;
                p->unpackSize -= (UInt32)srcSizeCur;
                p->state = (p->unpackSize == 0) ? LZMA2_STATE_CONTROL : LZMA2_STATE_DATA_CONT;
            }
            else
            {
                SizeT outSizeProcessed;
                SRes res;

                if (p->state == LZMA2_STATE_DATA)
                {
                    int mode = LZMA2_GET_LZMA_MODE(p);
                    Bool initDic = (mode == 3);
                    Bool initState = (mode > 0);
                    if ((!initDic && p->needInitDic) || (!initState && p->needInitState))
                        return SZ_ERROR_DATA;

                    LzmaDec_InitDicAndState(&p->decoder, initDic, initState);
                    p->needInitDic = False;
                    p->needInitState = False;
                    p->state = LZMA2_STATE_DATA_CONT;
                }
                if (srcSizeCur > p->packSize)
                    srcSizeCur = (SizeT)p->packSize;

                res = LzmaDec_DecodeToDic(&p->decoder, dicPos + destSizeCur, src, &srcSizeCur, curFinishMode, status);

                src += srcSizeCur;
                *srcLen += srcSizeCur;
                p->packSize -= (UInt32)srcSizeCur;

                outSizeProcessed = p->decoder.dicPos - dicPos;
                p->unpackSize -= (UInt32)outSizeProcessed;

                RINOK(res);
                if (*status == LZMA_STATUS_NEEDS_MORE_INPUT)
                    return res;

                if (srcSizeCur == 0 && outSizeProcessed == 0)
                {
                    if (*status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK ||
                        p->unpackSize != 0 || p->packSize != 0)
                        return SZ_ERROR_DATA;
                    p->state = LZMA2_STATE_CONTROL;
                }
                if (*status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)
                    *status = LZMA_STATUS_NOT_FINISHED;
            }
        }
  }
  *status = LZMA_STATUS_FINISHED_WITH_MARK;
  return SZ_OK;
}

SRes Lzma2Dec_DecodeToBuf(CLzma2Dec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status)
{
    SizeT outSize = *destLen, inSize = *srcLen;
    *srcLen = *destLen = 0;
    for (;;)
    {
        SizeT srcSizeCur = inSize, outSizeCur, dicPos;
        ELzmaFinishMode curFinishMode;
        SRes res;
        if (p->decoder.dicPos == p->decoder.dicBufSize)
            p->decoder.dicPos = 0;
        dicPos = p->decoder.dicPos;
        if (outSize > p->decoder.dicBufSize - dicPos)
        {
            outSizeCur = p->decoder.dicBufSize;
            curFinishMode = LZMA_FINISH_ANY;
        }
        else
        {
            outSizeCur = dicPos + outSize;
            curFinishMode = finishMode;
        }

        res = Lzma2Dec_DecodeToDic(p, outSizeCur, src, &srcSizeCur, curFinishMode, status);
        src += srcSizeCur;
        inSize -= srcSizeCur;
        *srcLen += srcSizeCur;
        outSizeCur = p->decoder.dicPos - dicPos;
        memcpy(dest, p->decoder.dic + dicPos, outSizeCur);
        dest += outSizeCur;
        outSize -= outSizeCur;
        *destLen += outSizeCur;
        if (res != 0)
            return res;
        if (outSizeCur == 0 || outSize == 0)
            return SZ_OK;
    }
}

SRes Lzma2Decode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
                 Byte prop, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc)
{
    CLzma2Dec decoder;
    SRes res;
    SizeT outSize = *destLen, inSize = *srcLen;
    Byte props[LZMA_PROPS_SIZE];

    Lzma2Dec_Construct(&decoder);

    *destLen = *srcLen = 0;
    *status = LZMA_STATUS_NOT_SPECIFIED;
    decoder.decoder.dic = dest;
    decoder.decoder.dicBufSize = outSize;

    RINOK(Lzma2Dec_GetOldProps(prop, props));
    RINOK(LzmaDec_AllocateProbs(&decoder.decoder, props, LZMA_PROPS_SIZE, alloc));

    *srcLen = inSize;
    res = Lzma2Dec_DecodeToDic(&decoder, outSize, src, srcLen, finishMode, status);
    *destLen = decoder.decoder.dicPos;
    if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT)
        res = SZ_ERROR_INPUT_EOF;

    LzmaDec_FreeProbs(&decoder.decoder, alloc);
    return res;
}

#define kNumTopBits 24
#define kTopValue ((UInt32)1 << kNumTopBits)

#define kNumBitModelTotalBits 11
#define kBitModelTotal (1 << kNumBitModelTotalBits)
#define kNumMoveBits 5

#define RC_INIT_SIZE 5

#define NORMALIZE_2 if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); }

#define IF_BIT_0_2(p) ttt = *(p); NORMALIZE_2; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
#define UPDATE_0_2(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits));
#define UPDATE_1_2(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits));
#define GET_BIT2(p, i, A0, A1) IF_BIT_0_2(p) \
{ UPDATE_0_2(p); i = (i + i); A0; } else \
{ UPDATE_1_2(p); i = (i + i) + 1; A1; }
#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;)

#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); }
#define TREE_DECODE(probs, limit, i) \
{ i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; }

/* #define _LZMA_SIZE_OPT */

#ifdef _LZMA_SIZE_OPT
#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i)
#else
#define TREE_6_DECODE(probs, i) \
{ i = 1; \
    TREE_GET_BIT(probs, i); \
    TREE_GET_BIT(probs, i); \
    TREE_GET_BIT(probs, i); \
    TREE_GET_BIT(probs, i); \
    TREE_GET_BIT(probs, i); \
    TREE_GET_BIT(probs, i); \
i -= 0x40; }
#endif

#define NORMALIZE_2_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); }

#define IF_BIT_0_2_CHECK(p) ttt = *(p); NORMALIZE_2_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
#define UPDATE_0_2_CHECK range = bound;
#define UPDATE_1_2_CHECK range -= bound; code -= bound;
#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_2_CHECK(p) \
{ UPDATE_0_2_CHECK; i = (i + i); A0; } else \
{ UPDATE_1_2_CHECK; i = (i + i) + 1; A1; }
#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;)
#define TREE_DECODE_CHECK(probs, limit, i) \
{ i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; }


#define kNumPosBitsMax 4
#define kNumPosStatesMax (1 << kNumPosBitsMax)

#define kLenNumLowBits 3
#define kLenNumLowSymbols (1 << kLenNumLowBits)
#define kLenNumMidBits 3
#define kLenNumMidSymbols (1 << kLenNumMidBits)
#define kLenNumHighBits 8
#define kLenNumHighSymbols (1 << kLenNumHighBits)

#define LenChoice 0
#define LenChoice2 (LenChoice + 1)
#define LenLow (LenChoice2 + 1)
#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits))
#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits))
#define kNumLenProbs (LenHigh + kLenNumHighSymbols)


#define kNumStates 12
#define kNumLitStates 7

#define kStartPosModelIndex 4
#define kEndPosModelIndex 14
#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))

#define kNumPosSlotBits 6
#define kNumLenToPosStates 4

#define kNumAlignBits 4
#define kAlignTableSize (1 << kNumAlignBits)

#define kMatchMinLen 2
#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols)

#define IsMatch 0
#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax))
#define IsRepG0 (IsRep + kNumStates)
#define IsRepG1 (IsRepG0 + kNumStates)
#define IsRepG2 (IsRepG1 + kNumStates)
#define IsRep0Long (IsRepG2 + kNumStates)
#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax))
#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex)
#define LenCoder (Align + kAlignTableSize)
#define RepLenCoder (LenCoder + kNumLenProbs)
#define Literal (RepLenCoder + kNumLenProbs)

#define LZMA_BASE_SIZE 1846
#define LZMA_LIT_SIZE 768

#define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp)))

#if Literal != LZMA_BASE_SIZE
StopCompilingDueBUG
#endif

#define LZMA_DIC_MIN (1 << 12)

/* First LZMA-symbol is always decoded.
And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization
Out:
Result:
SZ_OK - OK
SZ_ERROR_DATA - Error
p->remainLen:
< kMatchSpecLenStart : normal remain
= kMatchSpecLenStart : finished
= kMatchSpecLenStart + 1 : Flush marker
= kMatchSpecLenStart + 2 : State Init Marker
*/

static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit)
{
    CLzmaProb *probs = p->probs;

    unsigned state = p->state;
    UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3];
    unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1;
    unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1;
    unsigned lc = p->prop.lc;

    Byte *dic = p->dic;
    SizeT dicBufSize = p->dicBufSize;
    SizeT dicPos = p->dicPos;

    UInt32 processedPos = p->processedPos;
    UInt32 checkDicSize = p->checkDicSize;
    unsigned len = 0;

    const Byte *buf = p->buf;
    UInt32 range = p->range;
    UInt32 code = p->code;

    do
    {
        CLzmaProb *prob;
        UInt32 bound;
        unsigned ttt;
        unsigned posState = processedPos & pbMask;

        prob = probs + IsMatch + (state << kNumPosBitsMax) + posState;
        IF_BIT_0_2(prob)
        {
            unsigned symbol;
            UPDATE_0_2(prob);
            prob = probs + Literal;
            if (checkDicSize != 0 || processedPos != 0)
                prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) +
                (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc))));

            if (state < kNumLitStates)
            {
                state -= (state < 4) ? state : 3;
                symbol = 1;
                do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100);
            }
            else
            {
                unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)];
                unsigned offs = 0x100;
                state -= (state < 10) ? 3 : 6;
                symbol = 1;
                do
                {
                    unsigned bit;
                    CLzmaProb *probLit;
                    matchByte <<= 1;
                    bit = (matchByte & offs);
                    probLit = prob + offs + bit + symbol;
                    GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit)
                }
                while (symbol < 0x100);
            }
            dic[dicPos++] = (Byte)symbol;
            processedPos++;
            continue;
        }
        else
        {
            UPDATE_1_2(prob);
            prob = probs + IsRep + state;
            IF_BIT_0_2(prob)
            {
                UPDATE_0_2(prob);
                state += kNumStates;
                prob = probs + LenCoder;
            }
            else
            {
                UPDATE_1_2(prob);
                if (checkDicSize == 0 && processedPos == 0)
                    return SZ_ERROR_DATA;
                prob = probs + IsRepG0 + state;
                IF_BIT_0_2(prob)
                {
                    UPDATE_0_2(prob);
                    prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState;
                    IF_BIT_0_2(prob)
                    {
                        UPDATE_0_2(prob);
                        dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)];
                        dicPos++;
                        processedPos++;
                        state = state < kNumLitStates ? 9 : 11;
                        continue;
                    }
                    UPDATE_1_2(prob);
                }
                else
                {
                    UInt32 distance;
                    UPDATE_1_2(prob);
                    prob = probs + IsRepG1 + state;
                    IF_BIT_0_2(prob)
                    {
                        UPDATE_0_2(prob);
                        distance = rep1;
                    }
                    else
                    {
                        UPDATE_1_2(prob);
                        prob = probs + IsRepG2 + state;
                        IF_BIT_0_2(prob)
                        {
                            UPDATE_0_2(prob);
                            distance = rep2;
                        }
                        else
                        {
                            UPDATE_1_2(prob);
                            distance = rep3;
                            rep3 = rep2;
                        }
                        rep2 = rep1;
                    }
                    rep1 = rep0;
                    rep0 = distance;
                }
                state = state < kNumLitStates ? 8 : 11;
                prob = probs + RepLenCoder;
            }
            {
                unsigned limit, offset;
                CLzmaProb *probLen = prob + LenChoice;
                IF_BIT_0_2(probLen)
                {
                    UPDATE_0_2(probLen);
                    probLen = prob + LenLow + (posState << kLenNumLowBits);
                    offset = 0;
                    limit = (1 << kLenNumLowBits);
                }
                else
                {
                    UPDATE_1_2(probLen);
                    probLen = prob + LenChoice2;
                    IF_BIT_0_2(probLen)
                    {
                        UPDATE_0_2(probLen);
                        probLen = prob + LenMid + (posState << kLenNumMidBits);
                        offset = kLenNumLowSymbols;
                        limit = (1 << kLenNumMidBits);
                    }
                    else
                    {
                        UPDATE_1_2(probLen);
                        probLen = prob + LenHigh;
                        offset = kLenNumLowSymbols + kLenNumMidSymbols;
                        limit = (1 << kLenNumHighBits);
                    }
                }
                TREE_DECODE(probLen, limit, len);
                len += offset;
            }

            if (state >= kNumStates)
            {
                UInt32 distance;
                prob = probs + PosSlot +
                    ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits);
                TREE_6_DECODE(prob, distance);
                if (distance >= kStartPosModelIndex)
                {
                    unsigned posSlot = (unsigned)distance;
                    int numDirectBits = (int)(((distance >> 1) - 1));
                    distance = (2 | (distance & 1));
                    if (posSlot < kEndPosModelIndex)
                    {
                        distance <<= numDirectBits;
                        prob = probs + SpecPos + distance - posSlot - 1;
                        {
                            UInt32 mask = 1;
                            unsigned i = 1;
                            do
                            {
                                GET_BIT2(prob + i, i, ; , distance |= mask);
                                mask <<= 1;
                            }
                            while (--numDirectBits != 0);
                        }
                    }
                    else
                    {
                        numDirectBits -= kNumAlignBits;
                        do
                        {
                            NORMALIZE_2
                                range >>= 1;

                            {
                                UInt32 t;
                                code -= range;
                                t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */
                                distance = (distance << 1) + (t + 1);
                                code += range & t;
                            }
                            /*
                            distance <<= 1;
                            if (code >= range)
                            {
                            code -= range;
                            distance |= 1;
                            }
                            */
                        }
                        while (--numDirectBits != 0);
                        prob = probs + Align;
                        distance <<= kNumAlignBits;
                        {
                            unsigned i = 1;
                            GET_BIT2(prob + i, i, ; , distance |= 1);
                            GET_BIT2(prob + i, i, ; , distance |= 2);
                            GET_BIT2(prob + i, i, ; , distance |= 4);
                            GET_BIT2(prob + i, i, ; , distance |= 8);
                        }
                        if (distance == (UInt32)0xFFFFFFFF)
                        {
                            len += kMatchSpecLenStart;
                            state -= kNumStates;
                            break;
                        }
                    }
                }
                rep3 = rep2;
                rep2 = rep1;
                rep1 = rep0;
                rep0 = distance + 1;
                if (checkDicSize == 0)
                {
                    if (distance >= processedPos)
                        return SZ_ERROR_DATA;
                }
                else if (distance >= checkDicSize)
                    return SZ_ERROR_DATA;
                state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3;
            }

            len += kMatchMinLen;

            if (limit == dicPos)
                return SZ_ERROR_DATA;
            {
                SizeT rem = limit - dicPos;
                unsigned curLen = ((rem < len) ? (unsigned)rem : len);
                SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0);

                processedPos += curLen;

                len -= curLen;
                if (pos + curLen <= dicBufSize)
                {
                    Byte *dest = dic + dicPos;
                    ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos;
                    const Byte *lim = dest + curLen;
                    dicPos += curLen;
                    do
                    *(dest) = (Byte)*(dest + src);
                    while (++dest != lim);
                }
                else
                {
                    do
                    {
                        dic[dicPos++] = dic[pos];
                        if (++pos == dicBufSize)
                            pos = 0;
                    }
                    while (--curLen != 0);
                }
            }
    }
  }
  while (dicPos < limit && buf < bufLimit);
  NORMALIZE_2;
  p->buf = buf;
  p->range = range;
  p->code = code;
  p->remainLen = len;
  p->dicPos = dicPos;
  p->processedPos = processedPos;
  p->reps[0] = rep0;
  p->reps[1] = rep1;
  p->reps[2] = rep2;
  p->reps[3] = rep3;
  p->state = state;

  return SZ_OK;
}

static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit)
{
    if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart)
    {
        Byte *dic = p->dic;
        SizeT dicPos = p->dicPos;
        SizeT dicBufSize = p->dicBufSize;
        unsigned len = p->remainLen;
        UInt32 rep0 = p->reps[0];
        if (limit - dicPos < len)
            len = (unsigned)(limit - dicPos);

        if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len)
            p->checkDicSize = p->prop.dicSize;

        p->processedPos += len;
        p->remainLen -= len;
        while (len-- != 0)
        {
            dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)];
            dicPos++;
        }
        p->dicPos = dicPos;
    }
}

static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit)
{
    do
    {
        SizeT limit2 = limit;
        if (p->checkDicSize == 0)
        {
            UInt32 rem = p->prop.dicSize - p->processedPos;
            if (limit - p->dicPos > rem)
                limit2 = p->dicPos + rem;
        }
        RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit));
        if (p->processedPos >= p->prop.dicSize)
            p->checkDicSize = p->prop.dicSize;
        LzmaDec_WriteRem(p, limit);
    }
    while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart);

    if (p->remainLen > kMatchSpecLenStart)
    {
        p->remainLen = kMatchSpecLenStart;
    }
    return 0;
}

typedef enum
{
    DUMMY_ERROR, /* unexpected end of input stream */
        DUMMY_LIT,
        DUMMY_MATCH,
        DUMMY_REP
} ELzmaDummy;

static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize)
{
    UInt32 range = p->range;
    UInt32 code = p->code;
    const Byte *bufLimit = buf + inSize;
    CLzmaProb *probs = p->probs;
    unsigned state = p->state;
    ELzmaDummy res;

    {
        CLzmaProb *prob;
        UInt32 bound;
        unsigned ttt;
        unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1);

        prob = probs + IsMatch + (state << kNumPosBitsMax) + posState;
        IF_BIT_0_2_CHECK(prob)
        {
            UPDATE_0_2_CHECK

                /* if (bufLimit - buf >= 7) return DUMMY_LIT; */

                prob = probs + Literal;
            if (p->checkDicSize != 0 || p->processedPos != 0)
                prob += (LZMA_LIT_SIZE *
                ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) +
                (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc))));

            if (state < kNumLitStates)
            {
                unsigned symbol = 1;
                do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100);
            }
            else
            {
                unsigned matchByte = p->dic[p->dicPos - p->reps[0] +
                    ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)];
                unsigned offs = 0x100;
                unsigned symbol = 1;
                do
                {
                    unsigned bit;
                    CLzmaProb *probLit;
                    matchByte <<= 1;
                    bit = (matchByte & offs);
                    probLit = prob + offs + bit + symbol;
                    GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit)
                }
                while (symbol < 0x100);
            }
            res = DUMMY_LIT;
        }
        else
        {
            unsigned len;
            UPDATE_1_2_CHECK;

            prob = probs + IsRep + state;
            IF_BIT_0_2_CHECK(prob)
            {
                UPDATE_0_2_CHECK;
                state = 0;
                prob = probs + LenCoder;
                res = DUMMY_MATCH;
            }
            else
            {
                UPDATE_1_2_CHECK;
                res = DUMMY_REP;
                prob = probs + IsRepG0 + state;
                IF_BIT_0_2_CHECK(prob)
                {
                    UPDATE_0_2_CHECK;
                    prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState;
                    IF_BIT_0_2_CHECK(prob)
                    {
                        UPDATE_0_2_CHECK;
                        NORMALIZE_2_CHECK;
                        return DUMMY_REP;
                    }
                    else
                    {
                        UPDATE_1_2_CHECK;
                    }
                }
                else
                {
                    UPDATE_1_2_CHECK;
                    prob = probs + IsRepG1 + state;
                    IF_BIT_0_2_CHECK(prob)
                    {
                        UPDATE_0_2_CHECK;
                    }
                    else
                    {
                        UPDATE_1_2_CHECK;
                        prob = probs + IsRepG2 + state;
                        IF_BIT_0_2_CHECK(prob)
                        {
                            UPDATE_0_2_CHECK;
                        }
                        else
                        {
                            UPDATE_1_2_CHECK;
                        }
                    }
                }
                state = kNumStates;
                prob = probs + RepLenCoder;
            }
            {
                unsigned limit, offset;
                CLzmaProb *probLen = prob + LenChoice;
                IF_BIT_0_2_CHECK(probLen)
                {
                    UPDATE_0_2_CHECK;
                    probLen = prob + LenLow + (posState << kLenNumLowBits);
                    offset = 0;
                    limit = 1 << kLenNumLowBits;
                }
                else
                {
                    UPDATE_1_2_CHECK;
                    probLen = prob + LenChoice2;
                    IF_BIT_0_2_CHECK(probLen)
                    {
                        UPDATE_0_2_CHECK;
                        probLen = prob + LenMid + (posState << kLenNumMidBits);
                        offset = kLenNumLowSymbols;
                        limit = 1 << kLenNumMidBits;
                    }
                    else
                    {
                        UPDATE_1_2_CHECK;
                        probLen = prob + LenHigh;
                        offset = kLenNumLowSymbols + kLenNumMidSymbols;
                        limit = 1 << kLenNumHighBits;
                    }
                }
                TREE_DECODE_CHECK(probLen, limit, len);
                len += offset;
            }

            if (state < 4)
            {
                unsigned posSlot;
                prob = probs + PosSlot +
                    ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) <<
                    kNumPosSlotBits);
                TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot);
                if (posSlot >= kStartPosModelIndex)
                {
                    int numDirectBits = ((posSlot >> 1) - 1);

                    /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */

                    if (posSlot < kEndPosModelIndex)
                    {
                        prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1;
                    }
                    else
                    {
                        numDirectBits -= kNumAlignBits;
                        do
                        {
                            NORMALIZE_2_CHECK
                                range >>= 1;
                            code -= range & (((code - range) >> 31) - 1);
                            /* if (code >= range) code -= range; */
                        }
                        while (--numDirectBits != 0);
                        prob = probs + Align;
                        numDirectBits = kNumAlignBits;
                    }
                    {
                        unsigned i = 1;
                        do
                        {
                            GET_BIT_CHECK(prob + i, i);
                        }
                        while (--numDirectBits != 0);
                    }
                }
            }
    }
  }
  NORMALIZE_2_CHECK;
  return res;
}


static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data)
{
    p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]);
    p->range = 0xFFFFFFFF;
    p->needFlush = 0;
}

void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState)
{
    p->needFlush = 1;
    p->remainLen = 0;
    p->tempBufSize = 0;

    if (initDic)
    {
        p->processedPos = 0;
        p->checkDicSize = 0;
        p->needInitState = 1;
    }
    if (initState)
        p->needInitState = 1;
}

void LzmaDec_Init(CLzmaDec *p)
{
    p->dicPos = 0;
    LzmaDec_InitDicAndState(p, True, True);
}

static void LzmaDec_InitStateReal(CLzmaDec *p)
{
    UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp));
    UInt32 i;
    CLzmaProb *probs = p->probs;
    for (i = 0; i < numProbs; i++)
        probs[i] = kBitModelTotal >> 1;
    p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1;
    p->state = 0;
    p->needInitState = 0;
}

SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen,
                         ELzmaFinishMode finishMode, ELzmaStatus *status)
{
    SizeT inSize = *srcLen;
    (*srcLen) = 0;
    LzmaDec_WriteRem(p, dicLimit);

    *status = LZMA_STATUS_NOT_SPECIFIED;

    while (p->remainLen != kMatchSpecLenStart)
    {
        int checkEndMarkNow;

        if (p->needFlush != 0)
        {
            for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--)
                p->tempBuf[p->tempBufSize++] = *src++;
            if (p->tempBufSize < RC_INIT_SIZE)
            {
                *status = LZMA_STATUS_NEEDS_MORE_INPUT;
                return SZ_OK;
            }
            if (p->tempBuf[0] != 0)
                return SZ_ERROR_DATA;

            LzmaDec_InitRc(p, p->tempBuf);
            p->tempBufSize = 0;
        }

        checkEndMarkNow = 0;
        if (p->dicPos >= dicLimit)
        {
            if (p->remainLen == 0 && p->code == 0)
            {
                *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK;
                return SZ_OK;
            }
            if (finishMode == LZMA_FINISH_ANY)
            {
                *status = LZMA_STATUS_NOT_FINISHED;
                return SZ_OK;
            }
            if (p->remainLen != 0)
            {
                *status = LZMA_STATUS_NOT_FINISHED;
                return SZ_ERROR_DATA;
            }
            checkEndMarkNow = 1;
        }

        if (p->needInitState)
            LzmaDec_InitStateReal(p);

        if (p->tempBufSize == 0)
        {
            SizeT processed;
            const Byte *bufLimit;
            if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow)
            {
                int dummyRes = LzmaDec_TryDummy(p, src, inSize);
                if (dummyRes == DUMMY_ERROR)
                {
                    memcpy(p->tempBuf, src, inSize);
                    p->tempBufSize = (unsigned)inSize;
                    (*srcLen) += inSize;
                    *status = LZMA_STATUS_NEEDS_MORE_INPUT;
                    return SZ_OK;
                }
                if (checkEndMarkNow && dummyRes != DUMMY_MATCH)
                {
                    *status = LZMA_STATUS_NOT_FINISHED;
                    return SZ_ERROR_DATA;
                }
                bufLimit = src;
            }
            else
                bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX;
            p->buf = src;
            if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0)
                return SZ_ERROR_DATA;
            processed = (SizeT)(p->buf - src);
            (*srcLen) += processed;
            src += processed;
            inSize -= processed;
        }
        else
        {
            unsigned rem = p->tempBufSize, lookAhead = 0;
            while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize)
                p->tempBuf[rem++] = src[lookAhead++];
            p->tempBufSize = rem;
            if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow)
            {
                int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem);
                if (dummyRes == DUMMY_ERROR)
                {
                    (*srcLen) += lookAhead;
                    *status = LZMA_STATUS_NEEDS_MORE_INPUT;
                    return SZ_OK;
                }
                if (checkEndMarkNow && dummyRes != DUMMY_MATCH)
                {
                    *status = LZMA_STATUS_NOT_FINISHED;
                    return SZ_ERROR_DATA;
                }
            }
            p->buf = p->tempBuf;
            if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0)
                return SZ_ERROR_DATA;
            lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf));
            (*srcLen) += lookAhead;
            src += lookAhead;
            inSize -= lookAhead;
            p->tempBufSize = 0;
        }
  }
  if (p->code == 0)
      *status = LZMA_STATUS_FINISHED_WITH_MARK;
  return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA;
}

SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status)
{
    SizeT outSize = *destLen;
    SizeT inSize = *srcLen;
    *srcLen = *destLen = 0;
    for (;;)
    {
        SizeT inSizeCur = inSize, outSizeCur, dicPos;
        ELzmaFinishMode curFinishMode;
        SRes res;
        if (p->dicPos == p->dicBufSize)
            p->dicPos = 0;
        dicPos = p->dicPos;
        if (outSize > p->dicBufSize - dicPos)
        {
            outSizeCur = p->dicBufSize;
            curFinishMode = LZMA_FINISH_ANY;
        }
        else
        {
            outSizeCur = dicPos + outSize;
            curFinishMode = finishMode;
        }

        res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status);
        src += inSizeCur;
        inSize -= inSizeCur;
        *srcLen += inSizeCur;
        outSizeCur = p->dicPos - dicPos;
        memcpy(dest, p->dic + dicPos, outSizeCur);
        dest += outSizeCur;
        outSize -= outSizeCur;
        *destLen += outSizeCur;
        if (res != 0)
            return res;
        if (outSizeCur == 0 || outSize == 0)
            return SZ_OK;
    }
}

void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc)
{
    alloc->Free(alloc, p->probs);
    p->probs = 0;
}

static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc)
{
    alloc->Free(alloc, p->dic);
    p->dic = 0;
}

void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc)
{
    LzmaDec_FreeProbs(p, alloc);
    LzmaDec_FreeDict(p, alloc);
}

SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size)
{
    UInt32 dicSize;
    Byte d;

    if (size < LZMA_PROPS_SIZE)
        return SZ_ERROR_UNSUPPORTED;
    else
        dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24);

    if (dicSize < LZMA_DIC_MIN)
        dicSize = LZMA_DIC_MIN;
    p->dicSize = dicSize;

    d = data[0];
    if (d >= (9 * 5 * 5))
        return SZ_ERROR_UNSUPPORTED;

    p->lc = d % 9;
    d /= 9;
    p->pb = d / 5;
    p->lp = d % 5;

    return SZ_OK;
}

static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc)
{
    UInt32 numProbs = LzmaProps_GetNumProbs(propNew);
    if (p->probs == 0 || numProbs != p->numProbs)
    {
        LzmaDec_FreeProbs(p, alloc);
        p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb));
        p->numProbs = numProbs;
        if (p->probs == 0)
            return SZ_ERROR_MEM;
    }
    return SZ_OK;
}

SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
{
    CLzmaProps propNew;
    RINOK(LzmaProps_Decode(&propNew, props, propsSize));
    RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc));
    p->prop = propNew;
    return SZ_OK;
}

SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
{
    CLzmaProps propNew;
    SizeT dicBufSize;
    RINOK(LzmaProps_Decode(&propNew, props, propsSize));
    RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc));
    dicBufSize = propNew.dicSize;
    if (p->dic == 0 || dicBufSize != p->dicBufSize)
    {
        LzmaDec_FreeDict(p, alloc);
        p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize);
        if (p->dic == 0)
        {
            LzmaDec_FreeProbs(p, alloc);
            return SZ_ERROR_MEM;
        }
    }
    p->dicBufSize = dicBufSize;
    p->prop = propNew;
    return SZ_OK;
}

SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
                const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode,
                ELzmaStatus *status, ISzAlloc *alloc)
{
    CLzmaDec p;
    SRes res;
    SizeT inSize = *srcLen;
    SizeT outSize = *destLen;
    *srcLen = *destLen = 0;
    if (inSize < RC_INIT_SIZE)
        return SZ_ERROR_INPUT_EOF;

    LzmaDec_Construct(&p);
    res = LzmaDec_AllocateProbs(&p, propData, propSize, alloc);
    if (res != 0)
        return res;
    p.dic = dest;
    p.dicBufSize = outSize;

    LzmaDec_Init(&p);

    *srcLen = inSize;
    res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status);

    if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT)
        res = SZ_ERROR_INPUT_EOF;

    (*destLen) = p.dicPos;
    LzmaDec_FreeProbs(&p, alloc);
    return res;
}


/* #define _7ZIP_PPMD_SUPPPORT */

#define k_Copy 0
#define k_LZMA2 0x21
#define k_LZMA  0x30101
#define k_BCJ   0x03030103
#define k_PPC   0x03030205
#define k_ARM   0x03030501
#define k_ARMT  0x03030701
#define k_SPARC 0x03030805
#define k_BCJ2  0x0303011B

#ifdef _7ZIP_PPMD_SUPPPORT

#define k_PPMD 0x30401

typedef struct
{
    IByteIn p;
    const Byte *cur;
    const Byte *end;
    const Byte *begin;
    UInt64 processed;
    Bool extra;
    SRes res;
    ILookInStream *inStream;
} CByteInToLook;

static Byte ReadByte(void *pp)
{
    CByteInToLook *p = (CByteInToLook *)pp;
    if (p->cur != p->end)
        return *p->cur++;
    if (p->res == SZ_OK)
    {
        size_t size = p->cur - p->begin;
        p->processed += size;
        p->res = p->inStream->Skip(p->inStream, size);
        size = (1 << 25);
        p->res = p->inStream->Look(p->inStream, (const void **)&p->begin, &size);
        p->cur = p->begin;
        p->end = p->begin + size;
        if (size != 0)
            return *p->cur++;;
    }
    p->extra = True;
    return 0;
}

static SRes SzDecodePpmd(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream,
                         Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain)
{
    CPpmd7 ppmd;
    CByteInToLook s;
    SRes res = SZ_OK;

    s.p.Read = ReadByte;
    s.inStream = inStream;
    s.begin = s.end = s.cur = NULL;
    s.extra = False;
    s.res = SZ_OK;
    s.processed = 0;

    if (coder->Props.size != 5)
        return SZ_ERROR_UNSUPPORTED;

    {
        unsigned order = coder->Props.data[0];
        UInt32 memSize = GetUi32(coder->Props.data + 1);
        if (order < PPMD7_MIN_ORDER ||
            order > PPMD7_MAX_ORDER ||
            memSize < PPMD7_MIN_MEM_SIZE ||
            memSize > PPMD7_MAX_MEM_SIZE)
            return SZ_ERROR_UNSUPPORTED;
        Ppmd7_Construct(&ppmd);
        if (!Ppmd7_Alloc(&ppmd, memSize, allocMain))
            return SZ_ERROR_MEM;
        Ppmd7_Init(&ppmd, order);
    }
    {
        CPpmd7z_RangeDec rc;
        Ppmd7z_RangeDec_CreateVTable(&rc);
        rc.Stream = &s.p;
        if (!Ppmd7z_RangeDec_Init(&rc))
            res = SZ_ERROR_DATA;
        else if (s.extra)
            res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA);
        else
        {
            SizeT i;
            for (i = 0; i < outSize; i++)
            {
                int sym = Ppmd7_DecodeSymbol(&ppmd, &rc.p);
                if (s.extra || sym < 0)
                    break;
                outBuffer[i] = (Byte)sym;
            }
            if (i != outSize)
                res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA);
            else if (s.processed + (s.cur - s.begin) != inSize || !Ppmd7z_RangeDec_IsFinishedOK(&rc))
                res = SZ_ERROR_DATA;
        }
    }
    Ppmd7_Free(&ppmd, allocMain);
    return res;
}

#endif


static SRes SzDecodeLzma(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream,
                         Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain)
{
    CLzmaDec state;
    SRes res = SZ_OK;

    LzmaDec_Construct(&state);
    RINOK(LzmaDec_AllocateProbs(&state, coder->Props.data, (unsigned)coder->Props.size, allocMain));
    state.dic = outBuffer;
    state.dicBufSize = outSize;
    LzmaDec_Init(&state);

    for (;;)
    {
        Byte *inBuf = NULL;
        size_t lookahead = (1 << 18);
        if (lookahead > inSize)
            lookahead = (size_t)inSize;
        res = inStream->Look((void *)inStream, (const void **)&inBuf, &lookahead);
        if (res != SZ_OK)
            break;

        {
            SizeT inProcessed = (SizeT)lookahead, dicPos = state.dicPos;
            ELzmaStatus status;
            res = LzmaDec_DecodeToDic(&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status);
            lookahead -= inProcessed;
            inSize -= inProcessed;
            if (res != SZ_OK)
                break;
            if (state.dicPos == state.dicBufSize || (inProcessed == 0 && dicPos == state.dicPos))
            {
                if (state.dicBufSize != outSize || lookahead != 0 ||
                    (status != LZMA_STATUS_FINISHED_WITH_MARK &&
                    status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK))
                    res = SZ_ERROR_DATA;
                break;
            }
            res = inStream->Skip((void *)inStream, inProcessed);
            if (res != SZ_OK)
                break;
        }
    }

    LzmaDec_FreeProbs(&state, allocMain);
    return res;
}

static SRes SzDecodeLzma2(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream,
                          Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain)
{
    CLzma2Dec state;
    SRes res = SZ_OK;

    Lzma2Dec_Construct(&state);
    if (coder->Props.size != 1)
        return SZ_ERROR_DATA;
    RINOK(Lzma2Dec_AllocateProbs(&state, coder->Props.data[0], allocMain));
    state.decoder.dic = outBuffer;
    state.decoder.dicBufSize = outSize;
    Lzma2Dec_Init(&state);

    for (;;)
    {
        Byte *inBuf = NULL;
        size_t lookahead = (1 << 18);
        if (lookahead > inSize)
            lookahead = (size_t)inSize;
        res = inStream->Look((void *)inStream, (const void **)&inBuf, &lookahead);
        if (res != SZ_OK)
            break;

        {
            SizeT inProcessed = (SizeT)lookahead, dicPos = state.decoder.dicPos;
            ELzmaStatus status;
            res = Lzma2Dec_DecodeToDic(&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status);
            lookahead -= inProcessed;
            inSize -= inProcessed;
            if (res != SZ_OK)
                break;
            if (state.decoder.dicPos == state.decoder.dicBufSize || (inProcessed == 0 && dicPos == state.decoder.dicPos))
            {
                if (state.decoder.dicBufSize != outSize || lookahead != 0 ||
                    (status != LZMA_STATUS_FINISHED_WITH_MARK))
                    res = SZ_ERROR_DATA;
                break;
            }
            res = inStream->Skip((void *)inStream, inProcessed);
            if (res != SZ_OK)
                break;
        }
    }

    Lzma2Dec_FreeProbs(&state, allocMain);
    return res;
}

static SRes SzDecodeCopy(UInt64 inSize, ILookInStream *inStream, Byte *outBuffer)
{
    while (inSize > 0)
    {
        void *inBuf;
        size_t curSize = (1 << 18);
        if (curSize > inSize)
            curSize = (size_t)inSize;
        RINOK(inStream->Look((void *)inStream, (const void **)&inBuf, &curSize));
        if (curSize == 0)
            return SZ_ERROR_INPUT_EOF;
        memcpy(outBuffer, inBuf, curSize);
        outBuffer += curSize;
        inSize -= curSize;
        RINOK(inStream->Skip((void *)inStream, curSize));
    }
    return SZ_OK;
}

static Bool IS_MAIN_METHOD(UInt32 m)
{
    switch(m)
    {
    case k_Copy:
    case k_LZMA:
    case k_LZMA2:
#ifdef _7ZIP_PPMD_SUPPPORT
    case k_PPMD:
#endif
        return True;
    }
    return False;
}

static Bool IS_SUPPORTED_CODER(const CSzCoderInfo *c)
{
    return
        c->NumInStreams == 1 &&
        c->NumOutStreams == 1 &&
        c->MethodID <= (UInt32)0xFFFFFFFF &&
        IS_MAIN_METHOD((UInt32)c->MethodID);
}

#define IS_BCJ2(c) ((c)->MethodID == k_BCJ2 && (c)->NumInStreams == 4 && (c)->NumOutStreams == 1)

static SRes CheckSupportedFolder(const CSzFolder *f)
{
    if (f->NumCoders < 1 || f->NumCoders > 4)
        return SZ_ERROR_UNSUPPORTED;
    if (!IS_SUPPORTED_CODER(&f->Coders[0]))
        return SZ_ERROR_UNSUPPORTED;
    if (f->NumCoders == 1)
    {
        if (f->NumPackStreams != 1 || f->PackStreams[0] != 0 || f->NumBindPairs != 0)
            return SZ_ERROR_UNSUPPORTED;
        return SZ_OK;
    }
    if (f->NumCoders == 2)
    {
        CSzCoderInfo *c = &f->Coders[1];
        if (c->MethodID > (UInt32)0xFFFFFFFF ||
            c->NumInStreams != 1 ||
            c->NumOutStreams != 1 ||
            f->NumPackStreams != 1 ||
            f->PackStreams[0] != 0 ||
            f->NumBindPairs != 1 ||
            f->BindPairs[0].InIndex != 1 ||
            f->BindPairs[0].OutIndex != 0)
            return SZ_ERROR_UNSUPPORTED;
        switch ((UInt32)c->MethodID)
        {
        case k_BCJ:
        case k_ARM:
            break;
        default:
            return SZ_ERROR_UNSUPPORTED;
        }
        return SZ_OK;
    }
    if (f->NumCoders == 4)
    {
        if (!IS_SUPPORTED_CODER(&f->Coders[1]) ||
            !IS_SUPPORTED_CODER(&f->Coders[2]) ||
            !IS_BCJ2(&f->Coders[3]))
            return SZ_ERROR_UNSUPPORTED;
        if (f->NumPackStreams != 4 ||
            f->PackStreams[0] != 2 ||
            f->PackStreams[1] != 6 ||
            f->PackStreams[2] != 1 ||
            f->PackStreams[3] != 0 ||
            f->NumBindPairs != 3 ||
            f->BindPairs[0].InIndex != 5 || f->BindPairs[0].OutIndex != 0 ||
            f->BindPairs[1].InIndex != 4 || f->BindPairs[1].OutIndex != 1 ||
            f->BindPairs[2].InIndex != 3 || f->BindPairs[2].OutIndex != 2)
            return SZ_ERROR_UNSUPPORTED;
        return SZ_OK;
    }
    return SZ_ERROR_UNSUPPORTED;
}

static UInt64 GetSum(const UInt64 *values, UInt32 index)
{
    UInt64 sum = 0;
    UInt32 i;
    for (i = 0; i < index; i++)
        sum += values[i];
    return sum;
}

#define CASE_BRA_CONV(isa) case k_ ## isa: isa ## _Convert(outBuffer, outSize, 0, 0); break;

static SRes SzFolder_Decode2(const CSzFolder *folder, const UInt64 *packSizes,
                             ILookInStream *inStream, UInt64 startPos,
                             Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain,
                             Byte *tempBuf[])
{
    UInt32 ci;
    SizeT tempSizes[3] = { 0, 0, 0};
    SizeT tempSize3 = 0;
    Byte *tempBuf3 = 0;

    RINOK(CheckSupportedFolder(folder));

    for (ci = 0; ci < folder->NumCoders; ci++)
    {
        CSzCoderInfo *coder = &folder->Coders[ci];

        if (IS_MAIN_METHOD((UInt32)coder->MethodID))
        {
            UInt32 si = 0;
            UInt64 offset;
            UInt64 inSize;
            Byte *outBufCur = outBuffer;
            SizeT outSizeCur = outSize;
            if (folder->NumCoders == 4)
            {
                UInt32 indices[] = { 3, 2, 0 };
                UInt64 unpackSize = folder->UnpackSizes[ci];
                si = indices[ci];
                if (ci < 2)
                {
                    Byte *temp;
                    outSizeCur = (SizeT)unpackSize;
                    if (outSizeCur != unpackSize)
                        return SZ_ERROR_MEM;
                    temp = (Byte *)IAlloc_Alloc(allocMain, outSizeCur);
                    if (temp == 0 && outSizeCur != 0)
                        return SZ_ERROR_MEM;
                    outBufCur = tempBuf[1 - ci] = temp;
                    tempSizes[1 - ci] = outSizeCur;
                }
                else if (ci == 2)
                {
                    if (unpackSize > outSize) /* check it */
                        return SZ_ERROR_PARAM;
                    tempBuf3 = outBufCur = outBuffer + (outSize - (size_t)unpackSize);
                    tempSize3 = outSizeCur = (SizeT)unpackSize;
                }
                else
                    return SZ_ERROR_UNSUPPORTED;
            }
            offset = GetSum(packSizes, si);
            inSize = packSizes[si];
            RINOK(LookInStream_SeekTo(inStream, startPos + offset));

            if (coder->MethodID == k_Copy)
            {
                if (inSize != outSizeCur) /* check it */
                    return SZ_ERROR_DATA;
                RINOK(SzDecodeCopy(inSize, inStream, outBufCur));
            }
            else if (coder->MethodID == k_LZMA)
            {
                RINOK(SzDecodeLzma(coder, inSize, inStream, outBufCur, outSizeCur, allocMain));
            }
            else if (coder->MethodID == k_LZMA2)
            {
                RINOK(SzDecodeLzma2(coder, inSize, inStream, outBufCur, outSizeCur, allocMain));
            }
            else
            {
#ifdef _7ZIP_PPMD_SUPPPORT
                RINOK(SzDecodePpmd(coder, inSize, inStream, outBufCur, outSizeCur, allocMain));
#else
                return SZ_ERROR_UNSUPPORTED;
#endif
            }
        }
        else if (coder->MethodID == k_BCJ2)
        {
            UInt64 offset = GetSum(packSizes, 1);
            UInt64 s3Size = packSizes[1];
            SRes res;
            if (ci != 3)
                return SZ_ERROR_UNSUPPORTED;
            RINOK(LookInStream_SeekTo(inStream, startPos + offset));
            tempSizes[2] = (SizeT)s3Size;
            if (tempSizes[2] != s3Size)
                return SZ_ERROR_MEM;
            tempBuf[2] = (Byte *)IAlloc_Alloc(allocMain, tempSizes[2]);
            if (tempBuf[2] == 0 && tempSizes[2] != 0)
                return SZ_ERROR_MEM;
            res = SzDecodeCopy(s3Size, inStream, tempBuf[2]);
            RINOK(res)

                res = Bcj2_Decode(
                tempBuf3, tempSize3,
                tempBuf[0], tempSizes[0],
                tempBuf[1], tempSizes[1],
                tempBuf[2], tempSizes[2],
                outBuffer, outSize);
            RINOK(res)
        }
        else
        {
            if (ci != 1)
                return SZ_ERROR_UNSUPPORTED;
            switch(coder->MethodID)
            {
            case k_BCJ:
                {
                    UInt32 state;
                    x86_Convert_Init(state);
                    x86_Convert(outBuffer, outSize, 0, &state, 0);
                    break;
                }
                CASE_BRA_CONV(ARM)
            default:
                return SZ_ERROR_UNSUPPORTED;
            }
        }
  }
  return SZ_OK;
}

SRes SzFolder_Decode(const CSzFolder *folder, const UInt64 *packSizes,
                     ILookInStream *inStream, UInt64 startPos,
                     Byte *outBuffer, size_t outSize, ISzAlloc *allocMain)
{
    Byte *tempBuf[3] = { 0, 0, 0};
    int i;
    SRes res = SzFolder_Decode2(folder, packSizes, inStream, startPos,
        outBuffer, (SizeT)outSize, allocMain, tempBuf);
    for (i = 0; i < 3; i++)
        IAlloc_Free(allocMain, tempBuf[i]);
    return res;
}
#ifndef USE_WINDOWS_FILE
/* for mkdir */
#ifdef _WIN32
#include <direct.h>
#else
#include <sys/stat.h>
#include <errno.h>
#endif
#endif

static ISzAlloc g_Alloc = { SzAlloc, SzFree };

static int Buf_EnsureSize(CBuf *dest, size_t size)
{
    if (dest->size >= size)
        return 1;
    Buf_Free(dest, &g_Alloc);
    return Buf_Create(dest, size, &g_Alloc);
}

#ifndef _WIN32

static Byte kUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };

static Bool Utf16_To_Utf8(Byte *dest, size_t *destLen, const UInt16 *src, size_t srcLen)
{
    size_t destPos = 0, srcPos = 0;
    for (;;)
    {
        unsigned numAdds;
        UInt32 value;
        if (srcPos == srcLen)
        {
            *destLen = destPos;
            return True;
        }
        value = src[srcPos++];
        if (value < 0x80)
        {
            if (dest)
                dest[destPos] = (char)value;
            destPos++;
            continue;
        }
        if (value >= 0xD800 && value < 0xE000)
        {
            UInt32 c2;
            if (value >= 0xDC00 || srcPos == srcLen)
                break;
            c2 = src[srcPos++];
            if (c2 < 0xDC00 || c2 >= 0xE000)
                break;
            value = (((value - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000;
        }
        for (numAdds = 1; numAdds < 5; numAdds++)
            if (value < (((UInt32)1) << (numAdds * 5 + 6)))
                break;
            if (dest)
                dest[destPos] = (char)(kUtf8Limits[numAdds - 1] + (value >> (6 * numAdds)));
            destPos++;
            do
            {
                numAdds--;
                if (dest)
                    dest[destPos] = (char)(0x80 + ((value >> (6 * numAdds)) & 0x3F));
                destPos++;
            }
            while (numAdds != 0);
    }
    *destLen = destPos;
    return False;
}

static SRes Utf16_To_Utf8Buf(CBuf *dest, const UInt16 *src, size_t srcLen)
{
    size_t destLen = 0;
    Bool res;
    Utf16_To_Utf8(NULL, &destLen, src, srcLen);
    destLen += 1;
    if (!Buf_EnsureSize(dest, destLen))
        return SZ_ERROR_MEM;
    res = Utf16_To_Utf8(dest->data, &destLen, src, srcLen);
    dest->data[destLen] = 0;
    return res ? SZ_OK : SZ_ERROR_FAIL;
}
#endif

#include <string>
#include <direct.h> //_mkdir函数的头文件
#include <io.h>     //_access函数的头文件
using namespace std;

void string_replace(std::string &strBig, const std::string &strsrc, const std::string &strdst)
{
    std::string::size_type pos = 0;
    std::string::size_type srclen = strsrc.size();
    std::string::size_type dstlen = strdst.size();

    while ((pos = strBig.find(strsrc, pos)) != std::string::npos)
    {
        strBig.replace(pos, srclen, strdst);
        pos += dstlen;
    }
}

void CreateAllDir(const char *dir)
{
    int m = 0, n;
    string str1, str2;
    str1 = dir;
    string_replace(str1, "/", "\\");
    str2 = str1.substr(0, 2);
    str1 = str1.substr(3, str1.size());
    while (m >= 0)
    {
        m = str1.find('\\');

        str2 += '\\' + str1.substr(0, m);
        n = _access(str2.c_str(), 0); //判断该目录是否存在
        if (n == -1)
        {
            _mkdir(str2.c_str());     //创建目录
        }

        str1 = str1.substr(m + 1, str1.size());
    }
}

static SRes Utf16_To_Char(CBuf *buf, const UInt16 *s, int fileMode)
{
    int len = 0;
    for (len = 0; s[len] != '\0'; len++);

#ifdef _WIN32
    {
        int size = len * 3 + 100;
        if (!Buf_EnsureSize(buf, size))
            return SZ_ERROR_MEM;
        {
            char defaultChar = '_';
            BOOL defUsed;
            int numChars = WideCharToMultiByte(fileMode ?
                (
#ifdef UNDER_CE
                CP_ACP
#else
                AreFileApisANSI() ? CP_ACP : CP_OEMCP
#endif
                ) : CP_OEMCP,
                0, (LPCWCH)s, len, (char *)buf->data, size, &defaultChar, &defUsed);
            if (numChars == 0 || numChars >= size)
                return SZ_ERROR_FAIL;
            buf->data[numChars] = 0;
            return SZ_OK;
        }
    }
#else
    fileMode = fileMode;
    return Utf16_To_Utf8Buf(buf, s, len);
#endif
}

static WRes MyCreateDir(const UInt16 *name)
{
#ifdef USE_WINDOWS_FILE

    return CreateDirectoryW(name, NULL) ? 0 : GetLastError();

#else

    CBuf buf;
    WRes res=0;
    Buf_Init(&buf);
    RINOK(Utf16_To_Char(&buf, name, 1));

    CreateAllDir((const char *)buf.data);
    //res = ::CreateAllDir((const char *)buf.data,NULL) == 0 ? 0 : errno;

#ifdef _WIN32

#else
        mkdir((const char *)buf.data, 0777)
#endif

    Buf_Free(&buf, &g_Alloc);
    return res;

#endif
}

static WRes OutFile_OpenUtf16(CSzFile *p, const UInt16 *name)
{
#ifdef USE_WINDOWS_FILE
    return OutFile_OpenW(p, name);
#else
    CBuf buf;
    WRes res;
    Buf_Init(&buf);
    RINOK(Utf16_To_Char(&buf, name, 1));
    res = OutFile_Open(p, (const char *)buf.data);
    Buf_Free(&buf, &g_Alloc);
    return res;
#endif
}

static SRes PrintString(const UInt16 *s)
{
    CBuf buf;
    SRes res;
    Buf_Init(&buf);
    res = Utf16_To_Char(&buf, s, 0);
    if (res == SZ_OK)
        fputs((const char *)buf.data, stdout);
    Buf_Free(&buf, &g_Alloc);
    return res;
}

static void UInt64ToStr(UInt64 value, char *s)
{
    char temp[32];
    int pos = 0;
    do
    {
        temp[pos++] = (char)('0' + (unsigned)(value % 10));
        value /= 10;
    }
    while (value != 0);
    do
    *s++ = temp[--pos];
    while (pos);
    *s = '\0';
}

static char *UIntToStr(char *s, unsigned value, int numDigits)
{
    char temp[16];
    int pos = 0;
    do
    temp[pos++] = (char)('0' + (value % 10));
    while (value /= 10);
    for (numDigits -= pos; numDigits > 0; numDigits--)
        *s++ = '0';
    do
    *s++ = temp[--pos];
    while (pos);
    *s = '\0';
    return s;
}

#define PERIOD_4 (4 * 365 + 1)
#define PERIOD_100 (PERIOD_4 * 25 - 1)
#define PERIOD_400 (PERIOD_100 * 4 + 1)

static void ConvertFileTimeToString(const CNtfsFileTime *ft, char *s)
{
    unsigned year, mon, day, hour, min, sec;
    UInt64 v64 = (ft->Low | ((UInt64)ft->High << 32)) / 10000000;
    Byte ms[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    unsigned t;
    UInt32 v;
    sec = (unsigned)(v64 % 60); v64 /= 60;
    min = (unsigned)(v64 % 60); v64 /= 60;
    hour = (unsigned)(v64 % 24); v64 /= 24;

    v = (UInt32)v64;

    year = (unsigned)(1601 + v / PERIOD_400 * 400);
    v %= PERIOD_400;

    t = v / PERIOD_100; if (t ==  4) t =  3; year += t * 100; v -= t * PERIOD_100;
    t = v / PERIOD_4;   if (t == 25) t = 24; year += t * 4;   v -= t * PERIOD_4;
    t = v / 365;        if (t ==  4) t =  3; year += t;       v -= t * 365;

    if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
        ms[1] = 29;
    for (mon = 1; mon <= 12; mon++)
    {
        unsigned s = ms[mon - 1];
        if (v < s)
            break;
        v -= s;
    }
    day = (unsigned)v + 1;
    s = UIntToStr(s, year, 4); *s++ = '-';
    s = UIntToStr(s, mon, 2);  *s++ = '-';
    s = UIntToStr(s, day, 2);  *s++ = ' ';
    s = UIntToStr(s, hour, 2); *s++ = ':';
    s = UIntToStr(s, min, 2);  *s++ = ':';
    s = UIntToStr(s, sec, 2);
}

void PrintError(char *sz)
{
    printf("\nERROR: %s\n", sz);
}

#ifdef USE_WINDOWS_FILE
#define kEmptyAttribChar '.'
static void GetAttribString(UInt32 wa, Bool isDir, char *s)
{
    s[0] = (char)(((wa & FILE_ATTRIBUTE_DIRECTORY) != 0 || isDir) ? 'D' : kEmptyAttribChar);
    s[1] = (char)(((wa & FILE_ATTRIBUTE_READONLY) != 0) ? 'R': kEmptyAttribChar);
    s[2] = (char)(((wa & FILE_ATTRIBUTE_HIDDEN) != 0) ? 'H': kEmptyAttribChar);
    s[3] = (char)(((wa & FILE_ATTRIBUTE_SYSTEM) != 0) ? 'S': kEmptyAttribChar);
    s[4] = (char)(((wa & FILE_ATTRIBUTE_ARCHIVE) != 0) ? 'A': kEmptyAttribChar);
    s[5] = '\0';
}
#else
static void GetAttribString(UInt32, Bool, char *s)
{
    s[0] = '\0';
}
#endif

CWinApp theApp;

using namespace std;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    int nRetCode = 0;

    // initialize MFC and print and error on failure
    if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
    {
        // TODO: change error code to suit your needs
        cerr << _T("Fatal Error: MFC initialization failed") << endl;
        nRetCode = 1;
    }
    else
    {
        CFileInStream archiveStream;
        CLookToRead lookStream;
        CSzArEx db;
        SRes res;
        ISzAlloc allocImp;
        ISzAlloc allocTempImp;
        UInt16 *temp = NULL;
        size_t tempSize = 0;

        printf("\n7z ANSI-C Decoder\n\n");
        if (argc == 1)
        {
            printf(
                "Usage: 7zDec <command> <archive_name>\n\n"
                "<Commands>\n"
                "  e: Extract files from archive (without using directory names)\n"
                "  l: List contents of archive\n"
                "  t: Test integrity of archive\n"
                "  x: eXtract files with full paths\n");
            return 0;
        }
        if (argc < 3)
        {
            PrintError("incorrect command");
            return 1;
        }

        allocImp.Alloc = SzAlloc;
        allocImp.Free = SzFree;

        allocTempImp.Alloc = SzAllocTemp;
        allocTempImp.Free = SzFreeTemp;

        if (InFile_Open(&archiveStream.file, argv[2]))
        {
            PrintError("can not open input file");
            return 1;
        }

        FileInStream_CreateVTable(&archiveStream);
        LookToRead_CreateVTable(&lookStream, False);

        lookStream.realStream = &archiveStream.s;
        LookToRead_Init(&lookStream);

        CrcGenerateTable();

        SzArEx_Init(&db);
        res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp);
        if (res == SZ_OK)
        {
            char *command = argv[1];
            int listCommand = 0, testCommand = 0, extractCommand = 0, fullPaths = 0;
            if (strcmp(command, "l") == 0) listCommand = 1;
            else if (strcmp(command, "t") == 0) testCommand = 1;
            else if (strcmp(command, "e") == 0) extractCommand = 1;
            else if (strcmp(command, "x") == 0) { extractCommand = 1; fullPaths = 1; }
            else
            {
                PrintError("incorrect command");
                res = SZ_ERROR_FAIL;
            }

            if (res == SZ_OK)
            {

                /*
                if you need cache, use these 3 variables.
                if you use external function, you can make these variable as static.
                */
                UInt32 blockIndex = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */
                Byte *outBuffer = 0; /* it must be 0 before first call for each new archive. */
                size_t outBufferSize = 0;  /* it can have any value before first call (if outBuffer = 0) */

                const char *pDest = "D:/";
                int nHeadLen = strlen(pDest);
                for (UInt32 i= 0; i < db.db.NumFiles; i++)
                {
                    size_t offset = 0;
                    size_t outSizeProcessed = 0;
                    const CSzFileItem *f = db.db.Files + i;
                    size_t len;
                    if (listCommand == 0 && f->IsDir && !fullPaths)
                        continue;
                    len = SzArEx_GetFileNameUtf16(&db, i, NULL);
                    len += nHeadLen;

                    if (len > tempSize)
                    {
                        SzFree(NULL, temp);
                        tempSize = len;
                        temp = (UInt16 *)SzAlloc(NULL, tempSize * sizeof(UInt16));
                        if (temp == 0)
                        {
                            res = SZ_ERROR_MEM;
                            break;
                        }
                        for (int i = 0; i < nHeadLen; i++)
                            SetUi16(temp + i, (UInt16)pDest[i]);
                    }

                    SzArEx_GetFileNameUtf16(&db, i, temp + nHeadLen);
                    if (listCommand)
                    {
                        char attr[8], s[32], t[32];

                        GetAttribString(f->AttribDefined ? f->Attrib : 0, f->IsDir, attr);

                        UInt64ToStr(f->Size, s);
                        if (f->MTimeDefined)
                            ConvertFileTimeToString(&f->MTime, t);
                        else
                        {
                            size_t j;
                            for (j = 0; j < 19; j++)
                                t[j] = ' ';
                            t[j] = '\0';
                        }

                        printf("%s %s %10s  ", t, attr, s);
                        res = PrintString(temp);
                        if (res != SZ_OK)
                            break;
                        if (f->IsDir)
                            printf("/");
                        printf("\n");
                        continue;
                    }
                    fputs(testCommand ?
                        "Testing    ":
                    "Extracting ",
                        stdout);
                    res = PrintString(temp);
                    if (res != SZ_OK)
                        break;
                    if (f->IsDir)
                        printf("/");
                    else
                    {
                        res = SzArEx_Extract(&db, &lookStream.s, i,
                            &blockIndex, &outBuffer, &outBufferSize,
                            &offset, &outSizeProcessed,
                            &allocImp, &allocTempImp);
                        if (res != SZ_OK)
                            break;
                    }
                    if (!testCommand)
                    {
                        CSzFile outFile;
                        size_t processedSize;
                        if (f->IsDir)
                        {
                            MyCreateDir((UInt16 *)temp);
                            printf("\n");
                            continue;
                        }
                        else if (OutFile_OpenUtf16(&outFile, (UInt16 *)temp))
                        {
                            PrintError("can not open output file");
                            res = SZ_ERROR_FAIL;
                            break;
                        }
                        processedSize = outSizeProcessed;
                        if (File_Write(&outFile, outBuffer + offset, &processedSize) != 0 || processedSize != outSizeProcessed)
                        {
                            PrintError("can not write output file");
                            res = SZ_ERROR_FAIL;
                            break;
                        }
                        if (File_Close(&outFile))
                        {
                            PrintError("can not close output file");
                            res = SZ_ERROR_FAIL;
                            break;
                        }
#ifdef USE_WINDOWS_FILE
                        if (f->AttribDefined)
                            SetFileAttributesW(destPath, f->Attrib);
#endif
                    }
                    printf("\n");
      }
      IAlloc_Free(&allocImp, outBuffer);
    }
  }
  SzArEx_Free(&db, &allocImp);
  SzFree(NULL, temp);

  File_Close(&archiveStream.file);
  if (res == SZ_OK)
  {
      printf("\nEverything is Ok\n");
      return 0;
  }
  if (res == SZ_ERROR_UNSUPPORTED)
      PrintError("decoder doesn't support this archive");
  else if (res == SZ_ERROR_MEM)
      PrintError("can not allocate memory");
  else if (res == SZ_ERROR_CRC)
      PrintError("CRC error");
  else
      printf("\nERROR #%d\n", res);
  return 1;
    }

    return nRetCode;
}


猜你喜欢

转载自blog.csdn.net/what951006/article/details/80224713
今日推荐