mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-27 14:22:13 +00:00
591 lines
15 KiB
C
591 lines
15 KiB
C
/* 7zDec.c -- Decoding from 7z folder
|
|
2015-11-18 : Igor Pavlov : Public domain */
|
|
|
|
#include "Precomp.h"
|
|
|
|
#include <string.h>
|
|
|
|
/* #define _7ZIP_PPMD_SUPPPORT */
|
|
|
|
#include "7z.h"
|
|
#include "7zCrc.h"
|
|
|
|
#include "Bcj2.h"
|
|
#include "Bra.h"
|
|
#include "CpuArch.h"
|
|
#include "Delta.h"
|
|
#include "LzmaDec.h"
|
|
#include "Lzma2Dec.h"
|
|
#ifdef _7ZIP_PPMD_SUPPPORT
|
|
#include "Ppmd7.h"
|
|
#endif
|
|
|
|
#define k_Copy 0
|
|
#define k_Delta 3
|
|
#define k_LZMA2 0x21
|
|
#define k_LZMA 0x30101
|
|
#define k_BCJ 0x3030103
|
|
#define k_BCJ2 0x303011B
|
|
#define k_PPC 0x3030205
|
|
#define k_IA64 0x3030401
|
|
#define k_ARM 0x3030501
|
|
#define k_ARMT 0x3030701
|
|
#define k_SPARC 0x3030805
|
|
|
|
|
|
#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 (;;)
|
|
{
|
|
const void *inBuf = NULL;
|
|
size_t lookahead = (1 << 18);
|
|
if (lookahead > inSize)
|
|
lookahead = (size_t)inSize;
|
|
res = inStream->Look(inStream, &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 (status == LZMA_STATUS_FINISHED_WITH_MARK)
|
|
{
|
|
if (outSize != state.dicPos || inSize != 0)
|
|
res = SZ_ERROR_DATA;
|
|
break;
|
|
}
|
|
|
|
if (outSize == state.dicPos && inSize == 0 && status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)
|
|
break;
|
|
|
|
if (inProcessed == 0 && dicPos == state.dicPos)
|
|
{
|
|
res = SZ_ERROR_DATA;
|
|
break;
|
|
}
|
|
|
|
res = inStream->Skip((void *)inStream, inProcessed);
|
|
if (res != SZ_OK)
|
|
break;
|
|
}
|
|
}
|
|
|
|
LzmaDec_FreeProbs(&state, allocMain);
|
|
return res;
|
|
}
|
|
|
|
|
|
#ifndef _7Z_NO_METHOD_LZMA2
|
|
|
|
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 (;;)
|
|
{
|
|
const void *inBuf = NULL;
|
|
size_t lookahead = (1 << 18);
|
|
if (lookahead > inSize)
|
|
lookahead = (size_t)inSize;
|
|
res = inStream->Look(inStream, &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 (status == LZMA_STATUS_FINISHED_WITH_MARK)
|
|
{
|
|
if (outSize != state.decoder.dicPos || inSize != 0)
|
|
res = SZ_ERROR_DATA;
|
|
break;
|
|
}
|
|
|
|
if (inProcessed == 0 && dicPos == state.decoder.dicPos)
|
|
{
|
|
res = SZ_ERROR_DATA;
|
|
break;
|
|
}
|
|
|
|
res = inStream->Skip((void *)inStream, inProcessed);
|
|
if (res != SZ_OK)
|
|
break;
|
|
}
|
|
}
|
|
|
|
Lzma2Dec_FreeProbs(&state, allocMain);
|
|
return res;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
static SRes SzDecodeCopy(UInt64 inSize, ILookInStream *inStream, Byte *outBuffer)
|
|
{
|
|
while (inSize > 0)
|
|
{
|
|
const void *inBuf;
|
|
size_t curSize = (1 << 18);
|
|
if (curSize > inSize)
|
|
curSize = (size_t)inSize;
|
|
RINOK(inStream->Look(inStream, &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:
|
|
#ifndef _7Z_NO_METHOD_LZMA2
|
|
case k_LZMA2:
|
|
#endif
|
|
#ifdef _7ZIP_PPMD_SUPPPORT
|
|
case k_PPMD:
|
|
#endif
|
|
return True;
|
|
}
|
|
return False;
|
|
}
|
|
|
|
static Bool IS_SUPPORTED_CODER(const CSzCoderInfo *c)
|
|
{
|
|
return
|
|
c->NumStreams == 1
|
|
/* && c->MethodID <= (UInt32)0xFFFFFFFF */
|
|
&& IS_MAIN_METHOD((UInt32)c->MethodID);
|
|
}
|
|
|
|
#define IS_BCJ2(c) ((c)->MethodID == k_BCJ2 && (c)->NumStreams == 4)
|
|
|
|
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->NumBonds != 0)
|
|
return SZ_ERROR_UNSUPPORTED;
|
|
return SZ_OK;
|
|
}
|
|
|
|
|
|
#ifndef _7Z_NO_METHODS_FILTERS
|
|
|
|
if (f->NumCoders == 2)
|
|
{
|
|
const CSzCoderInfo *c = &f->Coders[1];
|
|
if (
|
|
/* c->MethodID > (UInt32)0xFFFFFFFF || */
|
|
c->NumStreams != 1
|
|
|| f->NumPackStreams != 1
|
|
|| f->PackStreams[0] != 0
|
|
|| f->NumBonds != 1
|
|
|| f->Bonds[0].InIndex != 1
|
|
|| f->Bonds[0].OutIndex != 0)
|
|
return SZ_ERROR_UNSUPPORTED;
|
|
switch ((UInt32)c->MethodID)
|
|
{
|
|
case k_Delta:
|
|
case k_BCJ:
|
|
case k_PPC:
|
|
case k_IA64:
|
|
case k_SPARC:
|
|
case k_ARM:
|
|
case k_ARMT:
|
|
break;
|
|
default:
|
|
return SZ_ERROR_UNSUPPORTED;
|
|
}
|
|
return SZ_OK;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
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->NumBonds != 3
|
|
|| f->Bonds[0].InIndex != 5 || f->Bonds[0].OutIndex != 0
|
|
|| f->Bonds[1].InIndex != 4 || f->Bonds[1].OutIndex != 1
|
|
|| f->Bonds[2].InIndex != 3 || f->Bonds[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 && 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));
|
|
}
|
|
#ifndef _7Z_NO_METHOD_LZMA2
|
|
else if (coder->MethodID == k_LZMA2)
|
|
{
|
|
RINOK(SzDecodeLzma2(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain));
|
|
}
|
|
#endif
|
|
#ifdef _7ZIP_PPMD_SUPPPORT
|
|
else if (coder->MethodID == k_PPMD)
|
|
{
|
|
RINOK(SzDecodePpmd(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain));
|
|
}
|
|
#endif
|
|
else
|
|
return SZ_ERROR_UNSUPPORTED;
|
|
}
|
|
else if (coder->MethodID == k_BCJ2)
|
|
{
|
|
UInt64 offset = packPositions[1];
|
|
UInt64 s3Size = packPositions[2] - offset;
|
|
|
|
if (ci != 3)
|
|
return SZ_ERROR_UNSUPPORTED;
|
|
|
|
tempSizes[2] = (SizeT)s3Size;
|
|
if (tempSizes[2] != s3Size)
|
|
return SZ_ERROR_MEM;
|
|
tempBuf[2] = (Byte *)IAlloc_Alloc(allocMain, tempSizes[2]);
|
|
if (!tempBuf[2] && tempSizes[2] != 0)
|
|
return SZ_ERROR_MEM;
|
|
|
|
RINOK(LookInStream_SeekTo(inStream, startPos + offset));
|
|
RINOK(SzDecodeCopy(s3Size, inStream, tempBuf[2]));
|
|
|
|
if ((tempSizes[0] & 3) != 0 ||
|
|
(tempSizes[1] & 3) != 0 ||
|
|
tempSize3 + tempSizes[0] + tempSizes[1] != outSize)
|
|
return SZ_ERROR_DATA;
|
|
|
|
{
|
|
CBcj2Dec p;
|
|
|
|
p.bufs[0] = tempBuf3; p.lims[0] = tempBuf3 + tempSize3;
|
|
p.bufs[1] = tempBuf[0]; p.lims[1] = tempBuf[0] + tempSizes[0];
|
|
p.bufs[2] = tempBuf[1]; p.lims[2] = tempBuf[1] + tempSizes[1];
|
|
p.bufs[3] = tempBuf[2]; p.lims[3] = tempBuf[2] + tempSizes[2];
|
|
|
|
p.dest = outBuffer;
|
|
p.destLim = outBuffer + outSize;
|
|
|
|
Bcj2Dec_Init(&p);
|
|
RINOK(Bcj2Dec_Decode(&p));
|
|
|
|
{
|
|
unsigned i;
|
|
for (i = 0; i < 4; i++)
|
|
if (p.bufs[i] != p.lims[i])
|
|
return SZ_ERROR_DATA;
|
|
|
|
if (!Bcj2Dec_IsFinished(&p))
|
|
return SZ_ERROR_DATA;
|
|
|
|
if (p.dest != p.destLim
|
|
|| p.state != BCJ2_STREAM_MAIN)
|
|
return SZ_ERROR_DATA;
|
|
}
|
|
}
|
|
}
|
|
#ifndef _7Z_NO_METHODS_FILTERS
|
|
else if (ci == 1)
|
|
{
|
|
if (coder->MethodID == k_Delta)
|
|
{
|
|
if (coder->PropsSize != 1)
|
|
return SZ_ERROR_UNSUPPORTED;
|
|
{
|
|
Byte state[DELTA_STATE_SIZE];
|
|
Delta_Init(state);
|
|
Delta_Decode(state, (unsigned)(propsData[coder->PropsOffset]) + 1, outBuffer, outSize);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (coder->PropsSize != 0)
|
|
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(PPC)
|
|
CASE_BRA_CONV(IA64)
|
|
CASE_BRA_CONV(SPARC)
|
|
CASE_BRA_CONV(ARM)
|
|
CASE_BRA_CONV(ARMT)
|
|
default:
|
|
return SZ_ERROR_UNSUPPORTED;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
else
|
|
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;
|
|
|
|
const Byte *data = p->CodersData + p->FoCodersOffsets[folderIndex];
|
|
sd.Data = data;
|
|
sd.Size = p->FoCodersOffsets[folderIndex + 1] - p->FoCodersOffsets[folderIndex];
|
|
|
|
res = SzGetNextFolderItem(&folder, &sd);
|
|
|
|
if (res != SZ_OK)
|
|
return res;
|
|
|
|
if (sd.Size != 0
|
|
|| folder.UnpackStream != p->FoToMainUnpackSizeIndex[folderIndex]
|
|
|| outSize != SzAr_GetFolderUnpackSize(p, folderIndex))
|
|
return SZ_ERROR_FAIL;
|
|
{
|
|
unsigned i;
|
|
Byte *tempBuf[3] = { 0, 0, 0};
|
|
|
|
res = SzFolder_Decode2(&folder, data,
|
|
&p->CoderUnpackSizes[p->FoToCoderUnpackSizes[folderIndex]],
|
|
p->PackPositions + p->FoStartPackStreamIndex[folderIndex],
|
|
inStream, startPos,
|
|
outBuffer, (SizeT)outSize, allocMain, tempBuf);
|
|
|
|
for (i = 0; i < 3; i++)
|
|
IAlloc_Free(allocMain, tempBuf[i]);
|
|
|
|
if (res == SZ_OK)
|
|
if (SzBitWithVals_Check(&p->FolderCRCs, folderIndex))
|
|
if (CrcCalc(outBuffer, outSize) != p->FolderCRCs.Vals[folderIndex])
|
|
res = SZ_ERROR_CRC;
|
|
|
|
return res;
|
|
}
|
|
}
|