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