1 | /* 7zDec.c -- Decoding from 7z folder |
2 | 2010-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 | |
33 | typedef 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 | |
45 | static 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 | |
66 | static 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 | |
127 | static 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 | |
175 | static 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 | |
224 | static 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 | |
243 | static 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 | |
258 | static 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 | |
269 | static 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 | |
324 | static 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 | |
335 | static 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 | |
459 | SRes 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 | |