repo sync | baa3858 | 2013-07-26 17:53:31 -0700 | [diff] [blame] | 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 | }
|