You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
493 lines
13 KiB
493 lines
13 KiB
/* 7zDec.c -- Decoding from 7z folder |
|
2014-06-16 : Igor Pavlov : Public domain */ |
|
|
|
#include "Precomp.h" |
|
|
|
#include <string.h> |
|
|
|
/* #define _7ZIP_PPMD_SUPPPORT */ |
|
|
|
#include "7z.h" |
|
|
|
#include "Bcj2.h" |
|
#include "Bra.h" |
|
#include "CpuArch.h" |
|
#include "LzmaDec.h" |
|
#include "Lzma2Dec.h" |
|
#ifdef _7ZIP_PPMD_SUPPPORT |
|
#include "Ppmd7.h" |
|
#endif |
|
|
|
#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(const Byte *props, unsigned propsSize, 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 (propsSize != 5) |
|
return SZ_ERROR_UNSUPPORTED; |
|
|
|
{ |
|
unsigned order = props[0]; |
|
UInt32 memSize = GetUi32(props + 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(const Byte *props, unsigned propsSize, UInt64 inSize, ILookInStream *inStream, |
|
Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain) |
|
{ |
|
CLzmaDec state; |
|
SRes res = SZ_OK; |
|
|
|
LzmaDec_Construct(&state); |
|
RINOK(LzmaDec_AllocateProbs(&state, props, propsSize, 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(const Byte *props, unsigned propsSize, UInt64 inSize, ILookInStream *inStream, |
|
Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain) |
|
{ |
|
CLzma2Dec state; |
|
SRes res = SZ_OK; |
|
|
|
Lzma2Dec_Construct(&state); |
|
if (propsSize != 1) |
|
return SZ_ERROR_DATA; |
|
RINOK(Lzma2Dec_AllocateProbs(&state, props[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) |
|
{ |
|
const 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; |
|
} |
|
|
|
#define CASE_BRA_CONV(isa) case k_ ## isa: isa ## _Convert(outBuffer, outSize, 0, 0); break; |
|
|
|
static SRes SzFolder_Decode2(const CSzFolder *folder, |
|
const Byte *propsData, |
|
const UInt64 *unpackSizes, |
|
const UInt64 *packPositions, |
|
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++) |
|
{ |
|
const 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 = 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 = packPositions[si]; |
|
inSize = packPositions[si + 1] - offset; |
|
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(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain)); |
|
} |
|
else if (coder->MethodID == k_LZMA2) |
|
{ |
|
RINOK(SzDecodeLzma2(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain)); |
|
} |
|
else |
|
{ |
|
#ifdef _7ZIP_PPMD_SUPPPORT |
|
RINOK(SzDecodePpmd(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain)); |
|
#else |
|
return SZ_ERROR_UNSUPPORTED; |
|
#endif |
|
} |
|
} |
|
else if (coder->MethodID == k_BCJ2) |
|
{ |
|
UInt64 offset = packPositions[1]; |
|
UInt64 s3Size = packPositions[2] - offset; |
|
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 SzAr_DecodeFolder(const CSzAr *p, UInt32 folderIndex, |
|
ILookInStream *inStream, UInt64 startPos, |
|
Byte *outBuffer, size_t outSize, |
|
ISzAlloc *allocMain) |
|
{ |
|
SRes res; |
|
CSzFolder folder; |
|
CSzData sd; |
|
CSzData sdSizes; |
|
|
|
const Byte *data = p->CodersData + p->FoCodersOffsets[folderIndex]; |
|
sd.Data = data; |
|
sd.Size = p->FoCodersOffsets[folderIndex + 1] - p->FoCodersOffsets[folderIndex]; |
|
|
|
sdSizes.Data = p->UnpackSizesData + p->FoSizesOffsets[folderIndex]; |
|
sdSizes.Size = |
|
p->FoSizesOffsets[folderIndex + 1] - |
|
p->FoSizesOffsets[folderIndex]; |
|
|
|
res = SzGetNextFolderItem(&folder, &sd, &sdSizes); |
|
|
|
if (res != SZ_OK) |
|
return res; |
|
|
|
if (sd.Size != 0 || outSize != folder.CodersUnpackSizes[folder.MainOutStream]) |
|
return SZ_ERROR_FAIL; |
|
{ |
|
int i; |
|
Byte *tempBuf[3] = { 0, 0, 0}; |
|
res = SzFolder_Decode2(&folder, data, folder.CodersUnpackSizes, |
|
p->PackPositions + p->FoStartPackStreamIndex[folderIndex], |
|
inStream, startPos, |
|
outBuffer, (SizeT)outSize, allocMain, tempBuf); |
|
for (i = 0; i < 3; i++) |
|
IAlloc_Free(allocMain, tempBuf[i]); |
|
return res; |
|
} |
|
}
|
|
|