1/* 7zDec.c -- Decoding from 7z folder
22010-11-02 : Igor Pavlov : Public domain */
3
4#include <string.h>
5
6/* #define _7ZIP_PPMD_SUPPPORT */
7
8#include "7z.h"
9
10#include "Bcj2.h"
11#include "Bra.h"
12#include "CpuArch.h"
13#include "LzmaDec.h"
14#include "Lzma2Dec.h"
15#ifdef _7ZIP_PPMD_SUPPPORT
16#include "Ppmd7.h"
17#endif
18
19#define k_Copy 0
20#define k_LZMA2 0x21
21#define k_LZMA 0x30101
22#define k_BCJ 0x03030103
23#define k_PPC 0x03030205
24#define k_ARM 0x03030501
25#define k_ARMT 0x03030701
26#define k_SPARC 0x03030805
27#define k_BCJ2 0x0303011B
28
29#ifdef _7ZIP_PPMD_SUPPPORT
30
31#define k_PPMD 0x30401
32
33typedef struct
34{
35 IByteIn p;
36 const Byte *cur;
37 const Byte *end;
38 const Byte *begin;
39 UInt64 processed;
40 Bool extra;
41 SRes res;
42 ILookInStream *inStream;
43} CByteInToLook;
44
45static Byte ReadByte(void *pp)
46{
47 CByteInToLook *p = (CByteInToLook *)pp;
48 if (p->cur != p->end)
49 return *p->cur++;
50 if (p->res == SZ_OK)
51 {
52 size_t size = p->cur - p->begin;
53 p->processed += size;
54 p->res = p->inStream->Skip(p->inStream, size);
55 size = (1 << 25);
56 p->res = p->inStream->Look(p->inStream, (const void **)&p->begin, &size);
57 p->cur = p->begin;
58 p->end = p->begin + size;
59 if (size != 0)
60 return *p->cur++;;
61 }
62 p->extra = True;
63 return 0;
64}
65
66static SRes SzDecodePpmd(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream,
67 Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain)
68{
69 CPpmd7 ppmd;
70 CByteInToLook s;
71 SRes res = SZ_OK;
72
73 s.p.Read = ReadByte;
74 s.inStream = inStream;
75 s.begin = s.end = s.cur = NULL;
76 s.extra = False;
77 s.res = SZ_OK;
78 s.processed = 0;
79
80 if (coder->Props.size != 5)
81 return SZ_ERROR_UNSUPPORTED;
82
83 {
84 unsigned order = coder->Props.data[0];
85 UInt32 memSize = GetUi32(coder->Props.data + 1);
86 if (order < PPMD7_MIN_ORDER ||
87 order > PPMD7_MAX_ORDER ||
88 memSize < PPMD7_MIN_MEM_SIZE ||
89 memSize > PPMD7_MAX_MEM_SIZE)
90 return SZ_ERROR_UNSUPPORTED;
91 Ppmd7_Construct(&ppmd);
92 if (!Ppmd7_Alloc(&ppmd, memSize, allocMain))
93 return SZ_ERROR_MEM;
94 Ppmd7_Init(&ppmd, order);
95 }
96 {
97 CPpmd7z_RangeDec rc;
98 Ppmd7z_RangeDec_CreateVTable(&rc);
99 rc.Stream = &s.p;
100 if (!Ppmd7z_RangeDec_Init(&rc))
101 res = SZ_ERROR_DATA;
102 else if (s.extra)
103 res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA);
104 else
105 {
106 SizeT i;
107 for (i = 0; i < outSize; i++)
108 {
109 int sym = Ppmd7_DecodeSymbol(&ppmd, &rc.p);
110 if (s.extra || sym < 0)
111 break;
112 outBuffer[i] = (Byte)sym;
113 }
114 if (i != outSize)
115 res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA);
116 else if (s.processed + (s.cur - s.begin) != inSize || !Ppmd7z_RangeDec_IsFinishedOK(&rc))
117 res = SZ_ERROR_DATA;
118 }
119 }
120 Ppmd7_Free(&ppmd, allocMain);
121 return res;
122}
123
124#endif
125
126
127static SRes SzDecodeLzma(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream,
128 Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain)
129{
130 CLzmaDec state;
131 SRes res = SZ_OK;
132
133 LzmaDec_Construct(&state);
134 RINOK(LzmaDec_AllocateProbs(&state, coder->Props.data, (unsigned)coder->Props.size, allocMain));
135 state.dic = outBuffer;
136 state.dicBufSize = outSize;
137 LzmaDec_Init(&state);
138
139 for (;;)
140 {
141 Byte *inBuf = NULL;
142 size_t lookahead = (1 << 18);
143 if (lookahead > inSize)
144 lookahead = (size_t)inSize;
145 res = inStream->Look((void *)inStream, (const void **)&inBuf, &lookahead);
146 if (res != SZ_OK)
147 break;
148
149 {
150 SizeT inProcessed = (SizeT)lookahead, dicPos = state.dicPos;
151 ELzmaStatus status;
152 res = LzmaDec_DecodeToDic(&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status);
153 lookahead -= inProcessed;
154 inSize -= inProcessed;
155 if (res != SZ_OK)
156 break;
157 if (state.dicPos == state.dicBufSize || (inProcessed == 0 && dicPos == state.dicPos))
158 {
159 if (state.dicBufSize != outSize || lookahead != 0 ||
160 (status != LZMA_STATUS_FINISHED_WITH_MARK &&
161 status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK))
162 res = SZ_ERROR_DATA;
163 break;
164 }
165 res = inStream->Skip((void *)inStream, inProcessed);
166 if (res != SZ_OK)
167 break;
168 }
169 }
170
171 LzmaDec_FreeProbs(&state, allocMain);
172 return res;
173}
174
175static SRes SzDecodeLzma2(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream,
176 Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain)
177{
178 CLzma2Dec state;
179 SRes res = SZ_OK;
180
181 Lzma2Dec_Construct(&state);
182 if (coder->Props.size != 1)
183 return SZ_ERROR_DATA;
184 RINOK(Lzma2Dec_AllocateProbs(&state, coder->Props.data[0], allocMain));
185 state.decoder.dic = outBuffer;
186 state.decoder.dicBufSize = outSize;
187 Lzma2Dec_Init(&state);
188
189 for (;;)
190 {
191 Byte *inBuf = NULL;
192 size_t lookahead = (1 << 18);
193 if (lookahead > inSize)
194 lookahead = (size_t)inSize;
195 res = inStream->Look((void *)inStream, (const void **)&inBuf, &lookahead);
196 if (res != SZ_OK)
197 break;
198
199 {
200 SizeT inProcessed = (SizeT)lookahead, dicPos = state.decoder.dicPos;
201 ELzmaStatus status;
202 res = Lzma2Dec_DecodeToDic(&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status);
203 lookahead -= inProcessed;
204 inSize -= inProcessed;
205 if (res != SZ_OK)
206 break;
207 if (state.decoder.dicPos == state.decoder.dicBufSize || (inProcessed == 0 && dicPos == state.decoder.dicPos))
208 {
209 if (state.decoder.dicBufSize != outSize || lookahead != 0 ||
210 (status != LZMA_STATUS_FINISHED_WITH_MARK))
211 res = SZ_ERROR_DATA;
212 break;
213 }
214 res = inStream->Skip((void *)inStream, inProcessed);
215 if (res != SZ_OK)
216 break;
217 }
218 }
219
220 Lzma2Dec_FreeProbs(&state, allocMain);
221 return res;
222}
223
224static SRes SzDecodeCopy(UInt64 inSize, ILookInStream *inStream, Byte *outBuffer)
225{
226 while (inSize > 0)
227 {
228 void *inBuf;
229 size_t curSize = (1 << 18);
230 if (curSize > inSize)
231 curSize = (size_t)inSize;
232 RINOK(inStream->Look((void *)inStream, (const void **)&inBuf, &curSize));
233 if (curSize == 0)
234 return SZ_ERROR_INPUT_EOF;
235 memcpy(outBuffer, inBuf, curSize);
236 outBuffer += curSize;
237 inSize -= curSize;
238 RINOK(inStream->Skip((void *)inStream, curSize));
239 }
240 return SZ_OK;
241}
242
243static Bool IS_MAIN_METHOD(UInt32 m)
244{
245 switch(m)
246 {
247 case k_Copy:
248 case k_LZMA:
249 case k_LZMA2:
250 #ifdef _7ZIP_PPMD_SUPPPORT
251 case k_PPMD:
252 #endif
253 return True;
254 }
255 return False;
256}
257
258static Bool IS_SUPPORTED_CODER(const CSzCoderInfo *c)
259{
260 return
261 c->NumInStreams == 1 &&
262 c->NumOutStreams == 1 &&
263 c->MethodID <= (UInt32)0xFFFFFFFF &&
264 IS_MAIN_METHOD((UInt32)c->MethodID);
265}
266
267#define IS_BCJ2(c) ((c)->MethodID == k_BCJ2 && (c)->NumInStreams == 4 && (c)->NumOutStreams == 1)
268
269static SRes CheckSupportedFolder(const CSzFolder *f)
270{
271 if (f->NumCoders < 1 || f->NumCoders > 4)
272 return SZ_ERROR_UNSUPPORTED;
273 if (!IS_SUPPORTED_CODER(&f->Coders[0]))
274 return SZ_ERROR_UNSUPPORTED;
275 if (f->NumCoders == 1)
276 {
277 if (f->NumPackStreams != 1 || f->PackStreams[0] != 0 || f->NumBindPairs != 0)
278 return SZ_ERROR_UNSUPPORTED;
279 return SZ_OK;
280 }
281 if (f->NumCoders == 2)
282 {
283 CSzCoderInfo *c = &f->Coders[1];
284 if (c->MethodID > (UInt32)0xFFFFFFFF ||
285 c->NumInStreams != 1 ||
286 c->NumOutStreams != 1 ||
287 f->NumPackStreams != 1 ||
288 f->PackStreams[0] != 0 ||
289 f->NumBindPairs != 1 ||
290 f->BindPairs[0].InIndex != 1 ||
291 f->BindPairs[0].OutIndex != 0)
292 return SZ_ERROR_UNSUPPORTED;
293 switch ((UInt32)c->MethodID)
294 {
295 case k_BCJ:
296 case k_ARM:
297 break;
298 default:
299 return SZ_ERROR_UNSUPPORTED;
300 }
301 return SZ_OK;
302 }
303 if (f->NumCoders == 4)
304 {
305 if (!IS_SUPPORTED_CODER(&f->Coders[1]) ||
306 !IS_SUPPORTED_CODER(&f->Coders[2]) ||
307 !IS_BCJ2(&f->Coders[3]))
308 return SZ_ERROR_UNSUPPORTED;
309 if (f->NumPackStreams != 4 ||
310 f->PackStreams[0] != 2 ||
311 f->PackStreams[1] != 6 ||
312 f->PackStreams[2] != 1 ||
313 f->PackStreams[3] != 0 ||
314 f->NumBindPairs != 3 ||
315 f->BindPairs[0].InIndex != 5 || f->BindPairs[0].OutIndex != 0 ||
316 f->BindPairs[1].InIndex != 4 || f->BindPairs[1].OutIndex != 1 ||
317 f->BindPairs[2].InIndex != 3 || f->BindPairs[2].OutIndex != 2)
318 return SZ_ERROR_UNSUPPORTED;
319 return SZ_OK;
320 }
321 return SZ_ERROR_UNSUPPORTED;
322}
323
324static UInt64 GetSum(const UInt64 *values, UInt32 index)
325{
326 UInt64 sum = 0;
327 UInt32 i;
328 for (i = 0; i < index; i++)
329 sum += values[i];
330 return sum;
331}
332
333#define CASE_BRA_CONV(isa) case k_ ## isa: isa ## _Convert(outBuffer, outSize, 0, 0); break;
334
335static SRes SzFolder_Decode2(const CSzFolder *folder, const UInt64 *packSizes,
336 ILookInStream *inStream, UInt64 startPos,
337 Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain,
338 Byte *tempBuf[])
339{
340 UInt32 ci;
341 SizeT tempSizes[3] = { 0, 0, 0};
342 SizeT tempSize3 = 0;
343 Byte *tempBuf3 = 0;
344
345 RINOK(CheckSupportedFolder(folder));
346
347 for (ci = 0; ci < folder->NumCoders; ci++)
348 {
349 CSzCoderInfo *coder = &folder->Coders[ci];
350
351 if (IS_MAIN_METHOD((UInt32)coder->MethodID))
352 {
353 UInt32 si = 0;
354 UInt64 offset;
355 UInt64 inSize;
356 Byte *outBufCur = outBuffer;
357 SizeT outSizeCur = outSize;
358 if (folder->NumCoders == 4)
359 {
360 UInt32 indices[] = { 3, 2, 0 };
361 UInt64 unpackSize = folder->UnpackSizes[ci];
362 si = indices[ci];
363 if (ci < 2)
364 {
365 Byte *temp;
366 outSizeCur = (SizeT)unpackSize;
367 if (outSizeCur != unpackSize)
368 return SZ_ERROR_MEM;
369 temp = (Byte *)IAlloc_Alloc(allocMain, outSizeCur);
370 if (temp == 0 && outSizeCur != 0)
371 return SZ_ERROR_MEM;
372 outBufCur = tempBuf[1 - ci] = temp;
373 tempSizes[1 - ci] = outSizeCur;
374 }
375 else if (ci == 2)
376 {
377 if (unpackSize > outSize) /* check it */
378 return SZ_ERROR_PARAM;
379 tempBuf3 = outBufCur = outBuffer + (outSize - (size_t)unpackSize);
380 tempSize3 = outSizeCur = (SizeT)unpackSize;
381 }
382 else
383 return SZ_ERROR_UNSUPPORTED;
384 }
385 offset = GetSum(packSizes, si);
386 inSize = packSizes[si];
387 RINOK(LookInStream_SeekTo(inStream, startPos + offset));
388
389 if (coder->MethodID == k_Copy)
390 {
391 if (inSize != outSizeCur) /* check it */
392 return SZ_ERROR_DATA;
393 RINOK(SzDecodeCopy(inSize, inStream, outBufCur));
394 }
395 else if (coder->MethodID == k_LZMA)
396 {
397 RINOK(SzDecodeLzma(coder, inSize, inStream, outBufCur, outSizeCur, allocMain));
398 }
399 else if (coder->MethodID == k_LZMA2)
400 {
401 RINOK(SzDecodeLzma2(coder, inSize, inStream, outBufCur, outSizeCur, allocMain));
402 }
403 else
404 {
405 #ifdef _7ZIP_PPMD_SUPPPORT
406 RINOK(SzDecodePpmd(coder, inSize, inStream, outBufCur, outSizeCur, allocMain));
407 #else
408 return SZ_ERROR_UNSUPPORTED;
409 #endif
410 }
411 }
412 else if (coder->MethodID == k_BCJ2)
413 {
414 UInt64 offset = GetSum(packSizes, 1);
415 UInt64 s3Size = packSizes[1];
416 SRes res;
417 if (ci != 3)
418 return SZ_ERROR_UNSUPPORTED;
419 RINOK(LookInStream_SeekTo(inStream, startPos + offset));
420 tempSizes[2] = (SizeT)s3Size;
421 if (tempSizes[2] != s3Size)
422 return SZ_ERROR_MEM;
423 tempBuf[2] = (Byte *)IAlloc_Alloc(allocMain, tempSizes[2]);
424 if (tempBuf[2] == 0 && tempSizes[2] != 0)
425 return SZ_ERROR_MEM;
426 res = SzDecodeCopy(s3Size, inStream, tempBuf[2]);
427 RINOK(res)
428
429 res = Bcj2_Decode(
430 tempBuf3, tempSize3,
431 tempBuf[0], tempSizes[0],
432 tempBuf[1], tempSizes[1],
433 tempBuf[2], tempSizes[2],
434 outBuffer, outSize);
435 RINOK(res)
436 }
437 else
438 {
439 if (ci != 1)
440 return SZ_ERROR_UNSUPPORTED;
441 switch(coder->MethodID)
442 {
443 case k_BCJ:
444 {
445 UInt32 state;
446 x86_Convert_Init(state);
447 x86_Convert(outBuffer, outSize, 0, &state, 0);
448 break;
449 }
450 CASE_BRA_CONV(ARM)
451 default:
452 return SZ_ERROR_UNSUPPORTED;
453 }
454 }
455 }
456 return SZ_OK;
457}
458
459SRes SzFolder_Decode(const CSzFolder *folder, const UInt64 *packSizes,
460 ILookInStream *inStream, UInt64 startPos,
461 Byte *outBuffer, size_t outSize, ISzAlloc *allocMain)
462{
463 Byte *tempBuf[3] = { 0, 0, 0};
464 int i;
465 SRes res = SzFolder_Decode2(folder, packSizes, inStream, startPos,
466 outBuffer, (SizeT)outSize, allocMain, tempBuf);
467 for (i = 0; i < 3; i++)
468 IAlloc_Free(allocMain, tempBuf[i]);
469 return res;
470}
471