1 /* 7zDec.c -- Decoding from 7z folder
\r
2 2015-11-18 : Igor Pavlov : Public domain */
\r
8 /* #define _7ZIP_PPMD_SUPPPORT */
\r
15 #include "CpuArch.h"
\r
17 #include "LzmaDec.h"
\r
18 #include "Lzma2Dec.h"
\r
19 #ifdef _7ZIP_PPMD_SUPPPORT
\r
25 #define k_LZMA2 0x21
\r
26 #define k_LZMA 0x30101
\r
27 #define k_BCJ 0x3030103
\r
28 #define k_BCJ2 0x303011B
\r
29 #define k_PPC 0x3030205
\r
30 #define k_IA64 0x3030401
\r
31 #define k_ARM 0x3030501
\r
32 #define k_ARMT 0x3030701
\r
33 #define k_SPARC 0x3030805
\r
36 #ifdef _7ZIP_PPMD_SUPPPORT
\r
38 #define k_PPMD 0x30401
\r
49 ILookInStream *inStream;
\r
52 static Byte ReadByte(void *pp)
\r
54 CByteInToLook *p = (CByteInToLook *)pp;
\r
55 if (p->cur != p->end)
\r
57 if (p->res == SZ_OK)
\r
59 size_t size = p->cur - p->begin;
\r
60 p->processed += size;
\r
61 p->res = p->inStream->Skip(p->inStream, size);
\r
63 p->res = p->inStream->Look(p->inStream, (const void **)&p->begin, &size);
\r
65 p->end = p->begin + size;
\r
73 static SRes SzDecodePpmd(const Byte *props, unsigned propsSize, UInt64 inSize, ILookInStream *inStream,
\r
74 Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain)
\r
80 s.p.Read = ReadByte;
\r
81 s.inStream = inStream;
\r
82 s.begin = s.end = s.cur = NULL;
\r
88 return SZ_ERROR_UNSUPPORTED;
\r
91 unsigned order = props[0];
\r
92 UInt32 memSize = GetUi32(props + 1);
\r
93 if (order < PPMD7_MIN_ORDER ||
\r
94 order > PPMD7_MAX_ORDER ||
\r
95 memSize < PPMD7_MIN_MEM_SIZE ||
\r
96 memSize > PPMD7_MAX_MEM_SIZE)
\r
97 return SZ_ERROR_UNSUPPORTED;
\r
98 Ppmd7_Construct(&ppmd);
\r
99 if (!Ppmd7_Alloc(&ppmd, memSize, allocMain))
\r
100 return SZ_ERROR_MEM;
\r
101 Ppmd7_Init(&ppmd, order);
\r
104 CPpmd7z_RangeDec rc;
\r
105 Ppmd7z_RangeDec_CreateVTable(&rc);
\r
107 if (!Ppmd7z_RangeDec_Init(&rc))
\r
108 res = SZ_ERROR_DATA;
\r
110 res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA);
\r
114 for (i = 0; i < outSize; i++)
\r
116 int sym = Ppmd7_DecodeSymbol(&ppmd, &rc.p);
\r
117 if (s.extra || sym < 0)
\r
119 outBuffer[i] = (Byte)sym;
\r
122 res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA);
\r
123 else if (s.processed + (s.cur - s.begin) != inSize || !Ppmd7z_RangeDec_IsFinishedOK(&rc))
\r
124 res = SZ_ERROR_DATA;
\r
127 Ppmd7_Free(&ppmd, allocMain);
\r
134 static SRes SzDecodeLzma(const Byte *props, unsigned propsSize, UInt64 inSize, ILookInStream *inStream,
\r
135 Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain)
\r
140 LzmaDec_Construct(&state);
\r
141 RINOK(LzmaDec_AllocateProbs(&state, props, propsSize, allocMain));
\r
142 state.dic = outBuffer;
\r
143 state.dicBufSize = outSize;
\r
144 LzmaDec_Init(&state);
\r
148 const void *inBuf = NULL;
\r
149 size_t lookahead = (1 << 18);
\r
150 if (lookahead > inSize)
\r
151 lookahead = (size_t)inSize;
\r
152 res = inStream->Look(inStream, &inBuf, &lookahead);
\r
157 SizeT inProcessed = (SizeT)lookahead, dicPos = state.dicPos;
\r
158 ELzmaStatus status;
\r
159 res = LzmaDec_DecodeToDic(&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status);
\r
160 lookahead -= inProcessed;
\r
161 inSize -= inProcessed;
\r
165 if (status == LZMA_STATUS_FINISHED_WITH_MARK)
\r
167 if (outSize != state.dicPos || inSize != 0)
\r
168 res = SZ_ERROR_DATA;
\r
172 if (outSize == state.dicPos && inSize == 0 && status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)
\r
175 if (inProcessed == 0 && dicPos == state.dicPos)
\r
177 res = SZ_ERROR_DATA;
\r
181 res = inStream->Skip((void *)inStream, inProcessed);
\r
187 LzmaDec_FreeProbs(&state, allocMain);
\r
192 #ifndef _7Z_NO_METHOD_LZMA2
\r
194 static SRes SzDecodeLzma2(const Byte *props, unsigned propsSize, UInt64 inSize, ILookInStream *inStream,
\r
195 Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain)
\r
200 Lzma2Dec_Construct(&state);
\r
201 if (propsSize != 1)
\r
202 return SZ_ERROR_DATA;
\r
203 RINOK(Lzma2Dec_AllocateProbs(&state, props[0], allocMain));
\r
204 state.decoder.dic = outBuffer;
\r
205 state.decoder.dicBufSize = outSize;
\r
206 Lzma2Dec_Init(&state);
\r
210 const void *inBuf = NULL;
\r
211 size_t lookahead = (1 << 18);
\r
212 if (lookahead > inSize)
\r
213 lookahead = (size_t)inSize;
\r
214 res = inStream->Look(inStream, &inBuf, &lookahead);
\r
219 SizeT inProcessed = (SizeT)lookahead, dicPos = state.decoder.dicPos;
\r
220 ELzmaStatus status;
\r
221 res = Lzma2Dec_DecodeToDic(&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status);
\r
222 lookahead -= inProcessed;
\r
223 inSize -= inProcessed;
\r
227 if (status == LZMA_STATUS_FINISHED_WITH_MARK)
\r
229 if (outSize != state.decoder.dicPos || inSize != 0)
\r
230 res = SZ_ERROR_DATA;
\r
234 if (inProcessed == 0 && dicPos == state.decoder.dicPos)
\r
236 res = SZ_ERROR_DATA;
\r
240 res = inStream->Skip((void *)inStream, inProcessed);
\r
246 Lzma2Dec_FreeProbs(&state, allocMain);
\r
253 static SRes SzDecodeCopy(UInt64 inSize, ILookInStream *inStream, Byte *outBuffer)
\r
258 size_t curSize = (1 << 18);
\r
259 if (curSize > inSize)
\r
260 curSize = (size_t)inSize;
\r
261 RINOK(inStream->Look(inStream, &inBuf, &curSize));
\r
263 return SZ_ERROR_INPUT_EOF;
\r
264 memcpy(outBuffer, inBuf, curSize);
\r
265 outBuffer += curSize;
\r
267 RINOK(inStream->Skip((void *)inStream, curSize));
\r
272 static Bool IS_MAIN_METHOD(UInt32 m)
\r
278 #ifndef _7Z_NO_METHOD_LZMA2
\r
281 #ifdef _7ZIP_PPMD_SUPPPORT
\r
289 static Bool IS_SUPPORTED_CODER(const CSzCoderInfo *c)
\r
293 /* && c->MethodID <= (UInt32)0xFFFFFFFF */
\r
294 && IS_MAIN_METHOD((UInt32)c->MethodID);
\r
297 #define IS_BCJ2(c) ((c)->MethodID == k_BCJ2 && (c)->NumStreams == 4)
\r
299 static SRes CheckSupportedFolder(const CSzFolder *f)
\r
301 if (f->NumCoders < 1 || f->NumCoders > 4)
\r
302 return SZ_ERROR_UNSUPPORTED;
\r
303 if (!IS_SUPPORTED_CODER(&f->Coders[0]))
\r
304 return SZ_ERROR_UNSUPPORTED;
\r
305 if (f->NumCoders == 1)
\r
307 if (f->NumPackStreams != 1 || f->PackStreams[0] != 0 || f->NumBonds != 0)
\r
308 return SZ_ERROR_UNSUPPORTED;
\r
313 #ifndef _7Z_NO_METHODS_FILTERS
\r
315 if (f->NumCoders == 2)
\r
317 const CSzCoderInfo *c = &f->Coders[1];
\r
319 /* c->MethodID > (UInt32)0xFFFFFFFF || */
\r
321 || f->NumPackStreams != 1
\r
322 || f->PackStreams[0] != 0
\r
323 || f->NumBonds != 1
\r
324 || f->Bonds[0].InIndex != 1
\r
325 || f->Bonds[0].OutIndex != 0)
\r
326 return SZ_ERROR_UNSUPPORTED;
\r
327 switch ((UInt32)c->MethodID)
\r
338 return SZ_ERROR_UNSUPPORTED;
\r
346 if (f->NumCoders == 4)
\r
348 if (!IS_SUPPORTED_CODER(&f->Coders[1])
\r
349 || !IS_SUPPORTED_CODER(&f->Coders[2])
\r
350 || !IS_BCJ2(&f->Coders[3]))
\r
351 return SZ_ERROR_UNSUPPORTED;
\r
352 if (f->NumPackStreams != 4
\r
353 || f->PackStreams[0] != 2
\r
354 || f->PackStreams[1] != 6
\r
355 || f->PackStreams[2] != 1
\r
356 || f->PackStreams[3] != 0
\r
357 || f->NumBonds != 3
\r
358 || f->Bonds[0].InIndex != 5 || f->Bonds[0].OutIndex != 0
\r
359 || f->Bonds[1].InIndex != 4 || f->Bonds[1].OutIndex != 1
\r
360 || f->Bonds[2].InIndex != 3 || f->Bonds[2].OutIndex != 2)
\r
361 return SZ_ERROR_UNSUPPORTED;
\r
365 return SZ_ERROR_UNSUPPORTED;
\r
368 #define CASE_BRA_CONV(isa) case k_ ## isa: isa ## _Convert(outBuffer, outSize, 0, 0); break;
\r
370 static SRes SzFolder_Decode2(const CSzFolder *folder,
\r
371 const Byte *propsData,
\r
372 const UInt64 *unpackSizes,
\r
373 const UInt64 *packPositions,
\r
374 ILookInStream *inStream, UInt64 startPos,
\r
375 Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain,
\r
379 SizeT tempSizes[3] = { 0, 0, 0};
\r
380 SizeT tempSize3 = 0;
\r
381 Byte *tempBuf3 = 0;
\r
383 RINOK(CheckSupportedFolder(folder));
\r
385 for (ci = 0; ci < folder->NumCoders; ci++)
\r
387 const CSzCoderInfo *coder = &folder->Coders[ci];
\r
389 if (IS_MAIN_METHOD((UInt32)coder->MethodID))
\r
394 Byte *outBufCur = outBuffer;
\r
395 SizeT outSizeCur = outSize;
\r
396 if (folder->NumCoders == 4)
\r
398 UInt32 indices[] = { 3, 2, 0 };
\r
399 UInt64 unpackSize = unpackSizes[ci];
\r
404 outSizeCur = (SizeT)unpackSize;
\r
405 if (outSizeCur != unpackSize)
\r
406 return SZ_ERROR_MEM;
\r
407 temp = (Byte *)IAlloc_Alloc(allocMain, outSizeCur);
\r
408 if (!temp && outSizeCur != 0)
\r
409 return SZ_ERROR_MEM;
\r
410 outBufCur = tempBuf[1 - ci] = temp;
\r
411 tempSizes[1 - ci] = outSizeCur;
\r
415 if (unpackSize > outSize) /* check it */
\r
416 return SZ_ERROR_PARAM;
\r
417 tempBuf3 = outBufCur = outBuffer + (outSize - (size_t)unpackSize);
\r
418 tempSize3 = outSizeCur = (SizeT)unpackSize;
\r
421 return SZ_ERROR_UNSUPPORTED;
\r
423 offset = packPositions[si];
\r
424 inSize = packPositions[si + 1] - offset;
\r
425 RINOK(LookInStream_SeekTo(inStream, startPos + offset));
\r
427 if (coder->MethodID == k_Copy)
\r
429 if (inSize != outSizeCur) /* check it */
\r
430 return SZ_ERROR_DATA;
\r
431 RINOK(SzDecodeCopy(inSize, inStream, outBufCur));
\r
433 else if (coder->MethodID == k_LZMA)
\r
435 RINOK(SzDecodeLzma(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain));
\r
437 #ifndef _7Z_NO_METHOD_LZMA2
\r
438 else if (coder->MethodID == k_LZMA2)
\r
440 RINOK(SzDecodeLzma2(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain));
\r
443 #ifdef _7ZIP_PPMD_SUPPPORT
\r
444 else if (coder->MethodID == k_PPMD)
\r
446 RINOK(SzDecodePpmd(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain));
\r
450 return SZ_ERROR_UNSUPPORTED;
\r
452 else if (coder->MethodID == k_BCJ2)
\r
454 UInt64 offset = packPositions[1];
\r
455 UInt64 s3Size = packPositions[2] - offset;
\r
458 return SZ_ERROR_UNSUPPORTED;
\r
460 tempSizes[2] = (SizeT)s3Size;
\r
461 if (tempSizes[2] != s3Size)
\r
462 return SZ_ERROR_MEM;
\r
463 tempBuf[2] = (Byte *)IAlloc_Alloc(allocMain, tempSizes[2]);
\r
464 if (!tempBuf[2] && tempSizes[2] != 0)
\r
465 return SZ_ERROR_MEM;
\r
467 RINOK(LookInStream_SeekTo(inStream, startPos + offset));
\r
468 RINOK(SzDecodeCopy(s3Size, inStream, tempBuf[2]));
\r
470 if ((tempSizes[0] & 3) != 0 ||
\r
471 (tempSizes[1] & 3) != 0 ||
\r
472 tempSize3 + tempSizes[0] + tempSizes[1] != outSize)
\r
473 return SZ_ERROR_DATA;
\r
478 p.bufs[0] = tempBuf3; p.lims[0] = tempBuf3 + tempSize3;
\r
479 p.bufs[1] = tempBuf[0]; p.lims[1] = tempBuf[0] + tempSizes[0];
\r
480 p.bufs[2] = tempBuf[1]; p.lims[2] = tempBuf[1] + tempSizes[1];
\r
481 p.bufs[3] = tempBuf[2]; p.lims[3] = tempBuf[2] + tempSizes[2];
\r
483 p.dest = outBuffer;
\r
484 p.destLim = outBuffer + outSize;
\r
487 RINOK(Bcj2Dec_Decode(&p));
\r
491 for (i = 0; i < 4; i++)
\r
492 if (p.bufs[i] != p.lims[i])
\r
493 return SZ_ERROR_DATA;
\r
495 if (!Bcj2Dec_IsFinished(&p))
\r
496 return SZ_ERROR_DATA;
\r
498 if (p.dest != p.destLim
\r
499 || p.state != BCJ2_STREAM_MAIN)
\r
500 return SZ_ERROR_DATA;
\r
504 #ifndef _7Z_NO_METHODS_FILTERS
\r
507 if (coder->MethodID == k_Delta)
\r
509 if (coder->PropsSize != 1)
\r
510 return SZ_ERROR_UNSUPPORTED;
\r
512 Byte state[DELTA_STATE_SIZE];
\r
514 Delta_Decode(state, (unsigned)(propsData[coder->PropsOffset]) + 1, outBuffer, outSize);
\r
519 if (coder->PropsSize != 0)
\r
520 return SZ_ERROR_UNSUPPORTED;
\r
521 switch (coder->MethodID)
\r
526 x86_Convert_Init(state);
\r
527 x86_Convert(outBuffer, outSize, 0, &state, 0);
\r
531 CASE_BRA_CONV(IA64)
\r
532 CASE_BRA_CONV(SPARC)
\r
534 CASE_BRA_CONV(ARMT)
\r
536 return SZ_ERROR_UNSUPPORTED;
\r
542 return SZ_ERROR_UNSUPPORTED;
\r
549 SRes SzAr_DecodeFolder(const CSzAr *p, UInt32 folderIndex,
\r
550 ILookInStream *inStream, UInt64 startPos,
\r
551 Byte *outBuffer, size_t outSize,
\r
552 ISzAlloc *allocMain)
\r
558 const Byte *data = p->CodersData + p->FoCodersOffsets[folderIndex];
\r
560 sd.Size = p->FoCodersOffsets[folderIndex + 1] - p->FoCodersOffsets[folderIndex];
\r
562 res = SzGetNextFolderItem(&folder, &sd);
\r
568 || folder.UnpackStream != p->FoToMainUnpackSizeIndex[folderIndex]
\r
569 || outSize != SzAr_GetFolderUnpackSize(p, folderIndex))
\r
570 return SZ_ERROR_FAIL;
\r
573 Byte *tempBuf[3] = { 0, 0, 0};
\r
575 res = SzFolder_Decode2(&folder, data,
\r
576 &p->CoderUnpackSizes[p->FoToCoderUnpackSizes[folderIndex]],
\r
577 p->PackPositions + p->FoStartPackStreamIndex[folderIndex],
\r
578 inStream, startPos,
\r
579 outBuffer, (SizeT)outSize, allocMain, tempBuf);
\r
581 for (i = 0; i < 3; i++)
\r
582 IAlloc_Free(allocMain, tempBuf[i]);
\r
585 if (SzBitWithVals_Check(&p->FolderCRCs, folderIndex))
\r
586 if (CrcCalc(outBuffer, outSize) != p->FolderCRCs.Vals[folderIndex])
\r
587 res = SZ_ERROR_CRC;
\r