; Loader of ZIP archives for KFar_Arc.
; Written by diamond in 2007.

virtual at 0
file_in_zip:
.fullname       dd      ?       ; pointer to cp866 string
.name           dd      ?
.namelen        dd      ?
.bIsDirectory   db      ?
.bPseudoFolder  db      ?
                rb      2
.parent         dd      ?       ; pointer to parent directory record
.subfolders     dd      ?       ; head of L2-list of subfolders [for folders]
.subfolders.end dd      ?
.subfiles       dd      ?       ; head of L2-list of files [for folders]
.subfiles.end   dd      ?
.NumSubItems    dd      ?
.next           dd      ?       ; next item of list of subfolders or files
.prev           dd      ?       ; previous item of list of subfolders or files
.stamp          dd      ?
.version_made_by dw     ?
.version_needed dw      ?
.flags          dw      ?
.method         dw      ?
.time           dw      ?
.date           dw      ?
.crc            dd      ?
.compressed_size dq     ?
.uncompressed_size dq   ?
.local_hdr      dq      ?
.disk           dd      ?
.attr           dd      ?
.size = $
end virtual

virtual at 0
handle_zip:
.type           dd      ?
.root.subfolders dd     ?
.root.subfolders.end dd ?
.root.subfiles  dd      ?
.root.subfiles.end dd   ?
.root.NumSubItems dd    ?
.curdir         dd      ?
.NumFiles       dd      ?
.names_buf      dd      ?
.host           dd      ?
.host_datetime  rd      6
.num_disks      dd      ?
.cur_disk       dd      ?
.cur_handle     dd      ?
.host_hPlugin   dd      ?
.host_idPlugin  dd      ?
.password_len   dd      ?       ; -1 if no password defined; in characters
.password       rb      password_maxlen
.basesize = $
end virtual

uglobal
align 4
zip.num_disks   dd      ?
zip.cur_disk    dd      ?
zip.cur_handle  dd      ?
zip.NumAllocated dd     ?
zip.NamesSize   dd      ?
zip.disk_name   rb      1024

zip64eocd:      ; zip64 end of central directory record
.sig            dd      ?       ; signature
.size           dq      ?       ; size of this record
.version_made_by dw     ?       ; version made by
.version_needed dw      ?       ; version needed to extract
.disk           dd      ?       ; number of this disk
.cdir_disk      dd      ?       ; number of the disk with the start
                                ; of the central directory
.cdir_entries_disk dq   ?       ; total number of entries in the central
                                ; directory on this disk
.cdir_entries   dq      ?       ; total number of entries in the central directory
.cdir_size      dq      ?       ; size of the central directory
.cdir_start     dq      ?       ; offset of start of central directory
.sz = $ - zip64eocd

zip.file_header:
.sig            dd      ?
.version_made_by dw     ?       ; (absent in local file header)
.version_needed dw      ?
.flags          dw      ?       ; general purpose bit flag
.method         dw      ?       ; compression method
.time           dw      ?       ; last modified file time
.date           dw      ?       ; last modified file date
.crc            dd      ?
.compressed_size dd     ?
.uncompressed_size dd   ?
.fname_len      dw      ?       ; file name length
.extra_len      dw      ?       ; extra field length
.sz_local = $ - (zip.file_header+2) ; size of local file header
.fcmt_len       dw      ?       ; file comment length
.disk           dw      ?       ; disk number start
.internal_attr  dw      ?
.external_attr  dd      ?
.local_hdr      dd      ?       ; relative offset of local header
.sz_cdir = $ - zip.file_header  ; size of file header in central directory

zip.extra:
.id     dw      ?
.size   dw      ?
zip.extra.zip64:
.uncompressed_size      dq      ?
.compressed_size        dq      ?
.local_hdr              dq      ?
.disk                   dd      ?
.sz = $ - zip.extra.zip64
endg

open_zip:
        and     [zip.cur_handle], 0
; look for end of central directory record
        push    ebp
        call    [filesize]
        sub     eax, 1024
        sbb     edx, 0
        jnb     @f
        xor     eax, eax
        xor     edx, edx
@@:
        push    edx
        push    eax
        push    0
        push    ebp
        call    [seek]
        push    1024
        push    buffer
        push    ebp
        call    [read]
        cmp     eax, 22
        jge     @f
        xor     eax, eax
.ret:
        ret
@@:
        mov     [inStream], ebp
        lea     edi, [buffer+eax-22]
@@:
        cmp     dword [edi], 0x06054B50
        jz      .eocd_found
        dec     edi
        cmp     edi, buffer
        jae     @b
        xor     eax, eax
        ret
.eocd_found:
        cmp     edi, buffer+20
        jb      .no_eocd64
        cmp     dword [edi-20], 0x07064B50
        jnz     .no_eocd64
        mov     ebx, ebp
        mov     ecx, [edi-16]
        mov     edx, [edi-4]
        mov     [zip.num_disks], edx
        dec     edx
        mov     [zip.cur_disk], edx
        cmp     edx, ecx
        jz      @f
        push    1
        push    dword [esp+8+28]
        push    dword [esp+12+24]
        push    dword [esp+16+20]
        call    zip.open_splitted
        test    eax, eax
        jz      .ret
        mov     ebx, eax
@@:
        push    dword [edi-8]
        push    dword [edi-12]
        push    0
        push    ebx
        call    [seek]
        push    zip64eocd.sz
        push    zip64eocd
        push    ebx
        call    [read]
        cmp     eax, zip64eocd.sz
        jnz     .close_err
        cmp     [zip64eocd.sig], 0x06064B50
        jnz     .close_err
        mov     ecx, [zip64eocd.cdir_disk]
        cmp     ecx, [zip.cur_disk]
        jz      @f
        push    1
        push    dword [esp+8+28]
        push    dword [esp+12+24]
        push    dword [esp+16+20]
        call    zip.open_splitted
        test    eax, eax
        jz      .ret
        mov     ebx, eax
@@:
        push    dword [zip64eocd.cdir_start+4]
        push    dword [zip64eocd.cdir_start]
        push    0
        push    ebx
        call    [seek]
        mov     ecx, dword [zip64eocd.cdir_entries]
        jmp     .parse_cdir
.close_err:
        call    close_cur_handle
        or      eax, -1
        ret
.no_eocd64:
        mov     ebx, ebp
        movzx   ecx, word [edi+6]
        movzx   edx, word [edi+4]
        inc     edx
        mov     [zip.num_disks], edx
        dec     edx
        mov     [zip.cur_disk], edx
        cmp     edx, ecx
        jz      @f
        push    1
        push    dword [esp+8+28]
        push    dword [esp+12+24]
        push    dword [esp+16+20]
        call    zip.open_splitted
        test    eax, eax
        jz      .ret
        mov     ebx, eax
@@:
        push    0
        push    dword [edi+16]
        push    0
        push    ebx
        call    [seek]
        movzx   ecx, word [edi+10]
.parse_cdir:
        mov     [zip.NumAllocated], ecx
        imul    ecx, file_in_zip.size
        jc      .close_err
        add     ecx, handle_zip.basesize
        jc      .close_err
        mov     [hOut.allocated], ecx
        call    [pgalloc]
        test    eax, eax
        jz      .close_err
        mov     [hOut], eax
        push    eax
        mov     edi, eax
        shr     ecx, 2
        xor     eax, eax
        rep     stosd
        mov     edi, [esp+8+28]
        dec     ecx
        repnz   scasb
        not     ecx
        mov     [zip.NamesSize], ecx
        call    [pgalloc]
        mov     ebp, eax
        pop     edi
        test    eax, eax
        jz      .free_err
        push    edi
        mov     edi, ebp
        mov     esi, [esp+8+28]
        rep     movsb
        pop     edi
        mov     byte [edi+handle_zip.type], type_zip
        add     edi, handle_zip.basesize + file_in_zip.version_made_by
.cdir_loop:
        mov     esi, zip.file_header
        push    zip.file_header.sz_cdir
        push    esi
        push    ebx
        call    [read]
        cmp     eax, zip.file_header.sz_cdir
        jz      .cdir_item_ok
        mov     ecx, [zip.cur_disk]
        inc     ecx
        cmp     ecx, [zip.num_disks]
        jz      .cdir_done
        push    1
        push    dword [esp+8+28]
        push    dword [esp+12+24]
        push    dword [esp+16+20]
        call    zip.open_splitted
        test    eax, eax
        jz      .free_err
        mov     ebx, eax
        jmp     .cdir_loop
.cdir_item_ok:
        lodsd
        cmp     eax, 0x02014B50
        jnz     .cdir_done
        mov     eax, [hOut]
        inc     [eax+handle_zip.NumFiles]
        mov     eax, [eax+handle_zip.NumFiles]
        cmp     eax, [zip.NumAllocated]
        jbe     .norealloc
        inc     [zip.NumAllocated]
        mov     ecx, [hOut.allocated]
        add     ecx, file_in_zip.size
        push    ecx
        and     ecx, 0xFFF
        cmp     ecx, file_in_zip.size
        pop     ecx
        mov     [hOut.allocated], ecx
        ja      .norealloc
        mov     edx, [hOut]
        call    [pgrealloc]
        test    eax, eax
        jnz     @f
.free_err:
        mov     ecx, [hOut]
        call    [pgfree]
        mov     ecx, ebp
        call    [pgfree]
        jmp     .close_err
@@:
        mov     [hOut], eax
        sub     edi, edx
        add     edi, eax
.norealloc:
        push    5
        pop     ecx
        rep     movsd
        xor     eax, eax
        stosd
        movsd
        stosd
        lodsw
        mov     ecx, [zip.NamesSize]
        lea     edx, [ecx+0xFFF]
        and     edx, not 0xFFF
        lea     ecx, [ecx+eax+1]
        mov     [zip.NamesSize], ecx
        push    ecx
        add     ecx, 0xFFF
        and     ecx, not 0xFFF
        cmp     ecx, edx
        pop     ecx
        jz      .noreallocname
        mov     edx, ebp
        call    [pgrealloc]
        test    eax, eax
        jz      .free_err
        mov     ebp, eax
.noreallocname:
        movzx   eax, word [esi-2]
        inc     eax
        mov     edx, [zip.NamesSize]
        sub     edx, eax
        mov     dword [edi-(file_in_zip.uncompressed_size+8)+file_in_zip.fullname], edx
        add     edx, ebp
        dec     eax
        mov     byte [edx+eax], 0
        push    eax
        push    edx
        push    ebx
        call    [read]
        movzx   eax, word [esi-2]
        cmp     byte [edx+eax-1], '/'
        jnz     @f
        mov     byte [edx+eax-1], 0
        mov     byte [edi-(file_in_zip.uncompressed_size+8)+file_in_zip.bIsDirectory], 1
@@:
        mov     eax, [esi+12]
        mov     [edi], eax
        movzx   eax, word [esi+4]
        mov     [edi+8], eax
        lodsw
        push    esi
        movzx   ecx, ax
        mov     edx, ecx
.parse_extra:
        sub     ecx, 4
        jb      .extra_done
        mov     esi, zip.extra
        push    4
        push    esi
        push    ebx
        call    [read]
        cmp     eax, 4
        jnz     .extra_done
        sub     edx, eax
        cmp     word [esi], 1
        jnz     .extra_skip
        mov     esi, zip.extra.zip64
        push    esi edi
        sub     edi, 8
        xchg    esi, edi
        movsd
        movsd
        sub     esi, 16
        movsd
        movsd
        add     esi, 8
        movsd
        movsd
        movsd
        pop     edi esi
        movzx   eax, [zip.extra.size]
        cmp     eax, zip.extra.zip64.sz
        jb      @f
        mov     al, zip.extra.zip64.sz
@@:
        sub     ecx, eax
        jb      .extra_done
        push    eax
        push    esi
        push    ebx
        call    [read]
        sub     edx, eax
        sub     [zip.extra.size], ax
        sub     edi, 8
        movsd
        movsd
        sub     edi, 16
        movsd
        movsd
        add     edi, 8
        movsd
        movsd
        movsd
        sub     edi, 12
.extra_skip:
        movzx   eax, word [zip.extra.size]
        sub     ecx, eax
        jb      .extra_done
        sub     edx, eax
        push    0
        push    eax
        push    1
        push    ebx
        call    [seek]
        jmp     .parse_extra
.extra_done:
        pop     esi
        xor     eax, eax
        lodsw
        add     edx, eax
        jz      @f
        push    0
        push    edx
        push    1
        push    ebx
        call    [seek]
@@:
        mov     eax, [esi+4]
        mov     dword [edi+file_in_zip.attr-(file_in_zip.uncompressed_size+8)], eax
;        test    al, 10h
;        setnz   byte [edi+file_in_zip.bIsDirectory-(file_in_zip.uncompressed_size+8)]
        add     edi, file_in_zip.size-file_in_zip.uncompressed_size-8+file_in_zip.version_made_by
        jmp     .cdir_loop
.cdir_done:
        mov     edi, [hOut]
        mov     esi, zip.num_disks
        push    edi
        add     edi, handle_zip.num_disks
        movsd
        movsd
        movsd
        pop     edi
        mov     [edi+handle_zip.names_buf], ebp
        mov     ecx, [edi+handle_zip.NumFiles]
        jecxz   .nofiles
        add     edi, handle_zip.basesize + file_in_zip.fullname
@@:
        add     [edi], ebp
        add     edi, file_in_zip.size
        loop    @b
.nofiles:
        mov     edx, [hOut]
        mov     ebx, [edx+handle_zip.NumFiles]
        lea     edi, [edx+handle_zip.root.subfolders]
        add     edx, handle_zip.basesize
        push    file_in_zip.size
        call    init_file_links
        mov     eax, [hOut]
        and     [eax+handle_zip.curdir], 0      ; set root directory
        mov     esi, [inStream]
        mov     [eax+handle_zip.host], esi
        or      [eax+handle_zip.password_len], -1
        mov     esi, [esp+4+20]
        mov     [eax+handle_zip.host_idPlugin], esi
        mov     esi, [esp+4+24]
        mov     [eax+handle_zip.host_hPlugin], esi
        lea     edi, [eax+handle_zip.host_datetime]
        mov     esi, [esp+12]
        add     esi, 8
        mov     ecx, 6
        rep     movsd
        ret

close_cur_handle:
        mov     edx, zip.cur_handle
        or      dword [edx-4], -1
        cmp     dword [edx], 0
        jz      @f
        push    dword [edx]
        call    [close]
        and     dword [edx], 0
@@:
        ret

zip.open_splitted:
        call    close_cur_handle
        mov     [zip.cur_disk], ecx
        inc     ecx
        mov     edx, ecx
        cmp     ecx, [zip.num_disks]
        jnz     @f
        mov     ecx, [inStream]
        push    0
        push    0
        push    0
        push    ecx
        call    [seek]
        mov     eax, ecx
        ret     16
@@:
        push    esi edi
        mov     esi, [esp+8+12]
        mov     edi, zip.disk_name
        mov     [esp+8+12], edi
        mov     ecx, 1024
@@:
        lodsb
        stosb
        test    al, al
        loopnz  @b
        jnz     .ret0
        cmp     ecx, 4
        jb      .ret0
        dec     edi
        mov     al, '.'
        stosb
        cmp     edx, 100
        jae     .big
        mov     al, 'z'
        stosb
        mov     eax, edx
        aam
        add     ax, '00'
        xchg    al, ah
        stosw
        mov     byte [edi], 0
.nameok:
        pop     edi esi
        push    dword [esp+16]
        push    dword [esp+16]
        push    dword [esp+16]
        push    dword [esp+16]
        call    [open2]
        mov     [zip.cur_handle], eax
        ret     16
.ret0:
        mov     byte [edi-1], 0
        pop     edi esi
        xor     eax, eax
        ret     16
.big:
        cmp     ecx, 12
        jb      .ret0
        push    10
        pop     ecx
        push    -'0'
        mov     eax, edx
@@:
        xor     edx, edx
        div     ecx
        push    edx
        test    eax, eax
        jnz     @b
@@:
        pop     eax
        add     al, '0'
        jz      .nameok
        stosb
        jmp     @b

close_zip:
        mov     esi, [esp+4]
        push    [esi+handle_zip.host]
        call    [close]
        mov     ecx, [esi+handle_zip.names_buf]
        call    [pgfree]
        mov     ecx, [esi+handle_zip.cur_handle]
        jecxz   @f
        push    ecx
        call    [close]
@@:
        mov     ecx, esi
        call    [pgfree]
        ret     4

; ebp=hPlugin, eax->item, edi->info
getattr_zip:
        cmp     [eax+file_in_zip.bPseudoFolder], 0
        jnz     .pseudo
        mov     esi, eax
        mov     eax, [eax+file_in_zip.attr]
        stosd
        xor     eax, eax
        stosd
        stosd
        stosd
        stosd
        stosd
        movzx   eax, word [esi+file_in_zip.time]
        mov     ecx, eax
        and     al, 0x1F
        shr     ecx, 5
        add     eax, eax
        stosb
        mov     eax, ecx
        and     al, 0x3F
        shr     ecx, 6
        stosb
        mov     eax, ecx
        stosw
        movzx   eax, word [esi+file_in_zip.date]
        mov     ecx, eax
        and     al, 0x1F
        shr     ecx, 5
        stosb
        mov     eax, ecx
        and     al, 0xF
        shr     ecx, 4
        stosb
        lea     eax, [ecx+1980]
        stosw
        mov     eax, dword [esi+file_in_zip.uncompressed_size]
        stosd
        mov     eax, dword [esi+file_in_zip.uncompressed_size+4]
        stosd
        ret
.pseudo:
        push    0x10    ; attributes: folder
        pop     eax
        stosd
        xor     eax, eax
        stosd
        lea     esi, [ebp+handle_zip.host_datetime]
        push    6
        pop     ecx
        rep     movsd
        stosd
        stosd
        ret

virtual at 0
file_handle_zip:
.type   dd      ?
.item   dd      ?
.base   dd      ?
.start_disk     dd      ?
.start          dq      ?
.pos            dq      ?
.posc           dq      ?
.decoder        dd      ?
.cur_disk       dd      ?
.cur_disk_start dq      ?
.bError         db      ?
.bPassProtected db      ?
.bPassInited    db      ?
                rb      1
.keys           rd      3
.saved_keys     rd      3
.size = $
end virtual

iglobal
handle_table_zip        dd      handle_table_zip, handle_table_zip
endg

; ebp=hPlugin, eax->item, edi=mode
open_file_zip:
        mov     [_esp], esp
        mov     [_ebp], ebp
        mov     [error_proc], .error
        mov     [clear_proc], .clear
        mov     ecx, [ebp+handle_zip.password_len]
        inc     ecx
        setnz   [bPasswordDefined]
        jz      @f
        dec     ecx
        mov     [password_size], ecx
        lea     esi, [ebp+handle_zip.password]
        mov     edi, password_ansi
        rep     movsb
@@:
        xor     edi, edi
        mov     [hOut], edi
        mov     [hOut.allocated], edi
        mov     ebx, eax
        movzx   eax, [ebx+file_in_zip.method]
        test    eax, eax
        jz      .common
.notcopy:
        cmp     eax, 8
        jz      .deflate
        cmp     eax, 9
        jz      .deflate
        push    -'0'
        push    10
        pop     ecx
@@:
        xor     edx, edx
        div     ecx
        push    edx
        test    eax, eax
        jnz     @b
        mov     edi, aUnknownMethod.z
        mov     al, ' '
        stosb
@@:
        pop     eax
        add     al, '0'
        stosb
        jnz     @b
        push    ContinueBtn
        push    1
        push    aUnknownMethod_ptr
        push    1
        call    [SayErr]
        xor     eax, eax
.ret1:
        ret
.deflate:
        mov     ecx, 0x12000
        call    [pgalloc]
        test    eax, eax
        jz      .ret1
        mov     [hOut], eax
        mov     ecx, dword [ebx+file_in_zip.compressed_size]
        mov     dword [eax+streamInfo.fullSize], ecx
        mov     ecx, dword [ebx+file_in_zip.compressed_size+4]
        mov     dword [eax+streamInfo.fullSize+4], ecx
        mov     [eax+streamInfo.fillBuf], zipin.fillBuf
        mov     [eax+streamInfo.bufSize], 0x2000 - (streamInfo.size+4) - deflate_decoder.size
        and     [eax+streamInfo.bufDataLen], 0
        lea     edx, [eax+streamInfo.size+4]
        mov     [eax+streamInfo.bufPtr], edx
        add     edx, [eax+streamInfo.bufSize]
        mov     ecx, dword [ebx+file_in_zip.uncompressed_size]
        mov     dword [edx+streamInfo.fullSize], ecx
        mov     ecx, dword [ebx+file_in_zip.uncompressed_size+4]
        mov     dword [edx+streamInfo.fullSize+4], ecx
        mov     [edx+streamInfo.bufSize], 0x10000
        and     [edx+streamInfo.bufDataLen], 0
        mov     [edx+streamInfo.size], eax
        lea     eax, [edx+deflate_decoder.size]
        mov     [edx+streamInfo.bufPtr], eax
        push    ebp
        mov     ebp, edx
        call    deflate_init_decoder
        mov     edi, ebp
        pop     ebp
        cmp     [ebx+file_in_zip.method], 9
        setz    [edi+deflate_decoder.bDeflate64]
.common:
        mov     esi, handle_table_zip
        push    file_handle_zip.size
        pop     ecx
        call    alloc_handle
        test    eax, eax
        jz      return.clear
        mov     edx, [hOut]
        mov     [edx+streamInfo.size], eax
        mov     [eax+file_handle_zip.type], type_zip
        xor     edx, edx
        mov     dword [eax+file_handle_zip.pos], edx
        mov     dword [eax+file_handle_zip.pos+4], edx
        mov     dword [eax+file_handle_zip.posc], edx
        mov     dword [eax+file_handle_zip.posc+4], edx
        mov     [eax+file_handle_zip.item], ebx
        mov     [eax+file_handle_zip.base], ebp
        mov     [eax+file_handle_zip.decoder], edi
        mov     [eax+file_handle_zip.bError], dl
        mov     [eax+file_handle_zip.bPassInited], dl
        test    [ebx+file_in_zip.flags], 1
        setnz   [eax+file_handle_zip.bPassProtected]
        mov     ecx, [ebx+file_in_zip.disk]
        mov     [eax+file_handle_zip.start_disk], ecx
        mov     [eax+file_handle_zip.cur_disk], ecx
        mov     ecx, dword [ebx+file_in_zip.local_hdr]
        mov     dword [eax+file_handle_zip.start], ecx
        mov     ecx, dword [ebx+file_in_zip.local_hdr+4]
        mov     dword [eax+file_handle_zip.start+4], ecx
        mov     dword [eax+file_handle_zip.cur_disk_start], edx
        mov     dword [eax+file_handle_zip.cur_disk_start+4], edx
        mov     [hOut.allocated], eax
        mov     [hOut], edx
        push    ebx
        mov     ebx, eax
        push    zip.file_header.sz_local
        pop     ecx
        mov     edi, zip.file_header+2
        call    zip.read
        cmp     dword [edi-zip.file_header.sz_local], 0x04034B50
        jnz     return.err
        movzx   ecx, word [edi-4]
        movzx   eax, word [edi-2]
        lea     esi, [ecx+eax+zip.file_header.sz_local]
        xor     edi, edi
        call    zip.setpos
        call    zip.update_start
        cmp     [ebx+file_handle_zip.bPassProtected], 0
        jz      .nopass
.redo_pass:
        call    query_password
        jz      return.clear
        call    zip.init_keys
        push    12
        pop     ecx
        mov     edi, buffer
        call    zip.read
        mov     eax, [ebx+file_handle_zip.item]
        mov     al, byte [eax+file_in_zip.crc+3]
        cmp     [edi-1], al
        jz      .passok
        push    CancelPassBtn
        push    2
        push    aArchiveDataErrorPass_ptr
        push    1
        call    [SayErr]
        cmp     al, 1
        jnz     return.clear
        xor     eax, eax
        mov     dword [ebx+file_handle_zip.posc], eax
        mov     dword [ebx+file_handle_zip.posc+4], eax
        mov     dword [ebx+file_handle_zip.cur_disk_start], eax
        mov     dword [ebx+file_handle_zip.cur_disk_start+4], eax
        mov     eax, [ebx+file_handle_zip.start_disk]
        mov     [ebx+file_handle_zip.cur_disk], eax
        mov     [bPasswordDefined], 0
        jmp     .redo_pass
.passok:
        mov     ecx, [password_size]
        mov     [ebp+handle_zip.password_len], ecx
        mov     esi, password_ansi
        lea     edi, [ebp+handle_zip.password]
        rep     movsb
        lea     esi, [ebx+file_handle_zip.keys]
        lea     edi, [ebx+file_handle_zip.saved_keys]
        movsd
        movsd
        movsd
        mov     esi, 12
        xor     edi, edi
        call    zip.update_start
.nopass:
        pop     ebx
        mov     eax, [hOut.allocated]
        cmp     dword [ebx+file_in_zip.uncompressed_size+4], 0
        jnz     .ret
        mov     ecx, dword [ebx+file_in_zip.uncompressed_size]
if defined LIMIT_FOR_MEM_STREAM
        cmp     ecx, LIMIT_FOR_MEM_STREAM
        ja      .ret
end if
        push    eax ecx
        add     ecx, 0x3FF
        shr     ecx, 10         ; get size in Kb
        call    [getfreemem]
        shr     eax, 2
        cmp     ecx, eax
        pop     ecx eax
        ja      .ret
; create memory stream and unpack to memory
        mov     ebx, eax
        add     ecx, mem_stream.buf
        call    [pgalloc]
        sub     ecx, mem_stream.buf
        test    eax, eax
        jz      return.clear
        mov     edi, eax
        mov     [hOut], eax
        xor     eax, eax        ; type_mem_stream
        stosd                   ; mem_stream.type
        mov     eax, ecx
        stosd                   ; mem_stream.size
        xor     eax, eax
        stosd                   ; mem_stream.pos
        push    ecx edi
        push    ecx
        push    edi
        push    ebx
        call    read_zip
        pop     esi ecx
        mov     [_esp], esp
        mov     [_ebp], ebp
        mov     [error_proc], .error
        mov     [clear_proc], .clear
        cmp     eax, -1
        jz      .clear
;        cmp     eax, ecx
;        jnz     .error
;        call    crc
;        mov     edx, [ebx+file_handle_zip.item]
;        cmp     eax, [edx+file_in_zip.crc]
;        jnz     .error
        call    .1
        mov     eax, [hOut]
.ret:
        ret

.error:
        push    ContinueBtn
        push    1
        push    aArchiveDataError_ptr
        push    1
        call    [SayErr]
.clear:
        mov     ecx, [hOut]
        call    [pgfree]
.1:
        mov     ebx, [hOut.allocated]
        test    ebx, ebx
        jz      @f
        push    ebx
        call    close_file_zip
@@:
        xor     eax, eax
        ret

close_file_zip:
        mov     ecx, [ebx+file_handle_zip.decoder]
        jecxz   @f
        mov     ecx, [ecx+streamInfo.size]
        call    [pgfree]
@@:
        mov     esi, ebx
        call    free_handle
        ret     4

zip.init_keys:
        mov     [ebx+file_handle_zip.keys+0], 305419896
        mov     [ebx+file_handle_zip.keys+4], 591751049
        mov     [ebx+file_handle_zip.keys+8], 878082192
        mov     ecx, [password_size]
        jecxz   .empty
        mov     esi, password_ansi
        xor     eax, eax
@@:
        lodsb
        call    zip.update_keys
        loop    @b
.empty:
        mov     [ebx+file_handle_zip.bPassInited], 1
        ret

zip.update_keys:
        mov     dl, byte [ebx+file_handle_zip.keys]
        xor     dl, al
        movzx   edx, dl
        mov     eax, [ebx+file_handle_zip.keys]
        shr     eax, 8
        xor     eax, [crc_table+edx*4]
        mov     [ebx+file_handle_zip.keys], eax
        movzx   eax, al
        add     eax, [ebx+file_handle_zip.keys+4]
        imul    eax, 134775813
        inc     eax
        mov     [ebx+file_handle_zip.keys+4], eax
        shr     eax, 24
        mov     edx, [ebx+file_handle_zip.keys+8]
        xor     al, dl
        shr     edx, 8
        xor     edx, [crc_table+eax*4]
        mov     [ebx+file_handle_zip.keys+8], edx
        ret

zip.decrypt_byte:
; out: ah = result
        mov     eax, [ebx+file_handle_zip.keys+8]
        or      eax, 2
        mov     edx, eax
        xor     edx, 1
        mul     edx
        ret

zip.get_cur_handle:
        mov     edx, [ebp+handle_zip.host]
        mov     eax, [ebp+handle_zip.num_disks]
        dec     eax
        cmp     eax, [ebx+file_handle_zip.cur_disk]
        jz      .handleok
        inc     eax
        mov     [zip.num_disks], eax
        mov     edx, [ebp+handle_zip.cur_handle]
        mov     eax, [ebp+handle_zip.cur_disk]
        cmp     eax, [ebx+file_handle_zip.cur_disk]
        jz      .handleok
        mov     [zip.cur_handle], edx
        push    ecx
        mov     ecx, [ebx+file_handle_zip.cur_disk]
        push    1
        push    [ebp+handle_zip.names_buf]
        push    [ebp+handle_zip.host_hPlugin]
        push    [ebp+handle_zip.host_idPlugin]
        call    zip.open_splitted
        pop     ecx
        mov     edx, eax
        test    eax, eax
        jnz     @f
        push    zip.disk_name
        push    aCannotOpenFile
        mov     eax, esp
        push    ContinueBtn
        push    1
        push    eax
        push    2
        call    [SayErr]
        jmp     return.clear
@@:
        mov     [ebp+handle_zip.cur_handle], eax
        mov     eax, [ebx+file_handle_zip.cur_disk]
        mov     [ebp+handle_zip.cur_disk], eax
.handleok:
        ret

zip.read:
; in: ebx=hFile, edi->buf, ecx=size
        push    ebp edi
        mov     ebp, [ebx+file_handle_zip.base]
.s:
        call    zip.get_cur_handle
        mov     eax, dword [ebx+file_handle_zip.posc]
        mov     esi, dword [ebx+file_handle_zip.posc+4]
        sub     eax, dword [ebx+file_handle_zip.cur_disk_start]
        sbb     esi, dword [ebx+file_handle_zip.cur_disk_start+4]
        push    eax
        mov     eax, dword [ebx+file_handle_zip.cur_disk]
        cmp     eax, dword [ebx+file_handle_zip.start_disk]
        pop     eax
        jnz     @f
        add     eax, dword [ebx+file_handle_zip.start]
        adc     esi, dword [ebx+file_handle_zip.start+4]
@@:
        push    esi
        push    eax
        push    0
        push    edx
        call    [seek]
        push    ecx
        push    edi
        push    edx
        call    [read]
        cmp     eax, -1
        jz      .error
        add     edi, eax
        add     dword [ebx+file_handle_zip.posc], eax
        adc     dword [ebx+file_handle_zip.posc+4], 0
        sub     ecx, eax
        jz      .ok
        inc     dword [ebx+file_handle_zip.cur_disk]
        mov     eax, dword [ebx+file_handle_zip.posc]
        mov     dword [ebx+file_handle_zip.cur_disk_start], eax
        mov     eax, dword [ebx+file_handle_zip.posc+4]
        mov     dword [ebx+file_handle_zip.cur_disk_start+4], eax
        jmp     .s
.ok:
        cmp     [ebx+file_handle_zip.bPassInited], 0
        jnz     .decrypt
        pop     eax ebp
        ret
.decrypt:
        mov     ecx, edi
        pop     edi
        sub     ecx, edi
        jz      .nodata
@@:
        call    zip.decrypt_byte
        xor     [edi], ah
        mov     al, [edi]
        inc     edi
        call    zip.update_keys
        loop    @b
.nodata:
        pop     ebp
        ret
.error:
        push    ContinueBtn
        push    1
        push    aReadError_ptr
        push    1
        call    [SayErr]
        jmp     return.clear

zip.setpos:
; in: ebx=hFile, edi:esi=newpos
        cmp     [ebx+file_handle_zip.bPassInited], 0
        jnz     .pass
        push    ebp
        mov     ebp, [ebx+file_handle_zip.base]
        push    edi esi
        sub     esi, dword [ebx+file_handle_zip.cur_disk_start]
        sbb     edi, dword [ebx+file_handle_zip.cur_disk_start+4]
        jae     .fwd
        mov     eax, [ebx+file_handle_zip.start_disk]
        mov     [ebx+file_handle_zip.cur_disk], eax
        and     dword [ebx+file_handle_zip.cur_disk_start], 0
        and     dword [ebx+file_handle_zip.cur_disk_start+4], 0
        pop     esi edi
        push    edi esi
.fwd:
        call    zip.get_cur_handle
        push    edx
        call    [filesize]
        push    eax
        mov     eax, [ebx+file_handle_zip.cur_disk]
        cmp     eax, [ebx+file_handle_zip.start_disk]
        pop     eax
        jnz     @f
        sub     eax, dword [ebx+file_handle_zip.start]
        sbb     edx, dword [ebx+file_handle_zip.start+4]
@@:
        sub     esi, eax
        sbb     edi, edx
        jb      @f
        inc     [ebx+file_handle_zip.cur_disk]
        add     dword [ebx+file_handle_zip.cur_disk_start], eax
        adc     dword [ebx+file_handle_zip.cur_disk_start+4], edx
        jmp     .fwd
@@:
        pop     esi edi
        mov     dword [ebx+file_handle_zip.posc], esi
        mov     dword [ebx+file_handle_zip.posc+4], edi
        pop     ebp
        ret
.pass:
        push    edi esi
        sub     esi, dword [ebx+file_handle_zip.posc]
        sbb     edi, dword [ebx+file_handle_zip.posc+4]
        jae     .pfwd
        lea     esi, [ebx+file_handle_zip.saved_keys]
        lea     edi, [ebx+file_handle_zip.keys]
        movsd
        movsd
        movsd
        and     dword [ebx+file_handle_zip.posc], 0
        and     dword [ebx+file_handle_zip.posc+4], 0
        mov     eax, [ebx+file_handle_zip.start_disk]
        mov     [ebx+file_handle_zip.cur_disk], eax
        and     dword [ebx+file_handle_zip.cur_disk_start], 0
        and     dword [ebx+file_handle_zip.cur_disk_start+4], 0
        pop     esi edi
        push    edi esi
.pfwd:
        push    edi esi
        mov     ecx, 1024
        sub     esi, ecx
        sbb     edi, 0
        jae     @f
        add     ecx, esi
@@:
        push    ecx
        mov     edi, buffer
        call    zip.read
        pop     ecx
        pop     esi edi
        sub     esi, ecx
        sbb     edi, 0
        mov     eax, esi
        or      eax, edi
        jnz     .pfwd
        pop     esi edi
        ret

zip.update_start:
; edi=0, esi=pos
        sub     esi, dword [ebx+file_handle_zip.cur_disk_start]
        mov     dword [ebx+file_handle_zip.cur_disk_start], edi
        add     dword [ebx+file_handle_zip.start], esi
        adc     dword [ebx+file_handle_zip.start+4], edi
        mov     dword [ebx+file_handle_zip.posc], edi
        mov     dword [ebx+file_handle_zip.posc+4], edi
        mov     eax, [ebx+file_handle_zip.cur_disk]
        cmp     eax, [ebx+file_handle_zip.start_disk]
        jz      @f
        mov     [ebx+file_handle_zip.start_disk], eax
        mov     dword [ebx+file_handle_zip.start], esi
        mov     dword [ebx+file_handle_zip.start+4], edi
@@:
        ret

; unsigned __stdcall read(HANDLE hFile, void* buf, unsigned size);
read_zip:
        mov     [_esp], esp
        mov     [_ebp], ebp
        mov     [error_proc], .error
        mov     [clear_proc], .clear
        cmp     [ebx+file_handle_zip.bError], 0
        jnz     .reterr
        mov     edi, [esp+8]
        mov     ecx, [esp+12]
        mov     esi, [ebx+file_handle_zip.item]
        mov     edx, dword [esi+file_in_zip.uncompressed_size]
        mov     esi, dword [esi+file_in_zip.uncompressed_size+4]
        sub     edx, dword [ebx+file_handle_zip.pos]
        sbb     esi, dword [ebx+file_handle_zip.pos+4]
        jnz     @f
        cmp     edx, ecx
        jae     @f
        mov     ecx, edx
@@:
        jecxz   .done
        mov     eax, [ebx+file_handle_zip.decoder]
        test    eax, eax
        jnz     .notcopy
        call    zip.read
        jmp     .done
.notcopy:
        mov     dword [eax+streamInfo.fullSize], ecx
        and     dword [eax+streamInfo.fullSize+4], 0
        call    read_7z_to_buf
.done:
        sub     edi, [esp+8]
        mov     eax, edi
        add     dword [ebx+file_handle_zip.pos], eax
        adc     dword [ebx+file_handle_zip.pos+4], 0
.ret:
        ret     12

.error:
        push    ContinueBtn
        push    1
        push    aArchiveDataError_ptr
        push    1
        call    [SayErr]
.clear:
        mov     ebx, [esp+4]
        mov     [ebx+file_handle_zip.bError], 1
.reterr:
        or      eax, -1
        ret     12

zipin.fillBuf:
        mov     ebx, [eax+streamInfo.size]
        call    zip.read
        popad
        ret

; void __stdcall setpos(HANDLE hFile, __int64 pos);
setpos_zip:
        mov     [_esp], esp
        mov     [_ebp], ebp
        mov     [error_proc], read_zip.error
        mov     [clear_proc], read_zip.clear
        cmp     [ebx+file_handle_zip.decoder], 0
        jnz     .notcopy
        mov     esi, [esp+8]
        mov     edi, [esp+12]
        call    zip.setpos
        mov     dword [ebx+file_handle_zip.pos], esi
        mov     dword [ebx+file_handle_zip.pos+4], edi
        ret     12
.notcopy:
        cmp     [ebx+file_handle_zip.bError], 0
        jnz     .backward
        mov     ecx, [esp+8]
        mov     edx, [esp+12]
        sub     ecx, dword [ebx+file_handle_zip.pos]
        sbb     edx, dword [ebx+file_handle_zip.pos+4]
        jb      .backward
        mov     eax, [ebx+file_handle_zip.decoder]
        mov     dword [eax+streamInfo.fullSize], ecx
        mov     dword [eax+streamInfo.fullSize+4], edx
        call    skip_7z
        add     dword [ebx+file_handle_zip.pos], ecx
        adc     dword [ebx+file_handle_zip.pos+4], edx
        ret     12
.backward:
        xor     eax, eax
        mov     [ebx+file_handle_zip.bError], al
        mov     dword [ebx+file_handle_zip.pos], eax
        mov     dword [ebx+file_handle_zip.pos+4], eax
        mov     dword [ebx+file_handle_zip.posc], eax
        mov     dword [ebx+file_handle_zip.posc+4], eax
        mov     dword [ebx+file_handle_zip.cur_disk_start], eax
        mov     dword [ebx+file_handle_zip.cur_disk_start+4], eax
        lea     esi, [ebx+file_handle_zip.saved_keys]
        lea     edi, [ebx+file_handle_zip.keys]
        movsd
        movsd
        movsd
        mov     eax, [ebx+file_handle_zip.start_disk]
        mov     [ebx+file_handle_zip.cur_disk], eax
        mov     eax, [ebx+file_handle_zip.decoder]
        mov     eax, [eax+deflate_decoder.inStream]
        mov     esi, [ebx+file_handle_zip.item]
        mov     ecx, dword [esi+file_in_zip.compressed_size]
        mov     dword [eax+streamInfo.fullSize], ecx
        mov     ecx, dword [esi+file_in_zip.compressed_size+4]
        mov     dword [eax+streamInfo.fullSize+4], ecx
        and     [eax+streamInfo.bufDataLen], 0
        lea     ebp, [eax+streamInfo.size+4]
        add     ebp, [eax+streamInfo.bufSize]
        and     [ebp+streamInfo.bufDataLen], 0
        call    deflate_init_decoder
        cmp     [esi+file_in_zip.method], 9
        setz    [ebp+deflate_decoder.bDeflate64]
        mov     eax, [esp+8]
        mov     dword [ebp+streamInfo.fullSize], eax
        mov     eax, [esp+12]
        mov     dword [ebp+streamInfo.fullSize+4], eax
        mov     eax, ebp
        call    skip_7z
        mov     eax, [esp+8]
        mov     dword [ebx+file_handle_zip.pos], eax
        mov     eax, [esp+12]
        mov     dword [ebx+file_handle_zip.pos+4], eax
        ret     12
