; LZMA decoder for *.7z archives. ; Based on C decoder in LZMA SDK (c) Igor Pavlov. ; Portions by Diamond, 2006, 2007. lzma_decoder: virtual at 0 .outStream rb streamInfo.size .inStream dd ? ; RangeDecoder data .inLen dd ? .inPtr dd ? .code dd ? .range dd ? ; parameters .pb db ? ; pos state bits (0 - 4) .lp db ? ; literal pos state bits (0 - 4) .lc db ? ; literal context bits (0 - 8) .previousByte db ? .posStateMask dd ? ; (1 shl .pb)-1 .literalPosMask dd ? ; (1 shl .lp)-1 ; constants .kNumPosBitsMax = 4 .kNumPosStatesMax = (1 shl .kNumPosBitsMax) .kLenNumLowBits = 3 .kLenNumLowSymbols = (1 shl .kLenNumLowBits) .kLenNumMidBits = 3 .kLenNumMidSymbols = (1 shl .kLenNumMidBits) .kLenNumHighBits = 8 .kLenNumHighSymbols = (1 shl .kLenNumHighBits) .LenChoice = 0 .LenChoice2 = 1 .LenLow = 2 .LenMid = (.LenLow + (.kNumPosStatesMax shl .kLenNumLowBits)) .LenHigh = (.LenMid + (.kNumPosStatesMax shl .kLenNumMidBits)) .kNumLenProbs = (.LenHigh + .kLenNumHighSymbols) .kNumStates = 12 .kNumLitStates = 7 .kStartPosModelIndex = 4 .kEndPosModelIndex = 14 .kNumFullDistances = (1 shl (.kEndPosModelIndex/2)) .kNumPosSlotBits = 6 .kNumLenToPosStates = 4 .kNumAlignBits = 4 .kAlignTableSize = (1 shl .kNumAlignBits) .kMatchMinLen = 2 .IsMatch = 0 .IsRep = (.IsMatch + (.kNumStates shl .kNumPosBitsMax)) .IsRepG0 = (.IsRep + .kNumStates) .IsRepG1 = (.IsRepG0 + .kNumStates) .IsRepG2 = (.IsRepG1 + .kNumStates) .IsRep0Long = (.IsRepG2 + .kNumStates) .PosSlot = (.IsRep0Long + (.kNumStates shl .kNumPosBitsMax)) .SpecPos = (.PosSlot + (.kNumLenToPosStates shl .kNumPosSlotBits)) .Align_ = (.SpecPos + .kNumFullDistances - .kEndPosModelIndex) .Lencoder = (.Align_ + .kAlignTableSize) .RepLencoder = (.Lencoder + .kNumLenProbs) .Literal = (.RepLencoder + .kNumLenProbs) LZMA_BASE_SIZE = 1846 ; must be ==Literal LZMA_LIT_SIZE = 768 .kNumTopBits = 24 .kTopValue = (1 shl .kNumTopBits) .kNumBitModelTotalBits = 11 .kBitModelTotal = (1 shl .kNumBitModelTotalBits) .kNumMoveBits = 5 ; variables .continue dd ? .ecx dd ? .outEnd dd ? .dictSize dd ? .state dd ? .rep0 dd ? .rep1 dd ? .rep2 dd ? .rep3 dd ? .p rd LZMA_BASE_SIZE .basesize = $ ; rd LZMA_LIT_SIZE shl (.lc+.lp) end virtual .fillBuf: mov ebp, eax mov ebx, [ebp+.state] jecxz .nodata add ecx, edi mov [ebp+.outEnd], ecx mov esi, [ebp+.inPtr] jmp [ebp+.continue] .nodata: popad ret .start: mov eax, [ebp+.inStream] call fillBuf mov esi, [eax+streamInfo.bufPtr] mov eax, [eax+streamInfo.bufDataLen] sub eax, 5 jb return.err mov [ebp+.inLen], eax inc esi lodsd bswap eax mov [ebp+.code], eax or [ebp+.range], -1 .main_loop: cmp edi, [ebp+.outEnd] jae .main_loop_done mov edx, edi and edx, [ebp+.posStateMask] mov eax, ebx shl eax, .kNumPosBitsMax add eax, edx lea eax, [ebp + .p + .IsMatch*4 + eax*4] call .RangeDecoderBitDecode jc .1 movzx eax, [ebp+.previousByte] mov ah, dl and ah, byte [ebp+.literalPosMask] mov cl, 8 sub cl, [ebp+.lc] shr eax, cl imul eax, LZMA_LIT_SIZE*4 lea eax, [ebp + eax + .p+.Literal*4] cmp ebx, .kNumLitStates jb .literal mov edx, edi sub edx, [ebp+.rep0] cmp edx, [ebp+streamInfo.bufPtr] jb .before_buf @@: mov dl, [edx] call .LzmaLiteralDecodeMatch jmp .got_byte .before_buf: add edx, [ebp+streamInfo.bufSize] cmp edx, [ebp+streamInfo.bufPtr] jb return.err jmp @b .literal: call .LzmaLiteralDecode .got_byte: mov [ebp+.previousByte], al stosb mov al, bl cmp bl, 4 jb @f mov al, 3 cmp bl, 10 jb @f mov al, 6 @@: sub bl, al jmp .main_loop .1: lea eax, [ebp + .p + .IsRep*4 + ebx*4] call .RangeDecoderBitDecode jnc .10 lea eax, [ebp + .p + .IsRepG0*4 + ebx*4] call .RangeDecoderBitDecode jc .111 mov eax, ebx shl eax, .kNumPosBitsMax add eax, edx lea eax, [ebp + .p + .IsRep0Long*4 + eax*4] call .RangeDecoderBitDecode jc .1101 cmp bl, 7 setae bl lea ebx, [9 + ebx + ebx] mov edx, edi sub edx, [ebp+.rep0] cmp edx, [ebp+streamInfo.bufPtr] jae @f add edx, [ebp+streamInfo.bufSize] cmp edx, [ebp+streamInfo.bufPtr] jb return.err @@: mov al, [edx] stosb mov [ebp+.previousByte], al jmp .main_loop .111: lea eax, [ebp + .p + .IsRepG1*4 + ebx*4] call .RangeDecoderBitDecode mov eax, [ebp+.rep1] jnc .l3 .l1: lea eax, [ebp + .p + .IsRepG2*4 + ebx*4] call .RangeDecoderBitDecode mov eax, [ebp+.rep2] jnc .l2 xchg [ebp+.rep3], eax .l2: push [ebp+.rep1] pop [ebp+.rep2] .l3: xchg eax, [ebp+.rep0] mov [ebp+.rep1], eax .1101: lea eax, [ebp + .p + .RepLencoder*4] call .LzmaLenDecode cmp bl, 7 setc bl adc bl, bl xor bl, 3 add bl, 8 jmp .repmovsb .10: mov eax, [ebp+.rep0] xchg eax, [ebp+.rep1] xchg eax, [ebp+.rep2] xchg eax, [ebp+.rep3] cmp bl, 7 setc bl adc bl, bl xor bl, 3 add bl, 7 lea eax, [ebp + .p + .Lencoder*4] call .LzmaLenDecode mov eax, .kNumLenToPosStates-1 cmp eax, ecx jb @f mov eax, ecx @@: push ecx mov ecx, .kNumPosSlotBits shl eax, cl lea eax, [ebp + .p+.PosSlot*4 + eax*4] call .RangeDecoderBitTreeDecode mov [ebp+.rep0], ecx cmp ecx, .kStartPosModelIndex jb .l6 push ecx mov eax, ecx and eax, 1 shr ecx, 1 or eax, 2 dec ecx shl eax, cl mov [ebp+.rep0], eax pop edx cmp edx, .kEndPosModelIndex jae .l5 sub eax, edx lea eax, [ebp + .p + (.SpecPos - 1)*4 + eax*4] call .RangeDecoderReverseBitTreeDecode add [ebp+.rep0], ecx jmp .l6 .l5: sub ecx, .kNumAlignBits call .RangeDecoderDecodeDirectBits mov ecx, .kNumAlignBits shl eax, cl add [ebp+.rep0], eax lea eax, [ebp+.p+.Align_*4] call .RangeDecoderReverseBitTreeDecode add [ebp+.rep0], ecx .l6: pop ecx inc [ebp+.rep0] jz .main_loop_done .repmovsb: add ecx, .kMatchMinLen .repmovsbz: push esi .repmovsbr: mov eax, [ebp+.rep0] cmp eax, [ebp+.dictSize] jae return.err mov esi, edi sub esi, eax mov eax, [ebp+streamInfo.bufPtr] sub eax, esi ja .repmovsb0 mov eax, [ebp+.outEnd] sub eax, edi push ecx cmp ecx, eax jb @f mov ecx, eax @@: sub [esp], ecx rep movsb pop ecx jz .repmovsb1 pop [ebp+.inPtr] mov [ebp+.state], ebx mov [ebp+.ecx], ecx mov [ebp+.continue], .restart_repmovsb popad ret .repmovsb0: mov edx, [ebp+.dictSize] cmp edx, [ebp+streamInfo.bufSize] jnz return.err add esi, edx push ecx cmp ecx, eax jb @f mov ecx, eax @@: mov eax, [ebp+.outEnd] sub eax, edi cmp ecx, eax jb @f mov ecx, eax @@: sub [esp], ecx rep movsb pop ecx jnz .repmovsbr .repmovsb1: pop esi mov al, [edi-1] mov [ebp+.previousByte], al jmp .main_loop .main_loop_done: mov [ebp+.state], ebx mov [ebp+.continue], .main_loop mov [ebp+.inPtr], esi popad ret .restart_repmovsb: mov ecx, [ebp+.ecx] jmp .repmovsbz .RangeDecoderBitDecode: ; in: eax->prob ; out: CF=bit; destroys eax push edx mov edx, [ebp+.range] shr edx, .kNumBitModelTotalBits imul edx, [eax] cmp [ebp+.code], edx jae .ae mov [ebp+.range], edx mov edx, .kBitModelTotal sub edx, [eax] shr edx, .kNumMoveBits add [eax], edx clc .n: lahf cmp [ebp+.range], .kTopValue jae @f shl [ebp+.range], 8 shl [ebp+.code], 8 sub [ebp+.inLen], 1 js .refill1 .refilled1: lodsb mov byte [ebp+.code], al @@: sahf pop edx ret .ae: sub [ebp+.range], edx sub [ebp+.code], edx mov edx, [eax] shr edx, .kNumMoveBits sub [eax], edx stc jmp .n .refill1: push eax call .refill pop eax jmp .refilled1 .refill: mov eax, [ebp+.inStream] cmp dword [eax+streamInfo.fullSize+4], 0 jnz @f cmp dword [eax+streamInfo.fullSize], 0 jz return.err @@: call fillBuf mov esi, [eax+streamInfo.bufPtr] mov eax, [eax+streamInfo.bufDataLen] dec eax js return.err mov [ebp+.inLen], eax ret .refill2: call .refill jmp .refilled2 .RangeDecoderDecodeDirectBits: ; in: ecx=numTotalBits ; out: eax=result; destroys edx xor eax, eax .l: shr [ebp+.range], 1 shl eax, 1 mov edx, [ebp+.code] sub edx, [ebp+.range] jb @f mov [ebp+.code], edx or eax, 1 @@: cmp [ebp+.range], .kTopValue jae @f shl [ebp+.range], 8 shl [ebp+.code], 8 push eax dec [ebp+.inLen] js .refill2 .refilled2: lodsb mov byte [ebp+.code], al pop eax @@: loop .l ret .LzmaLiteralDecode: ; in: eax->probs ; out: al=byte; destroys edx push ecx mov ecx, 1 @@: push eax lea eax, [eax+ecx*4] call .RangeDecoderBitDecode pop eax adc cl, cl jnc @b .LzmaLiteralDecode.ret: mov al, cl pop ecx ret .LzmaLiteralDecodeMatch: ; in: eax->probs, dl=matchByte ; out: al=byte; destroys edx push ecx mov ecx, 1 .LzmaLiteralDecodeMatch.1: add dl, dl setc ch push eax lea eax, [eax+ecx*4+0x100*4] call .RangeDecoderBitDecode pop eax adc cl, cl jc .LzmaLiteralDecode.ret xor ch, cl test ch, 1 mov ch, 0 jnz @b jmp .LzmaLiteralDecodeMatch.1 .LzmaLenDecode: ; in: eax->prob, edx=posState ; out: ecx=len push eax add eax, .LenChoice*4 call .RangeDecoderBitDecode pop eax jnc .0 push eax add eax, .LenChoice2*4 call .RangeDecoderBitDecode pop eax jc @f mov ecx, .kLenNumMidBits shl edx, cl lea eax, [eax + .LenMid*4 + edx*4] call .RangeDecoderBitTreeDecode add ecx, .kLenNumLowSymbols ret @@: add eax, .LenHigh*4 mov ecx, .kLenNumHighBits call .RangeDecoderBitTreeDecode add ecx, .kLenNumLowSymbols + .kLenNumMidSymbols ret .0: mov ecx, .kLenNumLowBits shl edx, cl lea eax, [eax + .LenLow*4 + edx*4] .RangeDecoderBitTreeDecode: ; in: eax->probs,ecx=numLevels ; out: ecx=length; destroys edx push ebx mov edx, 1 mov ebx, edx @@: push eax lea eax, [eax+edx*4] call .RangeDecoderBitDecode pop eax adc dl, dl add bl, bl loop @b sub dl, bl pop ebx mov ecx, edx ret .RangeDecoderReverseBitTreeDecode: ; in: eax->probs,ecx=numLevels ; out: ecx=length; destroys edx push ebx ecx mov edx, 1 xor ebx, ebx @@: push eax lea eax, [eax+edx*4] call .RangeDecoderBitDecode lahf adc edx, edx sahf rcr ebx, 1 pop eax loop @b pop ecx rol ebx, cl mov ecx, ebx pop ebx ret ; LZMA parameters: ; db lc + 9 * (lp + 5 * pb) ; dd dictionarySize lzma_get_buf_size: cmp dword [esi-4], 5 jb return.err push ecx lodsb aam 9 mov cl, al mov al, ah aam 5 add cl, al mov eax, LZMA_LIT_SIZE shl eax, cl lea eax, [lzma_decoder.basesize+eax*4] pop ecx mov edx, [esi] ret lzma_init_decoder: lodsb aam 9 mov [ebp+lzma_decoder.lc], al mov al, ah aam 5 mov [ebp+lzma_decoder.lp], al mov [ebp+lzma_decoder.pb], ah cmp ah, lzma_decoder.kNumPosBitsMax ja return.err mov cl, ah lodsd mov [ebp+lzma_decoder.dictSize], eax push 1 pop eax shl eax, cl dec eax mov [ebp+lzma_decoder.posStateMask], eax mov cl, [ebp+lzma_decoder.lp] push 1 pop eax shl eax, cl dec eax mov [ebp+lzma_decoder.literalPosMask], eax mov [ebp+streamInfo.fillBuf], lzma_decoder.fillBuf mov [ebp+lzma_decoder.continue], lzma_decoder.start xor eax, eax mov [ebp+lzma_decoder.previousByte], al mov [ebp+lzma_decoder.state], eax inc eax lea edi, [ebp+lzma_decoder.rep0] stosd stosd stosd mov eax, LZMA_LIT_SIZE mov cl, [ebp+lzma_decoder.lc] add cl, [ebp+lzma_decoder.lp] shl eax, cl lea ecx, [eax+lzma_decoder.Literal] mov eax, lzma_decoder.kBitModelTotal/2 lea edi, [ebp+lzma_decoder.p] rep stosd ret