; PPMD decoder, ported from C++ sources of 7-Zip (c) Igor Pavlov
; C++ code is based on Dmitry Shkarin's PPMdH code
uglobal
ppmd_decoder.NS2Indx    rb      256
ppmd_decoder.NS2BSIndx  rb      256
ppmd_decoder.HB2Flag    rb      256
ppmd_decoder.Indx2Units rb      ppmd_decoder.N_INDEXES
ppmd_decoder.Units2Indx rb      128
endg

iglobal
label ppmd_decoder.InitBinEsc word
        dw      0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051
ppmd_decoder.ExpEscape db 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2
endg

init_ppmd:
; NS2Indx table
        mov     edi, ppmd_decoder.NS2Indx
        xor     eax, eax
        stosb
        inc     eax
        stosb
        inc     eax
        stosb
        mov     edx, 3-256
@@:
        lea     ecx, [eax-1]
        inc     eax
        add     edx, ecx
        jc      @f
        rep     stosb
        jmp     @b
@@:
        sub     ecx, edx
        rep     stosb
; NS2BSIndx table
        xor     eax, eax
        stosb
        add     al, 2
        stosb
        add     al, 2
        mov     cl, 9
        rep     stosb
        add     al, 2
        mov     cl, 256-11
        rep     stosb
; HB2Flag table
        mov     cl, 0x40/4
        xor     eax, eax
        rep     stosd
        mov     al, 8
        mov     cl, 0x100-0x40
        rep     stosb
; Indx2Units table
        mov     eax, 0x04030201
        stosd
        mov     eax, 0x0C0A0806
        stosd
        mov     eax, 0x1815120F
        stosd
        mov     al, 0x1C
@@:
        stosb
        add     al, 4
        cmp     al, 0x80
        jbe     @b
; Units2Indx table
        xor     eax, eax
        xor     edx, edx
        inc     edx
        xor     ecx, ecx
@@:
        cmp     [ppmd_decoder.Indx2Units+eax], dl
        adc     al, 0
        stosb
        inc     edx
        cmp     dl, 0x80
        jbe     @b
        ret

ppmd_decoder:
virtual at 0
; base is standard structure
.outStream      rb      streamInfo.size
.inStream       dd      ?

; RangeDecoder data
.inLen          dd      ?
.inPtr          dd      ?
.code           dd      ?
.range          dd      ?

.outSize        dd      ?       ; number of bytes rest for output

; PPMD data
.order          db      ?
.GlueCount      db      ?
.bInited        db      ?
                rb      1
.usedMemorySize dd      ?

; CSubAllocator constants
.N1 = 4
.N2 = 4
.N3 = 4
.N4 = (128+3-1*.N1-2*.N2-3*.N3)/4
.UNIT_SIZE = 12
.N_INDEXES = .N1+.N2+.N3+.N4
.kExtraSize = .UNIT_SIZE*3
.kMaxMemBlockSize = 0xFFFFFFFF - .kExtraSize

; CSubAllocator data
.HeapStart      dd      ?
.LoUnit         dd      ?
.HiUnit         dd      ?
.pText          dd      ?
.UnitsStart     dd      ?
.FreeList       rd      .N_INDEXES

; Context constants
.INT_BITS = 7
.PERIOD_BITS = 7
.TOT_BITS = .INT_BITS + .PERIOD_BITS
.INTERVAL = 1 shl .INT_BITS
.BIN_SCALE = 1 shl .TOT_BITS
.MAX_FREQ = 124

.kMaxOrderCompress = 32
.MAX_O = 255

; CDecodeInfo (inherits from CInfo) data
; SEE2_CONTEXT is 4 bytes long
.SEE2Cont       rd      25*16
.DummySEE2Cont  dd      ?
.MinContext     dd      ?
.MaxContext     dd      ?
.FoundState     dd      ?       ; found next state transition
.NumMasked      dd      ?
.InitEsc        dd      ?
.OrderFall      dd      ?
.RunLength      dd      ?
.InitRL         dd      ?
.CharMask       rb      256
.EscCount       db      ?
.PrintCount     db      ?
.PrevSuccess    db      ?
.HiBitsFlag     db      ?
.BinSumm        rw      128*64

.basesize = $
.Base:
;               rb      .kExtraSize + [.usedMemorySize]
end virtual

.init:
        mov     eax, [eax+.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
        mov     [ebp+.inPtr], esi
        bswap   eax
        mov     [ebp+.code], eax
        or      [ebp+.range], -1
        mov     [ebp+.bInited], 1
        call    .StartModelRare
        mov     eax, ebp
        jmp     .mainloop

.fillBuf:
        mov     ebp, eax
        mov     [eax+.outSize], ecx
        cmp     [eax+.bInited], 0
        jz      .init
.mainloop:
        sub     [ebp+.outSize], 1
        js      .mainloopdone
;        cmp     edi, 0xde070+0x18
;        jnz     @f
;        int3
;@@:
        call    .DecodeSymbol
        jmp     .mainloop
.mainloopdone:
        popad
        ret

.GetBinSumm:
; CInfo::GetBinSumm(ebx=rs, ecx=numStates)
        movzx   eax, [ebp+.PrevSuccess]
        movzx   edx, [.NS2BSIndx+ecx-1]
        add     eax, edx
        mov     edx, [ebp+.FoundState]
        movzx   edx, byte [edx]
        movzx   edx, [.HB2Flag+edx]
        mov     [ebp+.HiBitsFlag], dl
        add     eax, edx
        movzx   edx, byte [ebx]
        movzx   edx, [.HB2Flag+edx]
        lea     eax, [eax+edx*2]
        mov     edx, [ebp+.RunLength]
        shr     edx, 26
        and     edx, 0x20
        add     eax, edx
        movzx   edx, byte [ebx+1]
        shl     edx, 6
        add     eax, edx
        lea     ecx, [ebp+.BinSumm+eax*2-2*64]
        ret

.StartModelRare:
; CInfo::StartModelRare(.order)
        mov     [ebp+.EscCount], 1
        mov     [ebp+.PrintCount], 1
; N.B.
; 1. Original code has some handling of [.order]<2, but this handling is incorrect
;    and causes exception (access violation).
; 2. 7-Zip never generates archives with [.order]<2 due to input preprocessing
;    (for PPMd method in switch -mo=<n> archiver checks that 2 <= n <= 32).
; 3. If manually created archive says [.order]<2, the exception will be generated
;    in StartModelRare, but it will be handled in Code() resulting in "data error".
        cmp     [ebp+.order], 2
        jb      return.err
        mov     byte [ebp+.DummySEE2Cont+2], .PERIOD_BITS

.RestartModelRare:
; CInfo::RestartModelRare(void)
        push    edi
        lea     edi, [ebp+.CharMask]
        xor     eax, eax
        push    0x40
        pop     ecx
        rep     stosd
; CSubAllocator::InitSubAllocator start
        mov     [ebp+.GlueCount], al
        lea     edi, [ebp+.FreeList]
        mov     cl, .N_INDEXES
        rep     stosd
        mov     ebx, [ebp+.HeapStart]
        mov     [ebp+.pText], ebx
        add     ebx, [ebp+.usedMemorySize]
        mov     [ebp+.HiUnit], ebx
        mov     eax, [ebp+.usedMemorySize]
        xor     edx, edx
        mov     cl, 8*.UNIT_SIZE
        div     ecx
        imul    eax, 7*.UNIT_SIZE
        sub     ebx, eax
        mov     [ebp+.LoUnit], ebx
        mov     [ebp+.UnitsStart], ebx
; CSubAllocator::InitSubAllocator end
        pop     edi
        movzx   eax, [ebp+.order]
        cmp     al, 12
        jb      @f
        mov     al, 12
@@:
        neg     eax
        dec     eax
        mov     [ebp+.InitRL], eax
        mov     [ebp+.RunLength], eax
        call    .AllocContext
        mov     [ebp+.MinContext], eax
        mov     [ebp+.MaxContext], eax
        and     dword [eax+8], 0
        mov     esi, eax
        movzx   edx, [ebp+.order]
        mov     [ebp+.OrderFall], edx
        mov     dword [eax], 257*10000h+256
        mov     ecx, 256/2
        call    .AllocUnits
        mov     [ebp+.FoundState], eax
        mov     [esi+4], eax
        push    edi
        mov     edi, eax
        xor     eax, eax
        mov     [ebp+.PrevSuccess], al
@@:
        stosb
        mov     byte [edi], 1
        and     dword [edi+1], 0
        add     edi, 5
        inc     al
        jnz     @b
        lea     edi, [ebp+.BinSumm]
        push    2
        pop     ecx
.rmr1:
        mov     esi, .InitBinEsc
@@:
        lodsw
        xor     edx, edx
        div     ecx
        sub     eax, .BIN_SCALE
        neg     eax
        mov     [edi+2*8], ax
        mov     [edi+2*16], ax
        mov     [edi+2*24], ax
        mov     [edi+2*32], ax
        mov     [edi+2*40], ax
        mov     [edi+2*48], ax
        mov     [edi+2*56], ax
        stosw
        cmp     esi, .InitBinEsc+2*8
        jb      @b
        add     edi, 128-16
        inc     ecx
        cmp     ecx, 128+2
        jb      .rmr1
        lea     edi, [ebp+.SEE2Cont]
        mov     eax, (10 shl (.PERIOD_BITS-4)) + ((.PERIOD_BITS-4) shl 16) + (4 shl 24)
        push    25
        pop     edx
@@:
        push    16
        pop     ecx
        rep     stosd
        add     ax, 5 shl (.PERIOD_BITS-4)
        dec     edx
        jnz     @b
        pop     edi
        ret

.CreateSuccessors:
; CInfo::CreateSuccessors(bool al=skip,STATE* esi=p1)
        push    ebx edi
        mov     ebx, [ebp+.MinContext]  ; ebx=pc
        mov     ecx, [ebp+.FoundState]
        mov     ecx, [ecx+2]            ; ecx=UpBranch
        sub     esp, .MAX_O*4           ; esp=ps
        mov     edi, esp                ; edi=pps
        test    al, al
        jnz     @f
        mov     eax, [ebp+.FoundState]
        stosd
        cmp     dword [ebx+8], 0
        jz      .csnoloop
@@:
        test    esi, esi
        jz      .csloopstart
        mov     edx, esi                ; edx=p
        mov     ebx, [ebx+8]
        jmp     .csloopentry
.csloopstart:
        mov     ebx, [ebx+8]
        lea     edx, [ebx+2]
        cmp     word [ebx], 1
        jz      .csloopentry
        mov     edx, [ebx+4]
        mov     eax, [ebp+.FoundState]
        sub     edx, 6
        mov     al, [eax]
@@:
        add     edx, 6
        cmp     al, [edx]
        jnz     @b
.csloopentry:
        cmp     ecx, [edx+2]
        jz      @f
        mov     ebx, [edx+2]
        jmp     .csnoloop
@@:
        mov     [edi], edx
        add     edi, 4
        cmp     dword [ebx+8], 0
        jnz     .csloopstart
.csnoloop:
        cmp     edi, esp
        jz      .csr
        push    eax
        push    eax
        mov     al, [ecx]
        mov     [esp], al
        add     ecx, 1
        mov     [esp+2], ecx
        mov     ah, [ebx+3]
        cmp     word [ebx], 1
        jz      .cs2
        mov     edx, [ebx+4]
        sub     edx, 6
@@:
        add     edx, 6
        cmp     [edx], al
        jnz     @b
        movzx   edx, byte [edx+1]
        sub     edx, 1          ; edx=cf
        movzx   ecx, word [ebx+2]
        movzx   eax, word [ebx]
        sub     ecx, eax
        sub     ecx, edx        ; ecx=s0
        lea     eax, [edx+edx]
        cmp     eax, ecx
        ja      .cs0
        lea     eax, [edx*5]
        cmp     eax, ecx
        seta    ah
        jmp     .cs1
.cs0:
        lea     eax, [eax+ecx*2]
        lea     eax, [eax+ecx-1]
        add     ecx, ecx
        xor     edx, edx
        div     ecx
        mov     ah, al
.cs1:
        add     ah, 1
.cs2:
        mov     [esp+1], ah
        sub     edi, 8
.cs3:
; PPM_CONTEXT::createChild(this=ebx,pStats=[edi+4],FirstState=esp) begin
        call    .AllocContext
        test    eax, eax
        jz      .csr0
        mov     word [eax], 1
        mov     dx, [esp]
        mov     [eax+2], dx
        mov     edx, [esp+2]
        mov     [eax+4], edx
        mov     [eax+8], ebx
        mov     edx, [edi+4]
        mov     [edx+2], eax
; PPM_CONTEXT::createChild end
        mov     ebx, eax
        sub     edi, 4
        cmp     edi, esp
        jnz     .cs3
        pop     eax eax
.csr:
        mov     eax, ebx
@@:
        add     esp, .MAX_O*4
        pop     edi ebx
        ret
.csr0:
        pop     eax eax
        xor     eax, eax
        jmp     @b

; CInfo::UpdateModel(void)
.UpdateModel:
        mov     ebx, [ebp+.FoundState]
        xor     esi, esi                ; esi=p
        movzx   eax, word [ebx]
        mov     ebx, [ebx+2]            ; ebx=fs.Successor
        push    eax
        cmp     ah, .MAX_FREQ/4
        jae     .um2
        mov     eax, [ebp+.MinContext]
        mov     eax, [eax+8]
        test    eax, eax
        jz      .um2
        cmp     word [eax], 1
        jz      .um1
        push    eax
        mov     esi, [eax+4]
        mov     al, [esp+4]
        cmp     al, [esi]
        jz      .um0
@@:
        add     esi, 6
        cmp     al, [esi]
        jnz     @b
        mov     al, [esi+1]
        cmp     al, [esi-6+1]
        jb      @f
        mov     eax, [esi]
        xchg    [esi-6], eax
        mov     [esi], eax
        mov     ax, [esi+4]
        xchg    [esi-6+4], ax
        mov     [esi+4], ax
        sub     esi, 6
@@:
.um0:
        pop     eax
        cmp     byte [esi+1], .MAX_FREQ-9
        jae     @f
        add     byte [esi+1], 2
        add     word [eax+2], 2
@@:
        jmp     .um2
.um1:
        lea     esi, [eax+2]
        cmp     byte [esi+1], 32
        adc     byte [esi+1], 0
.um2:
        cmp     [ebp+.OrderFall], 0
        jnz     .um3
        pop     eax
        mov     al, 1
        call    .CreateSuccessors
        mov     [ebp+.MinContext], eax
        mov     [ebp+.MaxContext], eax
        mov     edx, [ebp+.FoundState]
        mov     [edx+2], eax
        test    eax, eax
        jz      .RestartModel
        ret
.um3:
        mov     edx, [ebp+.pText]
        mov     al, [esp]
        mov     [edx], al
        add     edx, 1          ; edx=Successor
        mov     [ebp+.pText], edx
        cmp     edx, [ebp+.UnitsStart]
        jae     .RestartModelPop
        test    ebx, ebx
        jz      .um4
        cmp     ebx, [ebp+.pText]
        ja      @f
        push    edx
        xor     eax, eax
        call    .CreateSuccessors
        pop     edx
        mov     ebx, eax
        test    eax, eax
        jz      .RestartModelPop
@@:
        sub     [ebp+.OrderFall], 1
        jnz     @f
        mov     edx, ebx
        xor     ecx, ecx
        mov     eax, [ebp+.MinContext]
        cmp     eax, [ebp+.MaxContext]
        setnz   cl
        sub     [ebp+.pText], ecx
@@:
        jmp     .um5
.um4:
        mov     eax, [ebp+.FoundState]
        mov     [eax+2], edx
        mov     ebx, [ebp+.MinContext]
.um5:
        mov     eax, [ebp+.MinContext]
        movzx   ecx, word [eax] ; ecx=ns
        movzx   eax, word [eax+2]
        sub     eax, ecx
        push    eax
        movzx   eax, byte [esp+5]
        sub     eax, 1
        sub     [esp], eax      ; [esp]=s0
        mov     esi, [ebp+.MaxContext]  ; ebx=pc
        cmp     esi, [ebp+.MinContext]
        jz      .um12
.um6:
        movzx   eax, word [esi]
        cmp     eax, 1
        jz      .um8
        push    eax
        shr     eax, 1
        jc      .um7
        push    esi
        mov     esi, [esi+4]
        call    .ExpandUnits
        pop     esi
        mov     [esi+4], eax
        test    eax, eax
        jz      .RestartModelPop3
.um7:
        pop     eax
        add     eax, eax
        cmp     eax, ecx
        adc     word [esi+2], 0
        add     eax, eax
        cmp     eax, ecx
        ja      @f
        lea     eax, [eax+eax+1]
        cmp     word [esi+2], ax
        ja      @f
        add     word [esi+2], 2
@@:
        push    edx
        jmp     .um9
.um8:
        push    edx ecx
        mov     ecx, 1
        call    .AllocUnits
        pop     ecx
        test    eax, eax
        jz      .RestartModelPop3
        mov     dx, [esi+2]
        mov     [eax], dx
        mov     edx, [esi+4]
        mov     [eax+2], edx
        mov     [esi+4], eax
        movzx   edx, byte [eax+1]
        add     edx, edx
        cmp     edx, (.MAX_FREQ/4-1)*2
        jb      @f
        mov     edx, .MAX_FREQ-4
@@:
        mov     [eax+1], dl
        add     edx, [ebp+.InitEsc]
        cmp     ecx, 4
        sbb     edx, -1
        mov     [esi+2], dx
.um9:
        movzx   edx, word [esi+2]
        mov     eax, [esp+4]
        push    ecx
        lea     ecx, [eax+edx]  ; ecx=sf
        add     edx, 6
        movzx   eax, byte [esp+13]
        add     eax, eax
        imul    eax, edx        ; eax=cf
        lea     edx, [ecx*3]
        add     edx, edx
        cmp     eax, edx
        jae     .um10
        mov     edx, 1
        cmp     ecx, eax
        adc     edx, 0
        shl     ecx, 2
        add     eax, 1
        cmp     ecx, eax
        adc     edx, 0
        add     word [esi+2], 3
        jmp     .um11
.um10:
        lea     ecx, [ecx*3]
        lea     edx, [ecx*3]
        add     eax, 1
        push    4
        cmp     edx, eax
        adc     dword [esp], 0
        add     edx, ecx
        cmp     edx, eax
        adc     dword [esp], 0
        add     edx, ecx
        cmp     edx, eax
        adc     dword [esp], 0
        pop     edx
        add     [esi+2], dx
.um11:
        movzx   eax, word [esi]
        lea     eax, [eax*3]
        add     eax, eax
        add     eax, [esi+4]
        mov     ecx, [esp+4]
        mov     [eax+2], ecx
        mov     cl, [esp+12]
        mov     [eax], cl
        mov     [eax+1], dl
        add     word [esi], 1
        pop     ecx edx
        mov     esi, [esi+8]
        cmp     esi, [ebp+.MinContext]
        jnz     .um6
.um12:
        pop     eax
        pop     eax
        mov     [ebp+.MinContext], ebx
        mov     [ebp+.MaxContext], ebx
        ret
.RestartModelPop3:
        pop     eax
        pop     eax
.RestartModelPop:
        pop     eax
.RestartModel:
        call    .RestartModelRare
        mov     [ebp+.EscCount], 0
        mov     [ebp+.PrintCount], 0xFF
        ret

.rescale:
        mov     esi, [ebp+.MinContext]
        movzx   ecx, word [esi]
        push    ecx     ; [esp]=OldNS
        sub     ecx, 1
        mov     ebx, [ebp+.FoundState]
        cmp     ebx, [esi+4]
        jz      .r1
.r0:
        mov     ax, [ebx]
        xchg    [ebx-6], ax
        mov     [ebx], ax
        mov     eax, [ebx+2]
        xchg    [ebx-6+2], eax
        mov     [ebx+2], eax
        sub     ebx, 6
        cmp     ebx, [esi+4]
        jnz     .r0
.r1:
        add     byte [ebx+1], 4
        add     word [esi+2], 4
        movzx   eax, byte [ebx+1]
        movzx   edx, word [esi+2]
        sub     edx, eax        ; edx=EscFreq
        cmp     [ebp+.OrderFall], 1
        sbb     eax, -1
        shr     eax, 1
        mov     [ebx+1], al
        mov     [esi+2], ax
.r2:
        add     ebx, 6
        movzx   eax, byte [ebx+1]
        sub     edx, eax
        cmp     [ebp+.OrderFall], 1
        sbb     eax, -1
        shr     eax, 1
        mov     [ebx+1], al
        add     [esi+2], ax
        cmp     al, [ebx-6+1]
        jbe     .r3
        push    ecx
        push    ebx
        push    dword [ebx]
        push    word [ebx+4]
@@:
        mov     ecx, [ebx-6]
        mov     [ebx], ecx
        mov     cx, [ebx-6+4]
        mov     [ebx+4], cx
        sub     ebx, 6
        cmp     ebx, [esi+4]
        jz      @f
        cmp     al, [ebx-6+1]
        ja      @b
@@:
        pop     word [ebx+4]
        pop     dword [ebx]
        pop     ebx
        pop     ecx
.r3:
        sub     ecx, 1
        jnz     .r2
        cmp     byte [ebx+1], 0
        jnz     .r4
@@:
        add     ecx, 1
        sub     ebx, 6
        cmp     byte [ebx+1], 0
        jz      @b
        add     edx, ecx
        sub     word [esi], cx
        cmp     word [esi], 1
        jnz     .r4
        pop     ebx
        mov     eax, [esi+4]
        movzx   ecx, word [eax+4]
        push    ecx
        push    dword [eax]
        movzx   eax, byte [eax+1]
@@:
        add     eax, 1
        shr     eax, 1
        shr     edx, 1
        cmp     edx, 1
        ja      @b
        mov     [esp+1], al
        add     ebx, 1
        shr     ebx, 1
        mov     eax, [esi+4]
        call    .FreeUnits
        lea     ebx, [esi+2]
        mov     [ebp+.FoundState], ebx
        pop     dword [ebx]
        pop     eax
        mov     [ebx+4], ax
        ret
.r4:
        add     edx, 1
        shr     edx, 1
        add     [esi+2], dx
        pop     ebx
        add     ebx, 1
        shr     ebx, 1
        movzx   ecx, word [esi]
        add     ecx, 1
        shr     ecx, 1
        cmp     ebx, ecx
        jz      @f
        mov     eax, [esi+4]
        call    .ShrinkUnits
        mov     [esi+4], eax
@@:
        mov     eax, [esi+4]
        mov     [ebp+.FoundState], eax
        ret

.DecodeSymbol:
; CDecodeInfo::DecodeSymbol
        mov     esi, [ebp+.MinContext]
        cmp     word [esi], 1
        jz      .binsymbol
; CDecodeInfo::DecodeSymbol1 start
        mov     ebx, [esi+4]    ; state
        movzx   ecx, word [esi+2]
        mov     eax, [ebp+.range]
        xor     edx, edx
        div     ecx
        mov     [ebp+.range], eax
        mov     ecx, eax
        mov     eax, [ebp+.code]
        xor     edx, edx
        div     ecx
        movzx   edx, byte [ebx+1]
        cmp     eax, edx
        jae     .ds0
        push    edx
        add     edx, edx
        cmp     dx, [esi+2]
        pop     edx
        seta    [ebp+.PrevSuccess]
        movzx   eax, [ebp+.PrevSuccess]
        add     [ebp+.RunLength], eax
        xor     eax, eax
        call    .RangeDecoder.Decode
        mov     [ebp+.FoundState], ebx
        add     edx, 4
        mov     [ebx+1], dl
        add     word [esi+2], 4
        cmp     edx, .MAX_FREQ
        jbe     @f
        call    .rescale
@@:
        jmp     .dscmn
.ds0:
        mov     [ebp+.PrevSuccess], 0
        movzx   ecx, word [esi]
        sub     ecx, 1
        push    eax
.ds1:
        add     ebx, 6
        movzx   eax, byte [ebx+1]
        add     edx, eax
        cmp     edx, [esp]
        ja      .ds2
        sub     ecx, 1
        jnz     .ds1
        pop     eax
        mov     eax, [ebp+.FoundState]
        movzx   eax, byte [eax]
        mov     al, [.HB2Flag+eax]
        mov     [ebp+.HiBitsFlag], al
        mov     eax, edx
        movzx   edx, word [esi+2]
        sub     edx, eax
        call    .RangeDecoder.Decode
        mov     al, [ebp+.EscCount]
        movzx   edx, byte [ebx]
        mov     [ebp+.CharMask+edx], al
        movzx   ecx, word [esi]
        mov     [ebp+.NumMasked], ecx
        sub     ecx, 1
@@:
        sub     ebx, 6
        movzx   edx, byte [ebx]
        mov     [ebp+.CharMask+edx], al
        sub     ecx, 1
        jnz     @b
        mov     [ebp+.FoundState], ecx
        jmp     .dscmn
.ds2:
        pop     eax
        mov     eax, edx
        movzx   edx, byte [ebx+1]
        sub     eax, edx
        call    .RangeDecoder.Decode
.update1:
        mov     [ebp+.FoundState], ebx
        add     byte [ebx+1], 4
        add     word [esi+2], 4
        mov     al, [ebx+1]
        cmp     al, [ebx-6+1]
        jbe     @f
        mov     eax, [ebx]
        xchg    eax, [ebx-6]
        mov     [ebx], eax
        mov     ax, [ebx+4]
        xchg    ax, [ebx-6+4]
        mov     [ebx+4], ax
        sub     ebx, 6
        mov     [ebp+.FoundState], ebx
        cmp     byte [ebx+1], .MAX_FREQ
        jbe     @f
        call    .rescale
@@:
        jmp     .dscmn
; CDecodeInfo::DecodeSymbol1 end
.binsymbol:
; CDecodeInfo::DecodeBinSymbol start
        lea     ebx, [esi+2]
        mov     ecx, [esi+8]
        movzx   ecx, word [ecx]
        call    .GetBinSumm
        movzx   eax, word [ecx]
        call    .RangeDecoder.DecodeBit
        jc      .ds3
        mov     [ebp+.FoundState], ebx
        cmp     byte [ebx+1], 128
        adc     byte [ebx+1], 0
        movzx   eax, word [ecx]
        add     eax, 1 shl (.PERIOD_BITS-2)
        shr     eax, .PERIOD_BITS
        sub     eax, .INTERVAL
        sub     [ecx], ax
        mov     [ebp+.PrevSuccess], 1
        add     [ebp+.RunLength], 1
        jmp     .dscmn
.ds3:
        movzx   eax, word [ecx]
        add     eax, 1 shl (.PERIOD_BITS-2)
        shr     eax, .PERIOD_BITS
        sub     [ecx], ax
        movzx   eax, word [ecx]
        shr     eax, 10
        movzx   eax, [.ExpEscape+eax]
        mov     [ebp+.InitEsc], eax
        mov     [ebp+.NumMasked], 1
        mov     al, [ebp+.EscCount]
        movzx   edx, byte [ebx]
        mov     [ebp+.CharMask+edx], al
        mov     [ebp+.PrevSuccess], 0
        and     [ebp+.FoundState], 0
; CDecodeInfo::DecodeBinSymbol end
.dscmn:
        cmp     [ebp+.FoundState], 0
        jnz     .dsfnd
.ds4:
        add     [ebp+.OrderFall], 1
        mov     eax, [ebp+.MinContext]
        mov     eax, [eax+8]
        test    eax, eax
        jz      return.err      ; no end-of-stream mark
        mov     [ebp+.MinContext], eax
        movzx   ecx, word [eax]
        sub     ecx, [ebp+.NumMasked]
        jz      .ds4
; CDecodeInfo::DecodeSymbol2 start
        call    .makeEscFreq2
        push    eax
        mov     ebx, [esi+4]
        sub     ebx, 6
        sub     esp, 256*4
        mov     esi, esp
        xor     eax, eax
        push    eax
@@:
        add     ebx, 6
        mov     al, [ebx]
        mov     al, [ebp+.CharMask+eax]
        cmp     al, [ebp+.EscCount]
        jz      @b
        mov     al, [ebx+1]
        add     [esp], eax
        mov     [esi], ebx
        add     esi, 4
        sub     ecx, 1
        jnz     @b
        add     edx, [esp]
        mov     ecx, edx
        mov     eax, [ebp+.range]
        xor     edx, edx
        div     ecx
        mov     [ebp+.range], eax
        mov     eax, [ebp+.code]
        xor     edx, edx
        div     [ebp+.range]
        cmp     eax, [esp]
        jae     .ds5
        pop     ecx
        mov     esi, esp
        xor     ecx, ecx
@@:
        mov     ebx, [esi]
        add     esi, 4
        movzx   edx, byte [ebx+1]
        add     ecx, edx
        cmp     eax, ecx
        jae     @b
        mov     eax, ecx
        movzx   edx, byte [ebx+1]
        sub     eax, edx
        call    .RangeDecoder.Decode
        add     esp, 256*4
        pop     eax
        mov     cl, [eax+2]
        cmp     cl, .PERIOD_BITS
        jae     @f
        sub     byte [eax+3], 1
        jnz     @f
        shl     word [eax], 1
        mov     dl, 3
        shl     dl, cl
        mov     [eax+3], dl
        add     byte [eax+2], 1
@@:
.update2:
        mov     [ebp+.FoundState], ebx
        add     byte [ebx+1], 4
        mov     esi, [ebp+.MinContext]
        add     word [esi+2], 4
        cmp     byte [ebx+1], .MAX_FREQ
        jbe     @f
        call    .rescale
@@:
        add     [ebp+.EscCount], 1
        mov     eax, [ebp+.InitRL]
        mov     [ebp+.RunLength], eax
        jmp     .dsfnd
.ds5:
        pop     eax
        mov     edx, ecx
        sub     edx, eax
        call    .RangeDecoder.Decode
        mov     eax, [ebp+.MinContext]
        movzx   eax, word [eax]
        mov     ebx, eax
        sub     ebx, [ebp+.NumMasked]
        mov     [ebp+.NumMasked], eax
        mov     esi, esp
        mov     al, [ebp+.EscCount]
@@:
        mov     edx, [esi]
        add     esi, 4
        movzx   edx, byte [edx]
        mov     [ebp+.CharMask+edx], al
        sub     ebx, 1
        jnz     @b
        add     esp, 256*4
        pop     eax
        add     word [eax], cx
; CDecodeInfo::DecodeSymbol2 end
        cmp     [ebp+.FoundState], 0
        jz      .ds4
.dsfnd:
        mov     eax, [ebp+.FoundState]
        mov     al, [eax]
        stosb

.NextContext:
; CInfo::NextContext(void)
        mov     ebx, [ebp+.FoundState]
        mov     ebx, [ebx+2]
        cmp     [ebp+.OrderFall], 0
        jnz     .nc0
        cmp     ebx, [ebp+.pText]
        jbe     .nc0
        mov     [ebp+.MinContext], ebx
        mov     [ebp+.MaxContext], ebx
        ret
.nc0:
        call    .UpdateModel
        cmp     [ebp+.EscCount], 0
        jz      @f
        ret
@@:
        mov     [ebp+.EscCount], 1
        push    edi
        lea     edi, [ebp+.CharMask]
        mov     ecx, 256/4
        xor     eax, eax
        rep     stosd
        pop     edi
        ret

.makeEscFreq2:
; CInfo::makeEscFreq2(ecx=Diff)->{eax->SEE2_CONTEXT,edx=scale}
        mov     esi, [ebp+.MinContext]
        cmp     word [esi], 256
        jz      .mef0
        movzx   edx, [.NS2Indx+ecx-1]
        shl     edx, 4
        mov     eax, [esi+8]
        movzx   eax, word [eax]
        sub     ax, [esi]
        cmp     ecx, eax
        adc     edx, 0
        movzx   eax, word [esi]
        push    edx
        lea     edx, [eax*9]
        lea     edx, [edx+eax*2]
        movzx   eax, word [esi+2]
        cmp     eax, edx
        pop     edx
        setc    al
        movzx   eax, al
        lea     edx, [edx+eax*2]
        cmp     ecx, [ebp+.NumMasked]
        setc    al
        lea     edx, [edx+eax*4]
        add     dl, [ebp+.HiBitsFlag]
        lea     eax, [ebp+edx*4+.SEE2Cont]
        movzx   edx, word [eax]
        push    ecx
        mov     cl, [eax+2]
        shr     edx, cl
        sub     [eax], dx
        pop     ecx
        cmp     edx, 1
        adc     edx, 0
        ret
.mef0:
        lea     eax, [ebp+.DummySEE2Cont]
        mov     edx, 1
        ret

.RangeDecoder.DecodeBit:
; CRangeDecoder::DecodeBit(eax=size0,numTotalBits=.TOT_BITS)
        mov     edx, [ebp+.range]
        shr     edx, .TOT_BITS
        imul    eax, edx
        cmp     [ebp+.code], eax
        jae     .rddb
        mov     [ebp+.range], eax
        call    .RangeDecoder.Normalize
        clc
        ret
.rddb:
        sub     [ebp+.code], eax
        sub     [ebp+.range], eax
        call    .RangeDecoder.Normalize
        stc
        ret

.RangeDecoder.Decode:
        imul    eax, [ebp+.range]
        sub     [ebp+.code], eax
        mov     eax, [ebp+.range]
        imul    eax, edx
        mov     [ebp+.range], eax
.RangeDecoder.Normalize:
        cmp     byte [ebp+.range+3], 0
        jz      @f
        ret
@@:
        sub     [ebp+.inLen], 1
        js      .refill
.filled:
        shl     [ebp+.range], 8
        shl     [ebp+.code], 8
        mov     eax, [ebp+.inPtr]
        add     [ebp+.inPtr], 1
        mov     al, [eax]
        mov     byte [ebp+.code], al
        jmp     .RangeDecoder.Normalize
.refill:
        mov     eax, [ebp+.inStream]
        call    fillBuf
        push    [eax+streamInfo.bufPtr]
        pop     [ebp+.inPtr]
        mov     eax, [eax+streamInfo.bufDataLen]
        sub     eax, 1
        js      return.err
        mov     [ebp+.inLen], eax
        jmp     .filled

.GlueFreeBlocks:
; CSubAllocator::GlueFreeBlocks, called from AllocUnitsRare
        push    eax
        mov     [ebp+.GlueCount], 255
        mov     edx, [ebp+.HeapStart]
        add     edx, [ebp+.usedMemorySize]
        ; we need add extra MEM_BLK with Stamp=0
        and     word [edx], 0
        add     edx, .UNIT_SIZE
        mov     eax, [ebp+.LoUnit]
        cmp     eax, [ebp+.HiUnit]
        jz      @f
        mov     byte [eax], 0
@@:
        mov     [edx+4], edx
        mov     [edx+8], edx
        push    ecx
        xor     ecx, ecx
.gfb1:
        mov     eax, [ebp+ecx*4+.FreeList]
        test    eax, eax
        jz      .gfb2
        push    dword [eax]
        pop     dword [ebp+ecx*4+.FreeList]
        mov     [eax+8], edx
        push    edx
        mov     edx, [edx+4]
        mov     [eax+4], edx
        mov     [edx+8], eax
        or      word [eax], 0xFFFF
        movzx   edx, [.Indx2Units+ecx]
        mov     [eax+2], dx
        pop     edx
        mov     [edx+4], eax
        jmp     .gfb1
.gfb2:
        inc     ecx
        cmp     ecx, .N_INDEXES
        jb      .gfb1
        mov     ecx, edx
.gfb3:
        mov     ecx, [ecx+4]
        cmp     ecx, edx
        jz      .gfb5
.gfb4:
        movzx   eax, word [ecx+2]
        lea     eax, [eax*3]
        lea     eax, [ecx+eax*4]
        cmp     word [eax], 0xFFFF
        jnz     .gfb3
        push    eax
        mov     ax, [eax+2]
        add     ax, [ecx+2]
        pop     eax
        jc      .gfb3
        push    edx
        mov     edx, [eax+4]
        push    dword [eax+8]
        pop     dword [edx+8]
        mov     edx, [eax+8]
        push    dword [eax+4]
        pop     dword [edx+4]
        pop     edx
        mov     ax, [eax+2]
        add     [ecx+2], ax
        jmp     .gfb4
.gfb5:
        mov     ecx, [edx+4]
        cmp     ecx, edx
        jz      .gfb8
        mov     eax, [ecx+4]
        mov     [eax+8], edx
        mov     [edx+4], eax
        movzx   eax, word [ecx+2]
        push    edx
.gfb6:
        sub     eax, 128
        jbe     .gfb7
        mov     edx, ecx
        xchg    edx, [ebp+.FreeList+(.N_INDEXES-1)*4]
        mov     [ecx], edx
        add     ecx, 128*.UNIT_SIZE
        jmp     .gfb6
.gfb7:
        add     eax, 128
        movzx   edx, [.Units2Indx+eax-1]
        cmp     [.Indx2Units+edx], al
        jz      @f
        dec     edx
        push    edx
        movzx   edx, [.Indx2Units+edx]
        sub     eax, edx
        lea     eax, [ebp+.FreeList+(eax-1)*4]
        lea     edx, [edx*3]
        lea     edx, [ecx+edx*4]
        push    dword [eax]
        pop     dword [edx]
        mov     [eax], edx
        pop     edx
@@:
        mov     eax, ecx
        xchg    eax, [ebp+.FreeList+edx*4]
        mov     [ecx], eax
        pop     edx
        jmp     .gfb5
.gfb8:
        pop     ecx
        pop     eax
        mov     edx, [ebp+.FreeList+eax*4]
        test    edx, edx
        jz      .aur.cont
        push    edx
        mov     edx, [edx]
        mov     [ebp+.FreeList+eax*4], edx
        pop     eax
        ret

.AllocContext:
; CSubAllocator::AllocContext
        mov     eax, [ebp+.HiUnit]
        cmp     eax, [ebp+.LoUnit]
        jz      @f
        sub     eax, .UNIT_SIZE
        mov     [ebp+.HiUnit], eax
        ret
@@:
        mov     eax, [ebp+.FreeList]
        test    eax, eax
        jz      @f
        mov     edx, [eax]
        mov     [ebp+.FreeList], edx
        ret
@@:
        xor     eax, eax
        jmp     .AllocUnitsRare

.AllocUnits:
; CSubAllocator::AllocUnits(ecx)
        movzx   ecx, [.Units2Indx+ecx-1]
        mov     eax, [ebp+.FreeList+ecx*4]
        test    eax, eax
        jz      @f
        mov     edx, [eax]
        mov     [ebp+.FreeList+ecx*4], edx
        ret
@@:
        mov     eax, [ebp+.LoUnit]
        movzx   edx, [.Indx2Units+ecx]
        lea     edx, [edx*3]
        lea     eax, [eax+edx*4]
        cmp     eax, [ebp+.HiUnit]
        ja      @f
        xchg    eax, [ebp+.LoUnit]
        ret
@@:
        mov     eax, ecx

.AllocUnitsRare:
; CSubAllocator::AllocUnitsRare(eax)
        cmp     [ebp+.GlueCount], 0
        jz      .GlueFreeBlocks
.aur.cont:
        push    eax
.aur1:
        inc     eax
        cmp     eax, .N_INDEXES
        jz      .aur3
        mov     edx, [ebp+.FreeList+eax*4]
        test    edx, edx
        jz      .aur1
        push    edx
        mov     edx, [edx]
        mov     [ebp+.FreeList+eax*4], edx
        call    .SplitBlock
        pop     eax
        pop     edx
        ret
.aur3:
        dec     [ebp+.GlueCount]
        pop     eax
        movzx   eax, [.Indx2Units+eax]
        lea     edx, [eax*3]
        shl     edx, 2
        mov     eax, [ebp+.UnitsStart]
        sub     eax, [ebp+.pText]
        cmp     eax, edx
        jbe     .aur4
        mov     eax, [ebp+.UnitsStart]
        sub     eax, edx
        mov     [ebp+.UnitsStart], eax
        ret
.aur4:
        xor     eax, eax
        ret

.SplitBlock:
; CSubAllocator::SplitBlock(pv=[esp+4],oldIndx=eax,newIndx=[esp+8])
        push    eax
        mov     edx, [esp+12]
        movzx   eax, [.Indx2Units+eax]
        movzx   edx, [.Indx2Units+edx]
        sub     eax, edx
        lea     edx, [edx*3]
        push    ecx
        mov     ecx, [esp+12]
        lea     ecx, [ecx+edx*4]
        movzx   edx, [.Units2Indx+eax-1]
        cmp     [.Indx2Units+edx], al
        jz      .aur2
        push    dword [ebp+.FreeList+(edx-1)*4]
        pop     dword [ecx]
        mov     [ebp+.FreeList+(edx-1)*4], ecx
        movzx   edx, [.Indx2Units+edx-1]
        sub     eax, edx
        lea     edx, [edx*3]
        lea     ecx, [ecx+edx*4]
.aur2:
        movzx   eax, [.Units2Indx+eax-1]
        push    dword [ebp+.FreeList+eax*4]
        pop     dword [ecx]
        mov     [ebp+.FreeList+eax*4], ecx
        pop     ecx
        pop     eax
        ret

.ExpandUnits:
; CSubAllocator::ExpandUnits(void* oldPtr=esi, int oldNU=eax)
        push    edx
        movzx   edx, [.Units2Indx + eax - 1]
        cmp     dl, [.Units2Indx + eax]
        jnz     @f
        pop     edx
        mov     eax, esi
        ret
@@:
        push    eax ecx edx
        lea     ecx, [eax+1]
        call    .AllocUnits
        pop     edx
        test    eax, eax
        jz      @f
        push    esi edi
        mov     edi, eax
        mov     ecx, [esp+8+4]
        lea     ecx, [ecx*3]
        rep     movsd
        pop     edi esi
        mov     ecx, [ebp+.FreeList+edx*4]
        mov     [esi], ecx
        mov     [ebp+.FreeList+edx*4], esi
@@:
        pop     ecx
        add     esp, 4
        pop     edx
        ret

.ShrinkUnits:
; CSubAllocator::ShrinkUnits(void* oldPtr=eax, int oldNU=ebx, int newNU=ecx)
        push    ecx
        movzx   ebx, [.Units2Indx+ebx-1]
        movzx   ecx, [.Units2Indx+ecx-1]
        cmp     ebx, ecx
        jnz     @f
        pop     ecx
        ret
@@:
        mov     edx, [ebp+.FreeList+ecx*4]
        test    edx, edx
        jz      @f
        push    dword [edx]
        pop     [ebp+.FreeList+ecx*4]
        pop     ecx
        push    esi edi
        mov     esi, eax
        mov     edi, edx
        lea     ecx, [ecx*3]
        rep     movsd
        pop     edi esi
        mov     ecx, [ebp+.FreeList+ebx*4]
        mov     [eax], ecx
        mov     [ebp+.FreeList+ebx*4], eax
        mov     eax, edx
        ret
@@:
        push    ecx
        push    eax
        mov     eax, ebx
        call    .SplitBlock
        pop     eax
        pop     ecx
        pop     ecx
        ret

.FreeUnits:
; CSubAllocator::FreeUnits(void* ptr=eax, int oldNU=ebx)
        movzx   ebx, [.Units2Indx+ebx-1]
        push    [ebp+.FreeList+ebx*4]
        pop     dword [eax]
        mov     [ebp+.FreeList+ebx*4], eax
        ret

ppmd_get_buf_size:
        cmp     dword [esi-4], 5
        jb      return.err
        lodsb
        lodsd
        cmp     eax, ppmd_decoder.kMaxMemBlockSize
        ja      return.err
        add     eax, ppmd_decoder.basesize + ppmd_decoder.kExtraSize
        mov     edx, 0x4000
        ret

ppmd_init_decoder:
        mov     [ebp+ppmd_decoder.bInited], 0
; CDecoder::SetDecoderProperties2
        lodsb
        mov     [ebp+ppmd_decoder.order], al
        lodsd
        mov     [ebp+ppmd_decoder.usedMemorySize], eax
; CSubAllocator::CSubAllocator
        xor     eax, eax
        mov     [ebp+ppmd_decoder.GlueCount], al
        lea     edi, [ebp+ppmd_decoder.LoUnit]
        mov     ecx, (ppmd_decoder.SEE2Cont - ppmd_decoder.LoUnit)/4
        rep     stosd
; CSubAllocator::StartSubAllocator
        lea     eax, [ebp+ppmd_decoder.Base+ppmd_decoder.UNIT_SIZE]
        mov     [ebp+ppmd_decoder.HeapStart], eax
        mov     [ebp+streamInfo.fillBuf], ppmd_decoder.fillBuf
        ret
