; Loading of 7z archives was ported from 7z sources (file 7zip\Archive\7z\7zIn.cpp).
; The version 7-Zip 4.42 was used.
; 7-Zip is copyright (C) 1999-2006 Igor Pavlov.
; Assembler version as KFar plugin has been written by diamond, 2007.

virtual at 0
file_in_7z:
.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      ?
.attr           dd      ?
.FileCRC        dd      ?
.startPos       dq      ?
.CreationTime   dq      ?
.LastAccessTime dq      ?
.LastWriteTime  dq      ?
.UnPackSize     dq      ?
.folder         dd      ?
.folder_index   dd      ?
.folderStart    dq      ?
.bAttrDefined           db      ?
.bStartPosDefined       db      ?
.bCRCDefined            db      ?
.bCreationTimeDefined   db      ?
.bLastAccessTimeDefined db      ?
.bLastWriteTimeDefined  db      ?
.bHasStream             db      ?
.bAnti                  db      ?
.size = $
end virtual

virtual at 0
handle_7z:
.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
; streams info
.numPackStreams dd      ?
.packSizes      dd      ?
.numFolders     dd      ?
.unpackCRCsDefined dd   ?
.unpackCRCs     dd      ?
.folders        dd      ?
.last_folder    dd      ?
.last_pos       dq      ?
.last_context   dd      ?
.last_main_stream dd    ?
.password_len   dd      ?       ; -1 if no password defined; in characters
.password       rw      password_maxlen
.basesize = $
; .size = .basesize + .NumFiles * file_in_7z.size
end virtual

iglobal
align 4
empty_7z_handle:
        dd      type_7z
        dd      0,0,0,0,0,0
endg

kEnd = 0
kHeader = 1
kArchiveProperties = 2
kAdditionalStreamsInfo = 3
kMainStreamsInfo = 4
kFilesInfo = 5
kPackInfo = 6
kUnPackInfo = 7
kSubStreamsInfo = 8
kSize = 9
kCRC = 10
kFolder = 11
kCodersUnPackSize = 12
kNumUnPackStream = 13
kEmptyStream = 14
kEmptyFile = 15
kAnti = 16
kName = 17
kCreationTime = 18
kLastAccessTime = 19
kLastWriteTime = 20
kWinAttributes = 21
kComment = 22
kEncodedHeader = 23
kStartPos = 24

_7z.ReadByte:
        cmp     edi, [bufend]
        jae     return.err
        mov     al, [edi]
        inc     edi
        ret
_7z.ReadWord:
        add     edi, 2
        cmp     edi, [bufend]
        ja      return.err
        mov     ax, [edi-2]
        ret
_7z.ReadDword:
        add     edi, 4
        cmp     edi, [bufend]
        ja      return.err
        mov     eax, [edi-4]
        ret

_7z.SkipSize:
        test    edx, edx
        jnz     return.err
        add     edi, eax
        jc      return.err
        cmp     edi, [bufend]
        ja      return.err
        ret

_7z.ReadNumber:
        xor     eax, eax
        push    eax
        push    eax
        call    _7z.ReadByte
        push    ecx
        xor     ecx, ecx
@@:
        add     al, al
        jnc     @f
        inc     ecx
        jmp     @b
@@:
        shr     eax, cl
        shr     eax, 1
        jz      @f
        mov     [esp+4+ecx], al
@@:
        jecxz   .ret
        push    edx
        xor     edx, edx
@@:
        call    _7z.ReadByte
        mov     [esp+8+edx], al
        inc     edx
        loop    @b
        pop     edx
.ret:
        pop     ecx
        pop     eax
        pop     edx
        ret

_7z.ReadNum:
        push    edx
        call    _7z.ReadNumber
        test    edx, edx
        jnz     return.err
        test    eax, eax
        js      return.err
        pop     edx
        ret

open_7z:
; CInArchive::ReadDatabase()
        cmp     byte [esi+6], 0         ; major version must be 0
                                        ; minor version does not be checked ([byte +7]=2 for now)
        jz      @f
        xor     eax, eax
        ret
@@:
        and     [hOut], 0
        and     [_7z.FreeSubStreams], 0
        and     [_7z.tmpVector], 0
        push    esi
        add     esi, 12
        mov     ecx, 20
        call    crc
        pop     esi
        cmp     eax, [esi+8]
        jnz     .header_crc_error
        cmp     dword [esi+24], 0
        jnz     .header_error
        mov     edi, buffer
        mov     ecx, [esi+20]
        mov     [bufsize], ecx
        test    ecx, ecx
        jnz     .notempty
        mov     eax, empty_7z_handle
        push    ebp
        call    [close]
        ret
.notempty:
        cmp     ecx, 1024
        jbe     @f
        call    [pgalloc]
        test    eax, eax
        jz      .reterr
        mov     edi, eax
@@:
        mov     [bufptr], edi
        lea     eax, [edi+ecx]
        mov     [bufend], eax
        mov     eax, [esi+12]
        mov     edx, [esi+16]
        add     eax, 20h        ; end of main header
        adc     edx, 0
        push    edx
        push    eax
        push    0
        push    ebp
        call    [seek]
        push    ecx
        push    edi
        push    ebp
        call    [read]
        cmp     eax, ecx
        jnz     .header_error
        push    esi
        mov     esi, edi
        call    crc
        pop     esi
        cmp     eax, [esi+28]
        jz      .header_ok
.header_crc_error:
.header_error:
        push    ContinueBtn
        push    1
        push    HeaderError_ptr
        push    1
        call    [SayErr]
.clear:
        mov     ecx, [_7z.tmpVector]
        jecxz   @f
        call    [pgfree]
@@:
        mov     ecx, [hOut]
        jecxz   @f
        call    _7z.CloseHandle
@@:
        mov     ebx, [_7z.dataVectorSize]
        test    ebx, ebx
        jz      @f
        call    _7z.ReadAndDecodePackedStreams.free2
@@:
        mov     ecx, [_7z.FreeSubStreams]
        jecxz   @f
        call    [pgfree]
@@:
        mov     ecx, [bufptr]
        cmp     ecx, buffer
        jz      .reterr
        call    [pgfree]
.reterr:
        or      eax, -1
        ret
.header_ok:
        mov     [_esp], esp
        mov     [inStream], ebp
.loop:
        mov     [error_proc], .header_error
        mov     [clear_proc], .clear
        and     [_7z.dataVectorSize], 0
        call    _7z.ReadNum
        cmp     eax, kHeader
        jz      .found_header
        cmp     eax, kEncodedHeader
        jnz     .header_error
        call    _7z.ReadAndDecodePackedStreams
        cmp     [_7z.StreamsInfo.numFolders], 0
        jz      .free_empty
        cmp     [_7z.StreamsInfo.numFolders], 1
        jnz     return.err
        mov     ecx, [bufptr]
        cmp     ecx, buffer
        jz      @f
        call    [pgfree]
@@:
        mov     edi, [_7z.unpbuf+4]
        mov     [bufptr], edi
        mov     ecx, [_7z.unpbuf]
        add     ecx, edi
        mov     [bufend], ecx
        mov     [error_proc], .header_error
        mov     [clear_proc], .clear
        and     [_7z.dataVectorSize], 0
        call    _7z.ReadNum
        cmp     eax, kHeader
        jz      .found_header
        jmp     return.err
.free_empty:
        mov     ecx, [bufptr]
        cmp     ecx, buffer
        jz      @f
        call    [pgfree]
@@:
        push    [inStream]
        call    [close]
        mov     eax, empty_7z_handle
        ret
.found_header:
        call    _7z.ReadNum
        cmp     eax, kArchiveProperties
        jnz     .no_archive_props
@@:
        call    _7z.ReadNum
        test    eax, eax
        jz      @f
        call    _7z.SkipData
        jmp     @b
@@:
        call    _7z.ReadNum
.no_archive_props:
        cmp     eax, kAdditionalStreamsInfo
        jnz     .no_additional_streams
        call    _7z.ReadAndDecodePackedStreams
        mov     eax, [_7z.unpacked]
        mov     [_7z.dataVector], eax
        mov     eax, [_7z.StreamsInfo.numFolders]
        mov     [_7z.dataVectorSize], eax
        mov     [error_proc], .header_error
        mov     [clear_proc], .clear
        call    _7z.ReadNum
.no_additional_streams:
        cmp     eax, kMainStreamsInfo
        jnz     return.err
        call    _7z.ReadStreamsInfo
        call    _7z.ReadNum
        test    eax, eax
        jnz     .notempty2
.empty2:
        mov     ebx, [_7z.dataVectorSize]
        test    ebx, ebx
        jz      @f
        call    _7z.ReadAndDecodePackedStreams.free2
@@:
        mov     ecx, [_7z.FreeSubStreams]
        jecxz   @f
        call    [pgfree]
@@:
        mov     ecx, [bufptr]
        cmp     ecx, buffer
        jz      @f
        call    [pgfree]
@@:
        push    [inStream]
        call    [close]
        mov     eax, empty_7z_handle
        ret
.notempty2:
        cmp     eax, kFilesInfo
        jnz     return.err
        call    _7z.ReadNum
        test    eax, eax
        jz      .empty2
        mov     ebx, eax        ; number of files in archive
        imul    ecx, ebx, file_in_7z.size
        add     ecx, handle_7z.basesize
        mov     [hOut.allocated], ecx
        call    [pgalloc]
        test    eax, eax
        jz      return.clear
        mov     [hOut], eax
        mov     [eax+handle_7z.type], type_7z
        mov     [eax+handle_7z.NumFiles], ebx
        push    edi
        lea     edi, [eax+handle_7z.numPackStreams]
        mov     esi, _7z.StreamsInfo
        mov     ecx, 6
        rep     movsd
        or      [eax+handle_7z.password_len], -1
        or      [eax+handle_7z.last_folder], -1
        mov     [eax+handle_7z.last_context], ecx
        mov     [eax+handle_7z.names_buf], ecx
        mov     [eax+handle_7z.host], ecx
        cmp     [bPasswordDefined], cl
        jz      @f
        mov     ecx, [password_size]
        mov     [eax+handle_7z.password_len], ecx
        lea     edi, [eax+handle_7z.password]
        mov     esi, password_unicode
        shr     ecx, 1
        rep     movsd
        adc     ecx, ecx
        rep     movsw
@@:
        lea     edi, [eax+handle_7z.basesize]
        imul    ecx, ebx, file_in_7z.size/4
        xor     eax, eax
        rep     stosd
        pop     edi
        mov     eax, [_7z.StreamsInfo.SubStreamsSizes]
        mov     [_7z.FreeSubStreams], eax
        mov     [error_proc], .header_error
        mov     [clear_proc], .clear
        mov     ecx, ebx
        shl     ecx, 2
        call    [pgalloc]
        test    eax, eax
        jz      return.clear
        push    edi
        mov     edi, eax
        mov     [_7z.tmpVector], eax
        add     eax, ebx
        mov     [_7z.emptyStreams], eax
        add     eax, ebx
        mov     [_7z.emptyFiles], eax
        add     eax, ebx
        mov     [_7z.antiFiles], eax
        mov     ecx, ebx
        xor     eax, eax
        rep     stosd
        pop     edi
        mov     [_7z.numEmptyStreams], eax
.parse_header:
        call    _7z.ReadNum
        test    eax, eax
        jz      .parse_header_done
        mov     ecx, eax        ; ecx = type
        call    _7z.ReadNum     ; eax = size
        cmp     ecx, kName
        jz      .parse_names
        cmp     ecx, kWinAttributes
        jz      .parse_winAttr
        cmp     ecx, kStartPos
        jz      .parse_startPos
        cmp     ecx, kEmptyStream
        jz      .parse_emptyStream
        cmp     ecx, kEmptyFile
        jz      .parse_emptyFile
        cmp     ecx, kAnti
        jz      .parse_anti
        cmp     ecx, kCreationTime
        jz      .parse_time
        cmp     ecx, kLastWriteTime
        jz      .parse_time
        cmp     ecx, kLastAccessTime
        jz      .parse_time
        xor     edx, edx
        call    _7z.SkipSize
        jmp     .parse_header
.parse_names:
        push    eax
        call    _7z.SwitchStream
        pop     eax
        jz      @f
        mov     eax, [bufend]
        sub     eax, edi
@@:
        shr     eax, 1          ; size of ANSI string is a half of size of UNICODE string
        mov     ecx, eax
        call    [pgalloc]
        test    eax, eax
        jz      return.clear
        mov     ebp, eax
        mov     esi, [hOut]
        mov     [esi+handle_7z.names_buf], eax
        add     eax, ecx
        push    eax     ; remember limit
        mov     ecx, ebx
        add     esi, handle_7z.basesize + file_in_7z.fullname
        mov     [bWasWarning], 0
.parse_names_loop:
        mov     [esi], ebp
        add     esi, file_in_7z.size
.parse_names_loop_int:
        cmp     ebp, [esp]
        jae     return.err
        call    _7z.ReadWord
; UNICODE char -> cp866 char
        cmp     ax, 0x80
        jb      .id
        cmp     ax, 0x401
        jz      .yo1
        cmp     ax, 0x451
        jz      .yo2
        cmp     ax, 0x410
        jb      .unk
        cmp     ax, 0x440
        jb      .rus1
        cmp     ax, 0x450
        jb      .rus2
.unk:
        cmp     [bWasWarning], 0
        jnz     @f
        push    ContinueBtn
        push    1
        push    aEncodingProblem_str_ptr
        push    3
        push    aEncodingProblem
        call    [Message]
        inc     [bWasWarning]
@@:
        mov     al, '_'
        jmp     .id
.yo1:
        mov     al, 'Ё'
        jmp     .id
.yo2:
        mov     al, 'ё'
        jmp     .id
.rus1:
; 0x410-0x43F -> 0x80-0xAF
        add     al, 0x70
        jmp     .id
.rus2:
; 0x440-0x44F -> 0xE0-0xEF
        add     al, 0xA0
.id:
        mov     [ebp], al
        inc     ebp
        test    al, al
        jnz     .parse_names_loop_int
        loop    .parse_names_loop
        pop     eax
        call    _7z.UnswitchStream
        jmp     .parse_header
.parse_winAttr:
        mov     edx, [_7z.tmpVector]
        push    ebx
        mov     ecx, ebx
        mov     ebx, edx
        call    _7z.ReadBoolVector2
        pop     ebx
        call    _7z.SwitchStream
        mov     esi, [hOut]
        add     esi, handle_7z.basesize
        mov     ecx, ebx
.winAttrLoop:
        mov     al, [edx]
        inc     edx
        mov     [esi+file_in_7z.bAttrDefined], al
        test    al, al
        jz      @f
        call    _7z.ReadDword
        mov     [esi+file_in_7z.attr], eax
@@:
        add     esi, file_in_7z.size
        loop    .winAttrLoop
        call    _7z.UnswitchStream
        jmp     .parse_header
.parse_startPos:
        mov     edx, [_7z.tmpVector]
        push    ebx
        mov     ecx, ebx
        mov     ebx, edx
        call    _7z.ReadBoolVector2
        pop     ebx
        call    _7z.SwitchStream
        mov     esi, [hOut]
        add     esi, handle_7z.basesize
        mov     ecx, ebx
.startPosLoop:
        mov     al, [edx]
        inc     edx
        mov     [esi+file_in_7z.bStartPosDefined], al
        test    al, al
        jz      @f
        call    _7z.ReadDword
        mov     dword [esi+file_in_7z.startPos], eax
        call    _7z.ReadDword
        mov     dword [esi+file_in_7z.startPos+4], eax
@@:
        add     esi, file_in_7z.size
        loop    .startPosLoop
        call    _7z.UnswitchStream
        jmp     .parse_header
.parse_emptyStream:
        mov     edx, [_7z.emptyStreams]
        push    ebx
        mov     ecx, ebx
        mov     ebx, edx
        call    _7z.ReadBoolVector
        pop     ebx
        and     [_7z.numEmptyStreams], 0
        mov     ecx, ebx
.emptyStrLoop:
        cmp     byte [edx], 0
        jz      @f
        inc     [_7z.numEmptyStreams]
@@:
        inc     edx
        loop    .emptyStrLoop
        jmp     .parse_header
.parse_emptyFile:
        push    ebx
        mov     ecx, [_7z.numEmptyStreams]
        mov     ebx, [_7z.emptyFiles]
        call    _7z.ReadBoolVector
        pop     ebx
        jmp     .parse_header
.parse_anti:
        push    ebx
        mov     ecx, [_7z.numEmptyStreams]
        mov     ebx, [_7z.antiFiles]
        call    _7z.ReadBoolVector
        pop     ebx
        jmp     .parse_header
.parse_time:
        push    ecx
        push    ebx
        mov     ecx, ebx
        mov     ebx, [_7z.tmpVector]
        call    _7z.ReadBoolVector2
        pop     ebx
        call    _7z.SwitchStream
        mov     esi, [hOut]
        add     esi, handle_7z.basesize
        xor     ecx, ecx
.timeLoop:
        mov     eax, [_7z.tmpVector]
        mov     al, [eax+ecx]
        pop     edx
        push    edx
        mov     [esi + file_in_7z.bCreationTimeDefined + edx - kCreationTime], al
        test    al, al
        jz      @f
        call    _7z.ReadDword
        push    eax
        call    _7z.ReadDword
        mov     edx, eax
        pop     eax
        push    edi
        mov     edi, [esp+4]
        lea     edi, [esi + file_in_7z.CreationTime + (edi-kCreationTime)*8]
        call    ntfs_datetime_to_bdfe
        pop     edi
@@:
        add     esi, file_in_7z.size
        inc     ecx
        cmp     ecx, ebx
        jb      .timeLoop
        call    _7z.UnswitchStream
        pop     eax
        jmp     .parse_header
.parse_header_done:
        xor     ecx, ecx        ; index in emptyFiles
        xor     edx, edx        ; index in subStreams
        xor     ebp, ebp        ; index in files
        mov     esi, [hOut]
        add     esi, handle_7z.basesize
.filesLoop:
        mov     eax, [_7z.emptyStreams]
        mov     al, [eax+ebp]
        xor     al, 1
        mov     [esi+file_in_7z.bHasStream], al
        jz      .nostream
        mov     [esi+file_in_7z.bIsDirectory], 0
        mov     [esi+file_in_7z.bAnti], 0
        mov     eax, [_7z.StreamsInfo.SubStreamsSizes]
        push    dword [eax+edx*8]
        pop     dword [esi+file_in_7z.UnPackSize]
        push    dword [eax+edx*8+4]
        pop     dword [esi+file_in_7z.UnPackSize+4]
        mov     eax, [_7z.StreamsInfo.SubStreamsCRCsDefined]
        mov     al, [eax+edx]
        mov     [esi+file_in_7z.bCRCDefined], al
        test    al, al
        jz      @f
        mov     eax, [_7z.StreamsInfo.SubStreamsCRCs]
        mov     eax, [eax+edx*4]
        mov     [esi+file_in_7z.FileCRC], eax
@@:
        inc     edx
        jmp     .filesCont
.nostream:
        mov     eax, [_7z.emptyFiles]
        cmp     byte [eax+ecx], 0
        setz    [esi+file_in_7z.bIsDirectory]
        mov     eax, [_7z.antiFiles]
        cmp     byte [eax+ecx], 0
        setnz   [esi+file_in_7z.bAnti]
        and     dword [esi+file_in_7z.UnPackSize], 0
        and     dword [esi+file_in_7z.UnPackSize+4], 0
        mov     [esi+file_in_7z.bCRCDefined], 0
        inc     ecx
.filesCont:
        inc     ebp
        add     esi, file_in_7z.size
        dec     ebx
        jnz     .filesLoop
        mov     ecx, [_7z.tmpVector]
        call    [pgfree]
        and     [_7z.tmpVector], 0
        mov     ebx, [_7z.dataVectorSize]
        test    ebx, ebx
        jz      @f
        call    _7z.ReadAndDecodePackedStreams.free2
        and     [_7z.dataVectorSize], 0
@@:
        mov     ecx, [_7z.FreeSubStreams]
        jecxz   @f
        call    [pgfree]
        and     [_7z.FreeSubStreams], 0
@@:
        mov     ecx, [bufptr]
        cmp     ecx, buffer
        jz      @f
        call    [pgfree]
        mov     [bufptr], buffer
@@:
        xor     esi, esi        ; index of folder
        mov     edi, [hOut]
        mov     ebx, [edi+handle_7z.NumFiles]
        add     edi, handle_7z.basesize ; pointer to fileinfo
        push    0               ; start index of packed stream for folder
.getfoldersloop0:
        xor     edx, edx        ; index in folder
        push    edx edx         ; position in folder
.getfoldersloop:
        dec     ebx
        js      .getfoldersdone
        cmp     [edi+file_in_7z.bHasStream], 0
        jnz     @f
        test    edx, edx
        jz      .nofolder
@@:
        test    edx, edx
        jnz     .notfirst
; skip empty folders
@@:
        mov     eax, [hOut]
        cmp     esi, [eax+handle_7z.numFolders]
        jae     return.err
        mov     eax, [eax+handle_7z.folders]
        mov     eax, [eax+esi*4]
        cmp     edx, [eax+_7z.StreamsInfo.numUnPackStreams]
        jb      @f
        inc     esi
        jmp     @b
@@:
.notfirst:
        mov     [edi+file_in_7z.folder], esi
        mov     [edi+file_in_7z.folder_index], edx
        mov     eax, [esp]
        mov     dword [edi+file_in_7z.folderStart], eax
        mov     eax, [esp+4]
        mov     dword [edi+file_in_7z.folderStart+4], eax
        mov     ecx, dword [edi+file_in_7z.UnPackSize]
        add     [esp], ecx
        mov     ecx, dword [edi+file_in_7z.UnPackSize+4]
        adc     [esp+4], ecx
        add     edi, file_in_7z.size
        inc     edx
        mov     eax, [hOut]
        mov     eax, [eax+handle_7z.folders]
        mov     eax, [eax+esi*4]
        cmp     edx, [eax+_7z.StreamsInfo.numUnPackStreams]
        jb      .getfoldersloop
        inc     esi
        pop     edx edx
        jmp     .getfoldersloop0
.nofolder:
        push    -1
        pop     eax
        mov     [edi+file_in_7z.folder], eax
        mov     [edi+file_in_7z.folder_index], eax
        mov     dword [edi+file_in_7z.folderStart], eax
        mov     dword [edi+file_in_7z.folderStart+4], eax
        add     edi, file_in_7z.size
        jmp     .getfoldersloop
.getfoldersdone:
        pop     eax eax eax
        mov     ebp, [hOut]
        mov     esi, [ebp+handle_7z.folders]
        xor     ebx, ebx
        cmp     ebx, [ebp+handle_7z.numFolders]
        jae     .getoffsdone
.getoffsloop:
        lodsd
        test    ebx, ebx
        jnz     .getoffs2
        mov     edx, dword [_7z.StreamsInfo.dataOffset]
        mov     dword [eax+_7z.StreamsInfo.packOffset], edx
        mov     edx, dword [_7z.StreamsInfo.dataOffset+4]
        mov     dword [eax+_7z.StreamsInfo.packOffset+4], edx
        jmp     .getoffscont
.getoffs2:
        mov     ecx, [esi-8]
        mov     edx, dword [ecx+_7z.StreamsInfo.packOffset]
        mov     dword [eax+_7z.StreamsInfo.packOffset], edx
        mov     edx, dword [ecx+_7z.StreamsInfo.packOffset+4]
        mov     dword [eax+_7z.StreamsInfo.packOffset+4], edx
        mov     edi, [ecx+_7z.StreamsInfo.startPackedStream]
        mov     ecx, [ecx+_7z.StreamsInfo.numPackedStreams]
        push    ecx
        add     ecx, edi
        cmp     ecx, [ebp+handle_7z.numPackStreams]
        ja      return.err
        pop     ecx
        shl     edi, 3
        add     edi, [ebp+handle_7z.packSizes]
@@:
        dec     ecx
        js      @f
        mov     edx, [edi]
        add     dword [eax+_7z.StreamsInfo.packOffset], edx
        mov     edx, [edi+4]
        adc     dword [eax+_7z.StreamsInfo.packOffset+4], edx
        add     edi, 8
        jmp     @b
@@:
.getoffscont:
        inc     ebx
        cmp     ebx, [ebp+handle_7z.numFolders]
        jb      .getoffsloop
.getoffsdone:
        mov     edx, [hOut]
        mov     ebx, [edx+handle_7z.NumFiles]
        lea     edi, [edx+handle_7z.root.subfolders]
        add     edx, handle_7z.basesize
        push    file_in_7z.size
        call    init_file_links
        mov     eax, [hOut]
        and     [eax+handle_7z.curdir], 0       ; set root directory
        mov     esi, [inStream]
        mov     [eax+handle_7z.host], esi
        lea     edi, [eax+handle_7z.host_datetime]
        mov     esi, [esp+12]
        add     esi, 8
        mov     ecx, 6
        rep     movsd
        ret

_7z.CloseHandle:
        mov     esi, ecx
        push    [esi+handle_7z.host]
        call    [close]
        mov     ecx, [esi+handle_7z.packSizes]
        call    [pgfree]
        mov     ecx, [esi+handle_7z.folders]
        call    [pgfree]
        mov     ecx, [esi+handle_7z.names_buf]
        call    [pgfree]
        mov     ecx, [esi+handle_7z.last_context]
        jecxz   @f
        call    [pgfree]
@@:
        mov     ecx, esi
        call    [pgfree]
        ret

close_7z:
        mov     ecx, [esp+4]
        cmp     ecx, empty_7z_handle
        jz      @f
        call    _7z.CloseHandle
@@:
        ret     4

uglobal
align 4
_7z.unpacked    dd      ?
_7z.unpbuf      rd      2*8
_7z.CurInStream dd      ?
_7z.dataVector  dd      ?
_7z.dataVectorSize dd   ?
_7z.FreeSubStreams dd   ?
_7z.tmpVector   dd      ?
_7z.emptyStreams dd     ?
_7z.numEmptyStreams dd  ?
_7z.emptyFiles  dd      ?
_7z.antiFiles   dd      ?
endg

_7z.ReadAndDecodePackedStreams:
        call    _7z.ReadStreamsInfo
        mov     ecx, [_7z.StreamsInfo.numFolders]
        test    ecx, ecx
        jz      .ret2
        push    ecx
        mov     ecx, [_7z.StreamsInfo.SubStreamsSizes]
        jecxz   @f
        call    [pgfree]
        and     [_7z.StreamsInfo.SubStreamsSizes], 0
@@:
        pop     ecx
        mov     eax, _7z.unpbuf
        cmp     ecx, 8
        jbe     .1
        shl     ecx, 3
        call    [pgalloc]
        test    eax, eax
        jz      return.clear
        mov     esi, eax
@@:
        and     dword [esi], 0
        add     esi, 4
        sub     ecx, 4
        jnz     @b
.1:
        mov     [_7z.unpacked], eax
        mov     esi, eax
        xor     ecx, ecx
        mov     [_7z.CurInStream], ecx
        mov     [error_proc], .error
        mov     [clear_proc], .clear
.loop:
        mov     eax, [_7z.StreamsInfo.folders]
        mov     ebx, [eax+ecx*4]
        call    _7z.GetUnPackSize
        test    edx, edx
        jnz     return.err
        test    eax, eax
        js      return.err
        push    ecx
        mov     [esi], eax
        mov     [_7z.decode.outBufferSize], eax
        mov     ecx, eax
        call    [pgalloc]
        test    eax, eax
        jz      return.err
        mov     [esi+4], eax
        mov     [_7z.decode.outBufferPtr], eax
        push    esi
        call    _7z.decode
        pop     esi
        pop     ecx
        push    ecx
        mov     eax, [_7z.StreamsInfo.unpackCRCsDefined]
        cmp     byte [eax+ecx], 0
        jz      @f
        push    esi
        mov     ecx, [esi]
        mov     esi, [esi+4]
        call    crc
        pop     esi
        pop     ecx
        push    ecx
        mov     edx, [_7z.StreamsInfo.unpackCRCs]
        cmp     [edx+ecx*4], eax
        jnz     return.err
@@:
        add     esi, 8
        pop     ecx
        inc     ecx
        cmp     ecx, [_7z.StreamsInfo.numFolders]
        jb      .loop
.ret:
        mov     ecx, [_7z.StreamsInfo.folders]
        call    [pgfree]
        mov     [error_proc], .error2
        mov     [clear_proc], .clear2
.ret2:
        mov     ecx, [_7z.StreamsInfo.packSizes]
        call    [pgfree]
        ret

.error:
        call    .free
        jmp     _7z.ReadStreamsInfo.error
.clear:
        call    .free
        jmp     _7z.ReadStreamsInfo.clear
.error2:
        call    .free
        jmp     open_7z.header_error
.clear2:
        call    .free
        jmp     open_7z.clear
.free:
        mov     ebx, [_7z.StreamsInfo.numFolders]
.free2:
        dec     ebx
        js      @f
        mov     ecx, [_7z.unpacked]
        mov     ecx, [ecx+ebx*8+4]
        call    [pgfree]
        jmp     .free2
@@:
        mov     ecx, [_7z.unpacked]
        cmp     ecx, _7z.unpbuf
        jz      @f
        call    [pgfree]
@@:
        ret

_7z.GetUnPackSize:
        xor     edx, edx
        mov     eax, [ebx+_7z.StreamsInfo.numOutStreams]
        test    eax, eax
        jz      .ret
.scan:
        dec     eax
        js      return.err
        push    ecx
        mov     ecx, [ebx+_7z.StreamsInfo.numBindPairs]
        mov     edx, [ebx+_7z.StreamsInfo.bindPairs]
@@:
        add     edx, 8
        dec     ecx
        js      .found
        cmp     [edx-4], eax
        jnz     @b
        pop     ecx
        jmp     .scan
.found:
        mov     [ebx+_7z.StreamsInfo.mainOutStream], eax
        mov     ecx, [ebx+_7z.StreamsInfo.unpackSizes]
        mov     edx, [ecx+eax*8+4]
        mov     eax, [ecx+eax*8]
        pop     ecx
.ret:
        ret

uglobal
align 4
_7z.StreamsInfo:
.numPackStreams dd      ?
.packSizes      dd      ?       ; 8 bytes per item
.numFolders     dd      ?
.unpackCRCsDefined dd   ?
.unpackCRCs     dd      ?
.folders        dd      ?
.folders.size   dd      ?
.folders.alloc  dd      ?
.dataOffset     dq      ?
.packCRCsDefined dd     ?       ; 1 byte per item
.packCRCs       dd      ?       ; 4 bytes per item
virtual at 0
.numCoders      dd      ?
.bindPairs      dd      ?
.numBindPairs   dd      ?
.packedStreams  dd      ?
.numPackedStreams dd    ?
.startPackedStream dd   ?
.numOutStreams  dd      ?
.unpackSizes    dd      ?
.mainOutStream  dd      ?
.numUnPackStreams dd    ?
.packOffset     dq      ?
.fsz = $
end virtual
.numSubStreams  dd      ?
.SubStreamsSizes dd     ?
.SubStreamsCRCsDefined dd ?
.SubStreamsCRCs dd      ?
.tmpSubStreamsCRCsDefined dd ?
.tmpSubStreamsCRCs dd   ?
endg

_7z.SkipData:
        call    _7z.ReadNumber
        call    _7z.SkipSize
        ret

_7z.WaitAttribute:
        call    _7z.ReadNumber
        cmp     eax, ecx
        jz      @f
        test    eax, eax
        jz      return.err
        call    _7z.SkipData
        jmp     _7z.WaitAttribute
@@:
        ret

_7z.ReadBoolVector2:
; in: ebx->vector, ecx=size
        call    _7z.ReadByte
        test    al, al
        jz      _7z.ReadBoolVector
        push    ebx ecx
        jecxz   .ret
@@:
        mov     byte [ebx], 1
        inc     ebx
        loop    @b
.ret:
        pop     ecx ebx
        ret

_7z.ReadBoolVector:
        push    ebx ecx
        jecxz   .ret
        xor     eax, eax
.1:
        add     al, al
        jnz     @f
        call    _7z.ReadByte
        stc
        adc     al, al
@@:
        setc    byte [ebx]
        inc     ebx
        loop    .1
.ret:
        pop     ecx ebx
        ret

_7z.ReadHashDigests:
; in: ebx->bool vector, esi->crc, ecx=size
        call    _7z.ReadBoolVector2
        jecxz   .ret
.1:
        xor     eax, eax
        cmp     byte [ebx], al
        jz      @f
        call    _7z.ReadDword
@@:
        mov     [esi], eax
        add     esi, 4
        inc     ebx
        loop    .1
.ret:
        ret

uglobal
_7z.saved_edi   dd      ?
_7z.saved_edi_end dd    ?
endg

_7z.SwitchStream:
        mov     eax, [bufend]
        mov     [_7z.saved_edi_end], eax
        call    _7z.ReadByte
        test    al, al
        jnz     @f
        ret
@@:
        call    _7z.ReadNum
        mov     [_7z.saved_edi], edi
        cmp     eax, [_7z.dataVectorSize]
        jae     return.err
        shl     eax, 3
        add     eax, [_7z.dataVector]
        mov     edi, [eax+4]
        mov     eax, [eax]
        add     eax, edi
        mov     [bufend], eax
        ret

_7z.UnswitchStream:
        mov     eax, [_7z.saved_edi_end]
        cmp     eax, [bufend]
        jnz     @f
        ret
@@:
        mov     [bufend], eax
        mov     edi, [_7z.saved_edi]
@@:
        ret

; 6 программистов пытались код понять,
; Один из них сошёл с ума, и их осталось 5.

_7z.ReadStreamsInfo:
        xor     eax, eax
        mov     [_7z.StreamsInfo.numPackStreams], eax
        mov     [_7z.StreamsInfo.packSizes], eax
        mov     [_7z.StreamsInfo.numFolders], eax
        mov     [_7z.StreamsInfo.folders], eax
        mov     [_7z.StreamsInfo.numSubStreams], eax
        mov     [_7z.StreamsInfo.SubStreamsSizes], eax
        mov     [_7z.StreamsInfo.tmpSubStreamsCRCsDefined], eax
        mov     [error_proc], .error
        mov     [clear_proc], .clear
.mainloop:
        call    _7z.ReadNum
        test    eax, eax
        jz      @b
        cmp     eax, kSubStreamsInfo
        jz      .SubStreamsInfo
        cmp     eax, kUnPackInfo
        jz      .UnpackInfo
        cmp     eax, kPackInfo
        jnz     return.err

.PackInfo:
        cmp     [_7z.StreamsInfo.numPackStreams], 0
        jnz     return.err
        call    _7z.ReadNumber
        add     eax, 0x20
        adc     edx, 0
        mov     dword [_7z.StreamsInfo.dataOffset], eax
        mov     dword [_7z.StreamsInfo.dataOffset+4], edx
        call    _7z.ReadNum
        mov     [_7z.StreamsInfo.numPackStreams], eax
        mov     ecx, kSize
        call    _7z.WaitAttribute
        mov     ecx, [_7z.StreamsInfo.numPackStreams]
        imul    ecx, 8+1+4
        jecxz   .nostreams1
        call    [pgalloc]
        test    eax, eax
        jz      return.clear
        mov     [_7z.StreamsInfo.packSizes], eax
        mov     ecx, [_7z.StreamsInfo.numPackStreams]
        push    eax
        lea     eax, [eax+ecx*8]
        mov     [_7z.StreamsInfo.packCRCsDefined], eax
        mov     ebx, eax
        add     eax, ecx
        mov     [_7z.StreamsInfo.packCRCs], eax
@@:
        mov     byte [ebx], 0
        inc     ebx
        loop    @b
        pop     eax
.nostreams1:
        mov     ecx, [_7z.StreamsInfo.numPackStreams]
        jecxz   .noloop1
        mov     esi, eax
@@:
        call    _7z.ReadNumber
        mov     [esi], eax
        mov     [esi+4], edx
        add     esi, 8
        loop    @b
.noloop1:
        call    _7z.ReadNum
        test    eax, eax
        jz      .mainloop
        cmp     eax, kCRC
        jz      .packInfo.crc
        call    _7z.SkipData
        jmp     .noloop1
.packInfo.crc:
        mov     esi, [_7z.StreamsInfo.packCRCs]
        mov     ebx, [_7z.StreamsInfo.packCRCsDefined]
        mov     ecx, [_7z.StreamsInfo.numPackStreams]
        call    _7z.ReadHashDigests
        jmp     .noloop1

.UnpackInfo:
        cmp     [_7z.StreamsInfo.folders], 0
        jnz     return.err
        mov     ecx, kFolder
        call    _7z.WaitAttribute
        call    _7z.ReadNum
        mov     ecx, eax
        mov     [_7z.StreamsInfo.numFolders], eax
        call    _7z.SwitchStream
        test    ecx, ecx
        jz      .nofolders
        push    ecx
        mov     ecx, 0x1000
        and     [_7z.StreamsInfo.folders.size], 0
        mov     [_7z.StreamsInfo.folders.alloc], ecx
        call    [pgalloc]
        test    eax, eax
        jz      return.clear
        mov     [_7z.StreamsInfo.folders], eax
        pop     ecx
        lea     ecx, [ecx*9]
        call    .folders_alloc
        xor     ecx, ecx
.folderloop:
        mov     eax, [_7z.StreamsInfo.folders]
        push    [_7z.StreamsInfo.folders.size]
        pop     dword [eax+ecx*4]
        push    ebp
        mov     [_ebp], ebp
        push    ecx
        mov     ecx, _7z.StreamsInfo.fsz
        call    .folders_alloc
        mov     ebp, eax
        add     ebp, [_7z.StreamsInfo.folders]
        call    _7z.ReadNum
        mov     [ebp+_7z.StreamsInfo.numCoders], eax
        test    eax, eax
        jz      return.err
        mov     ecx, eax
        push    ecx
        shl     ecx, 2
        sub     ebp, [_7z.StreamsInfo.folders]
        call    .folders_alloc
        add     ebp, [_7z.StreamsInfo.folders]
        pop     ecx
        add     eax, [_7z.StreamsInfo.folders]
@@:
        and     dword [eax], 0
        add     eax, 4
        loop    @b
        push    0       ; numOutStreams
        push    0       ; numInStreams
; [folders] = array of pointers to coders info
; Format of coder info:
; dd NextCoder
; db MethodID.IDSize
; times IDSize db MethodID.ID
; dd NumInStreams
; dd NumOutStreams
; dd PropSize
; times PropSize db Properties
        xor     esi, esi
.coders_loop:
        call    _7z.ReadByte
        mov     dl, al
        and     eax, 0xF
        lea     ecx, [eax+17]
        sub     ebp, [_7z.StreamsInfo.folders]
        call    .folders_alloc
        add     ebp, [_7z.StreamsInfo.folders]
        push    eax
        mov     ecx, [_7z.StreamsInfo.folders]
        lea     ebx, [ebp+esi*4+_7z.StreamsInfo.fsz]
@@:
        cmp     dword [ebx], 0
        jz      @f
        mov     ebx, [ebx]
        add     ebx, ecx
        jmp     @b
@@:
        mov     [ebx], eax
        pop     ebx
        add     ebx, ecx
        and     dword [ebx], 0
        add     ebx, 4
        mov     ecx, edx
        and     ecx, 0xF
        mov     byte [ebx], cl
        inc     ebx
        jecxz   .2
@@:
        call    _7z.ReadByte
        mov     byte [ebx], al
        inc     ebx
        loop    @b
.2:
        test    dl, 10h
        jnz     .coder_multiple_streams
        mov     dword [ebx], 1
        mov     dword [ebx+4], 1
        inc     dword [esp]
        inc     dword [esp+4]
        jmp     @f
.coder_multiple_streams:
        call    _7z.ReadNum
        mov     [ebx], eax
        add     dword [esp], eax
        call    _7z.ReadNum
        mov     [ebx+4], eax
        add     dword [esp+4], eax
; all currently defined 7z coders have 1 unpacked stream
; moreover, all reasonable coders have 1 unpacked stream
; so assume 1 output stream for unpacker - this simplifies decoding procedure
        cmp     eax, 1
        jnz     return.err
@@:
        and     dword [ebx+8], 0
        add     ebx, 12
        test    dl, 20h
        jz      .coder_no_properties
        call    _7z.ReadNum
        test    eax, eax
        jz      .coder_no_properties
        mov     [ebx-4], eax
        mov     ecx, eax
        sub     ebx, [_7z.StreamsInfo.folders]
        sub     ebp, [_7z.StreamsInfo.folders]
        call    .folders_alloc
        add     ebp, [_7z.StreamsInfo.folders]
        add     ebx, [_7z.StreamsInfo.folders]
@@:
        call    _7z.ReadByte
        mov     [ebx], al
        inc     ebx
        loop    @b
.coder_no_properties:
        test    dl, dl
        js      .coders_loop
        inc     esi
        cmp     esi, [ebp+_7z.StreamsInfo.numCoders]
        jb      .coders_loop
        mov     [ebp+_7z.StreamsInfo.numUnPackStreams], 1
        mov     eax, [esp+4]
        dec     eax
        js      return.err
        mov     [ebp+_7z.StreamsInfo.numBindPairs], eax
        push    eax
        mov     ecx, eax
        shl     ecx, 3
        sub     ebp, [_7z.StreamsInfo.folders]
        call    .folders_alloc
        add     ebp, [_7z.StreamsInfo.folders]
        mov     [ebp+_7z.StreamsInfo.bindPairs], eax
        pop     ecx
        jecxz   .noloop3
        mov     esi, eax
        add     esi, [_7z.StreamsInfo.folders]
@@:
        call    _7z.ReadNum
        mov     [esi], eax
        cmp     eax, [esp]
        jae     return.err
        call    _7z.ReadNum
        mov     [esi+4], eax
        cmp     eax, [esp+4]
        jae     return.err
        add     esi, 8
        loop    @b
.noloop3:
        pop     eax     ; numInStreams
        pop     edx     ; numOutStreams
        mov     [ebp+_7z.StreamsInfo.numOutStreams], edx
        sub     eax, edx
        js      return.err
        inc     eax
        mov     [ebp+_7z.StreamsInfo.numPackedStreams], eax
        mov     ecx, eax
        push    eax
        shl     ecx, 2
        sub     ebp, [_7z.StreamsInfo.folders]
        call    .folders_alloc
        add     ebp, [_7z.StreamsInfo.folders]
        mov     [ebp+_7z.StreamsInfo.packedStreams], eax
        pop     ecx
        mov     esi, eax
        add     esi, [_7z.StreamsInfo.folders]
        cmp     ecx, 1
        jnz     .multiplePackedStreams
        mov     ebx, [ebp+_7z.StreamsInfo.bindPairs]
        add     ebx, [_7z.StreamsInfo.folders]
        push    edx
        xor     ecx, ecx
.3:
        push    ebx
        mov     edx, [ebp+_7z.StreamsInfo.numBindPairs]
@@:
        dec     edx
        js      .5
        cmp     dword [ebx], ecx
        jz      .4
        add     ebx, 8
        jmp     @b
.4:
        pop     ebx
        inc     ecx
        jmp     .3
.5:
        pop     ebx
        pop     edx
        mov     [esi], ecx
        jmp     .commonPackedStreams
.multiplePackedStreams:
        call    _7z.ReadNum
        mov     [esi], eax
        add     esi, 4
        loop    .multiplePackedStreams
.commonPackedStreams:
        mov     ecx, edx
        shl     ecx, 3
        sub     ebp, [_7z.StreamsInfo.folders]
        call    .folders_alloc
        add     ebp, [_7z.StreamsInfo.folders]
        mov     [ebp+_7z.StreamsInfo.unpackSizes], eax
        pop     ecx
        xor     edx, edx
        jecxz   @f
        mov     eax, [_7z.StreamsInfo.folders]
        mov     eax, [eax+(ecx-1)*4]
        add     eax, [_7z.StreamsInfo.folders]
        mov     edx, [eax+_7z.StreamsInfo.numPackedStreams]
        add     edx, [eax+_7z.StreamsInfo.startPackedStream]
@@:
        mov     [ebp+_7z.StreamsInfo.startPackedStream], edx
        pop     ebp
        inc     ecx
        cmp     ecx, [_7z.StreamsInfo.numFolders]
        jb      .folderloop
.nofolders:
        call    _7z.UnswitchStream
        mov     ecx, kCodersUnPackSize
        call    _7z.WaitAttribute
        xor     ecx, ecx
.6:
        cmp     ecx, [_7z.StreamsInfo.numFolders]
        jae     .7
        mov     eax, [_7z.StreamsInfo.folders]
        push    ebp
        mov     ebp, [eax+ecx*4]
        add     ebp, eax
        mov     esi, [ebp+_7z.StreamsInfo.numOutStreams]
        mov     ebp, [ebp+_7z.StreamsInfo.unpackSizes]
        add     ebp, [_7z.StreamsInfo.folders]
@@:
        call    _7z.ReadNumber
        mov     dword [ebp], eax
        mov     dword [ebp+4], edx
        add     ebp, 8
        dec     esi
        jnz     @b
        pop     ebp
        inc     ecx
        jmp     .6
.7:
; convert all relative addresses to pointers
        xor     ecx, ecx
        mov     edx, [_7z.StreamsInfo.folders]
.8:
        cmp     ecx, [_7z.StreamsInfo.numFolders]
        jae     .9
        lea     eax, [edx+ecx*4]
        add     [eax], edx
        mov     eax, [eax]
        add     [eax+_7z.StreamsInfo.bindPairs], edx
        add     [eax+_7z.StreamsInfo.packedStreams], edx
        add     [eax+_7z.StreamsInfo.unpackSizes], edx
        mov     ebx, [eax+_7z.StreamsInfo.numCoders]
        add     eax, _7z.StreamsInfo.fsz
        inc     ecx
.11:
        dec     ebx
        js      .8
        push    eax
@@:
        cmp     dword [eax], 0
        jz      @f
        add     [eax], edx
        mov     eax, [eax]
        jmp     @b
@@:
        pop     eax
        add     eax, 4
        jmp     .11
.9:
; done
        mov     eax, [_7z.StreamsInfo.numFolders]
        lea     edx, [edx+eax*4]
        mov     [_7z.StreamsInfo.unpackCRCsDefined], edx
        add     edx, eax
        mov     [_7z.StreamsInfo.unpackCRCs], edx
@@:
        call    _7z.ReadNum
        test    eax, eax
        jz      .mainloop
        cmp     eax, kCRC
        jz      .unpackInfo.crc
        call    _7z.SkipData
        jmp     @b
.unpackInfo.crc:
        mov     esi, [_7z.StreamsInfo.unpackCRCs]
        mov     ebx, [_7z.StreamsInfo.unpackCRCsDefined]
        mov     ecx, [_7z.StreamsInfo.numFolders]
        call    _7z.ReadHashDigests
        jmp     @b

.SubStreamsInfo:
        cmp     [_7z.StreamsInfo.numFolders], 0
        jz      return.err
        cmp     [_7z.StreamsInfo.numSubStreams], 0
        jnz     return.err
        mov     ebx, [_7z.StreamsInfo.folders]
.ssloop1:
        call    _7z.ReadNum
        cmp     eax, kNumUnPackStream
        jz      .NumUnPackStream
        cmp     eax, kCRC
        jz      .break1
        cmp     eax, kSize
        jz      .break1
        test    eax, eax
        jz      .break1
        call    _7z.SkipData
        jmp     .ssloop1
.NumUnPackStream:
        xor     ecx, ecx
@@:
        call    _7z.ReadNum
        mov     edx, [ebx+ecx*4]
        mov     [edx+_7z.StreamsInfo.numUnPackStreams], eax
        inc     ecx
        cmp     ecx, [_7z.StreamsInfo.numFolders]
        jb      @b
        jmp     .ssloop1
.break1:
; calculate number of substreams and number of defined digests
        xor     ecx, ecx
        xor     ebp, ebp
        push    0
        push    eax
        xor     eax, eax
.ssloop2:
        mov     edx, [ebx+ecx*4]
        mov     edx, [edx+_7z.StreamsInfo.numUnPackStreams]
        cmp     edx, 1
        jz      @f
        inc     ebp
@@:
        add     eax, edx
        jc      return.err
        cmp     edx, 1
        jnz     @f
        push    eax
        mov     eax, [_7z.StreamsInfo.unpackCRCsDefined]
        cmp     byte [eax+ecx], 0
        pop     eax
        jz      @f
        xor     edx, edx
@@:
        add     [esp+4], edx
        inc     ecx
        cmp     ecx, [_7z.StreamsInfo.numFolders]
        jb      .ssloop2
        mov     [_7z.StreamsInfo.numSubStreams], eax
        imul    ecx, eax, 4+8+1
        jecxz   .noss1
        push    eax
        call    [pgalloc]
        pop     ecx
        test    eax, eax
        jz      return.clear
        mov     [_7z.StreamsInfo.SubStreamsSizes], eax
        lea     eax, [eax+ecx*8]
        mov     [_7z.StreamsInfo.SubStreamsCRCsDefined], eax
        add     eax, ecx
        mov     [_7z.StreamsInfo.SubStreamsCRCs], eax
@@:
        dec     eax
        mov     byte [eax], 0
        loop    @b
.noss1:
        mov     ecx, [esp+4]
        jecxz   @f
        imul    ecx, 4+1
        call    [pgalloc]
        test    eax, eax
        jz      return.clear
        mov     [_7z.StreamsInfo.tmpSubStreamsCRCsDefined], eax
        add     eax, [esp+4]
        mov     [_7z.StreamsInfo.tmpSubStreamsCRCs], eax
@@:
        test    ebp, ebp
        jz      @f
        cmp     dword [esp], kSize
        jnz     return.err
@@:
        xor     ecx, ecx
        mov     ebp, [_7z.StreamsInfo.SubStreamsSizes]
.ssloop3:
        mov     ebx, [_7z.StreamsInfo.folders]
        mov     ebx, [ebx+ecx*4]
        mov     eax, [ebx+_7z.StreamsInfo.numUnPackStreams]
        dec     eax
        js      .sscont3
        push    0
        push    0
@@:
        dec     eax
        js      @f
        push    eax
        call    _7z.ReadNumber
        mov     [ebp], eax
        mov     [ebp+4], edx
        add     ebp, 8
        add     [esp+4], eax
        adc     [esp+8], edx
        jc      return.err
        pop     eax
        jmp     @b
@@:
        call    _7z.GetUnPackSize
        sub     eax, [esp]
        sbb     edx, [esp+4]
        jc      return.err
        mov     [ebp], eax
        mov     [ebp+4], edx
        add     ebp, 8
        add     esp, 8
.sscont3:
        inc     ecx
        cmp     ecx, [_7z.StreamsInfo.numFolders]
        jb      .ssloop3
        pop     eax
        cmp     eax, kSize
        jnz     @f
        call    _7z.ReadNum
@@:
        cmp     eax, kCRC
        jz      .sscrc
        test    eax, eax
        jz      .ssend
        call    _7z.SkipData
.ssloop4:
        call    _7z.ReadNum
        jmp     @b
.sscrc:
        pop     ecx
        push    ecx
        mov     ebx, [_7z.StreamsInfo.tmpSubStreamsCRCsDefined]
        mov     esi, [_7z.StreamsInfo.tmpSubStreamsCRCs]
        call    _7z.ReadHashDigests
        xor     ecx, ecx        ; index in out
        xor     edx, edx        ; index in tmp
        push    -1
        pop     ebx             ; index in folders
.ssloop5:
        inc     ebx
        cmp     ebx, [_7z.StreamsInfo.numFolders]
        jae     .ssloop4
        mov     eax, [_7z.StreamsInfo.folders]
        mov     ebp, [eax+ebx*4]
        mov     ebp, [ebp+_7z.StreamsInfo.numUnPackStreams]
        cmp     ebp, 1
        jnz     @f
        mov     eax, [_7z.StreamsInfo.unpackCRCsDefined]
        cmp     byte [eax+ebx], 0
        jz      @f
        mov     esi, [_7z.StreamsInfo.SubStreamsCRCsDefined]
        mov     byte [esi+ecx], 1
        mov     esi, [_7z.StreamsInfo.unpackCRCs]
        mov     eax, [esi+ebx*4]
        mov     esi, [_7z.StreamsInfo.SubStreamsCRCs]
        mov     [esi+ecx*4], eax
        inc     ecx
        jmp     .ssloop5
@@:
        dec     ebp
        js      .ssloop5
        mov     esi, [_7z.StreamsInfo.tmpSubStreamsCRCsDefined]
        mov     al, [esi+edx]
        mov     esi, [_7z.StreamsInfo.SubStreamsCRCsDefined]
        mov     [esi+ecx], al
        mov     esi, [_7z.StreamsInfo.tmpSubStreamsCRCs]
        mov     eax, [esi+edx*4]
        mov     esi, [_7z.StreamsInfo.SubStreamsCRCs]
        mov     [esi+ecx*4], eax
        inc     edx
        inc     ecx
        jmp     @b
.ssend:
        mov     ecx, [_7z.StreamsInfo.tmpSubStreamsCRCsDefined]
        call    [pgfree]
        and     [_7z.StreamsInfo.tmpSubStreamsCRCsDefined], 0
        pop     ecx
        jmp     .mainloop

.folders_alloc:
        push    ecx
        add     ecx, [_7z.StreamsInfo.folders.size]
        sub     ecx, [_7z.StreamsInfo.folders.alloc]
        jbe     @f
        push    edx
        or      ecx, 0xFFF
        inc     ecx
        add     ecx, [_7z.StreamsInfo.folders.alloc]
        mov     [_7z.StreamsInfo.folders.alloc], ecx
        mov     edx, [_7z.StreamsInfo.folders]
        call    [pgrealloc]
        pop     edx
        test    eax, eax
        jz      return.clear
        mov     [_7z.StreamsInfo.folders], eax
@@:
        pop     ecx
        mov     eax, [_7z.StreamsInfo.folders.size]
        add     [_7z.StreamsInfo.folders.size], ecx
        ret

.error:
        mov     ecx, [_7z.StreamsInfo.packSizes]
        jecxz   @f
        call    [pgfree]
@@:
        mov     ecx, [_7z.StreamsInfo.folders]
        jecxz   @f
        call    [pgfree]
@@:
        mov     ecx, [_7z.StreamsInfo.SubStreamsSizes]
        jecxz   @f
        call    [pgfree]
@@:
        mov     ecx, [_7z.StreamsInfo.tmpSubStreamsCRCsDefined]
        jecxz   @f
        call    [pgfree]
@@:
        jmp     open_7z.header_error
.clear:
        mov     ecx, [_7z.StreamsInfo.packSizes]
        jecxz   @f
        call    [pgfree]
@@:
        mov     ecx, [_7z.StreamsInfo.folders]
        jecxz   @f
        call    [pgfree]
@@:
        mov     ecx, [_7z.StreamsInfo.SubStreamsSizes]
        jecxz   @f
        call    [pgfree]
@@:
        mov     ecx, [_7z.StreamsInfo.tmpSubStreamsCRCsDefined]
        jecxz   @f
        call    [pgfree]
@@:
        jmp     open_7z.clear

uglobal
align 4
_7z.decode.mainbuf      dd      ?
_7z.decode.pStreams     dd      ?
_7z.decode.pStreams_def rd      8
_7z.decode.outBufferSize dd     ?
_7z.decode.outBufferPtr dd      ?
_7z.decode.outBufferRead dd     ?
endg

virtual at 0
streamInfo:
.fullSize       dq      ?
.fillBuf        dd      ?       ; procedure which fills the buffer
.bufSize        dd      ?
.bufDataLen     dd      ?
.bufPtr         dd      ?
.size = $
end virtual

_7z.decode:
; in: ebx->folder, outBufferSize, outBufferPtr
; N.B. We assume that every coder has 1 output (unpacked) stream.
        and     [.mainbuf], 0
        mov     [.pStreams], .pStreams_def
        mov     [error_proc], .error
        mov     [clear_proc], .clear
        call    .init
        and     [.outBufferRead], 0
@@:
        push    edi
        mov     eax, [ebx+_7z.StreamsInfo.mainOutStream]
        mov     esi, [.pStreams]
        mov     eax, [esi+eax*4]
        call    fillBuf
        mov     ecx, [eax+streamInfo.bufSize]
        mov     esi, [eax+streamInfo.bufPtr]
        mov     edi, [.outBufferPtr]
        add     edi, [.outBufferRead]
        add     [.outBufferRead], ecx
        mov     edx, ecx
        shr     ecx, 2
        rep     movsd
        mov     ecx, edx
        and     ecx, 3
        rep     movsb
        pop     edi
        mov     eax, [.outBufferRead]
        cmp     eax, [.outBufferSize]
        jb      @b
        mov     ecx, [.pStreams]
        cmp     ecx, .pStreams_def
        jz      @f
        call    [pgfree]
@@:
        mov     ecx, [.mainbuf]
        jecxz   @f
        call    [pgfree]
@@:
        mov     [error_proc], _7z.ReadAndDecodePackedStreams.error
        mov     [clear_proc], _7z.ReadAndDecodePackedStreams.clear
        ret

.error:
        mov     ecx, [.pStreams]
        cmp     ecx, .pStreams_def
        jz      @f
        call    [pgfree]
@@:
        mov     ecx, [.mainbuf]
        jecxz   @f
        call    [pgfree]
@@:
        jmp     _7z.ReadAndDecodePackedStreams.error
.clear:
        mov     ecx, [.pStreams]
        cmp     ecx, .pStreams_def
        jz      @f
        call    [pgfree]
@@:
        mov     ecx, [.mainbuf]
        jecxz   @f
        call    [pgfree]
@@:
        jmp     _7z.ReadAndDecodePackedStreams.clear

.init:
; We use stack to keep some information on streams,
;   to avoid stack overflow we limit possible number of coders.
; Anyway, in real life maximum number of coders is 5
;   (password protection filter + BCJ2 filter + 3 LZMA coders),
;   from command line user can define more coders, but
;   archive with, say, 32 coders is most likely bogus.
        cmp     [ebx+_7z.StreamsInfo.numCoders], 128
        ja      return.err
; allocate space for streams info array
        mov     eax, .pStreams_def
        mov     ecx, [ebx+_7z.StreamsInfo.numPackedStreams]
        add     ecx, [ebx+_7z.StreamsInfo.numCoders]
        cmp     ecx, 8
        jbe     @f
        shl     ecx, 2
        call    [pgalloc]
        test    eax, eax
        jz      return.clear
@@:
        mov     [.pStreams], eax
; calculate size of buffers required for decoders
        xor     ecx, ecx
        push    ecx
        mov     ebp, [ebx+_7z.StreamsInfo.unpackSizes]
.calcbufsz:
; LZMA decoder: method ID = [size=03] 03 01 01, NumInStreams = NumOutStreams = 1
        mov     eax, [ebx+_7z.StreamsInfo.fsz+ecx*4]
        call    _7z.GetCoder
        jnc     .known_id
.unknown_method_id:
        movzx   esi, byte [eax+4]
        lea     ecx, [eax+5]
        mov     ebx, aUnknownMethod.z
@@:
        dec     esi
        js      @f
        mov     byte [ebx], ' '
        inc     ebx
        mov     al, [ecx]
        push    eax
        shr     al, 4
        cmp     al, 10
        sbb     al, 69h
        das
        mov     [ebx], al
        inc     ebx
        pop     eax
        and     al, 0xF
        cmp     al, 10
        sbb     al, 69h
        das
        mov     [ebx], al
        inc     ebx
        inc     ecx
        jmp     @b
@@:
        mov     byte [ebx], 0
        push    ContinueBtn
        push    1
        push    aUnknownMethod_ptr
        push    1
        call    [SayErr]
        jmp     return.clear
iglobal
if lang eq ru
aUnknownMethod  db      'Неизвестный метод:'
else
aUnknownMethod  db      'Unknown method:'
end if
.z:
                times 16*3+1 db 0
aUnknownMethod_ptr dd   aUnknownMethod
endg
.known_id:
        movzx   esi, byte [eax+4]
        lea     esi, [esi+eax+17]
        call    [_7z.GetBufSizeTable+edx*4]
        add     eax, 3
        and     al, not 3
        add     [esp], eax
        jc      .nomem
        cmp     dword [ebp+4], 0
        jnz     @f
        cmp     dword [ebp], edx
        jae     @f
        mov     edx, [ebp]
@@:
        add     edx, 0xF
        and     edx, not 0xF
        add     [esp], edx
        jc      .nomem
        add     ebp, 8
        inc     ecx
        cmp     ecx, [ebx+_7z.StreamsInfo.numCoders]
        jb      .calcbufsz
; add 4 pages for each of not binded input streams
        mov     ecx, [ebx+_7z.StreamsInfo.numPackedStreams]
        shl     ecx, 14
        add     [esp], ecx
;; calculate free RAM
;        call    [getfreemem]
;        sub     eax, 64         ; keep at least 64 Kb of free RAM
;        jbe     .nomem
;        pop     ecx
;        push    ecx
;        add     ecx, 0x3FF
;        shr     ecx, 10
;        cmp     eax, ecx
;        jae     .memok
        jmp     .memok
.nomem:
        push    ContinueBtn
        push    1
        push    aNoFreeRam_ptr
        push    1
        call    [SayErr]
        jmp     return.clear
.memok:
; allocate streamInfos
        pop     ecx
        call    [pgalloc]
        test    eax, eax
        jz      return.clear
        mov     [.mainbuf], eax
; initialize streamInfos for decoders
        xor     ecx, ecx
        mov     ebp, [ebx+_7z.StreamsInfo.unpackSizes]
        mov     edx, [.pStreams]
.init1:
        mov     [edx], eax
        add     edx, 4
        push    edx
        push    dword [ebp]
        pop     dword [eax]
        push    dword [ebp+4]
        pop     dword [eax+4]
        and     dword [eax+streamInfo.size], 0
        push    eax
        mov     eax, [ebx+_7z.StreamsInfo.fsz+ecx*4]
        call    _7z.GetCoder
        movzx   esi, byte [eax+4]
        lea     esi, [esi+eax+17]
        call    [_7z.GetBufSizeTable+edx*4]
        pop     esi
        push    esi
        add     eax, 3
        and     al, not 3
        add     [esp], eax
        pop     [esi+streamInfo.bufPtr]
        push    [esi+streamInfo.bufPtr]
        cmp     dword [ebp+4], 0
        jnz     @f
        cmp     dword [ebp], edx
        jae     @f
        mov     edx, [ebp]
@@:
        add     edx, 0xF
        and     edx, not 0xF
        mov     [esi+streamInfo.bufSize], edx
        and     [esi+streamInfo.bufDataLen], 0
        pop     eax
        add     eax, edx
        pop     edx
        add     ebp, 8
        inc     ecx
        cmp     ecx, [ebx+_7z.StreamsInfo.numCoders]
        jb      .init1
; initialize streamInfos for input streams
        xor     ecx, ecx
@@:
        mov     [edx], eax
        add     edx, 4
        mov     esi, [_7z.CurInStream]
        inc     [_7z.CurInStream]
        shl     esi, 3
        add     esi, [_7z.StreamsInfo.packSizes]
        push    dword [esi]
        pop     dword [eax]
        push    dword [esi+4]
        pop     dword [eax+4]
        mov     [eax+streamInfo.fillBuf], fillBufFromInStream
        lea     esi, [eax+streamInfo.size+8]
        mov     [eax+streamInfo.bufPtr], esi
        mov     [eax+streamInfo.bufSize], 0x4000 - streamInfo.size - 8
        and     [eax+streamInfo.bufDataLen], 0
        mov     esi, dword [_7z.StreamsInfo.dataOffset]
        mov     dword [eax+streamInfo.size], esi
        mov     esi, dword [_7z.StreamsInfo.dataOffset+4]
        mov     dword [eax+streamInfo.size+4], esi
        mov     esi, [eax]
        add     dword [_7z.StreamsInfo.dataOffset], esi
        mov     esi, [eax+4]
        adc     dword [_7z.StreamsInfo.dataOffset+4], esi
        add     eax, 0x4000
        inc     ecx
        cmp     ecx, [ebx+_7z.StreamsInfo.numPackedStreams]
        jb      @b
; initialize links between streams
        push    -1
        mov     edx, [ebx+_7z.StreamsInfo.mainOutStream]
        xor     ecx, ecx
.init_links:
        mov     eax, [ebx+_7z.StreamsInfo.fsz+edx*4]
        movzx   esi, byte [eax+4]
        cmp     ecx, [eax+esi+5]
        jb      @f
        pop     ecx
        cmp     ecx, -1
        jz      .inited_links
        pop     edx
        jmp     .init_links
@@:
        push    edx
        push    ecx
@@:
        dec     edx
        js      @f
        mov     eax, [ebx+_7z.StreamsInfo.fsz+edx*4]
        movzx   esi, byte [eax+4]
        add     ecx, [eax+esi+5]
        jmp     @b
@@:
        mov     esi, [ebx+_7z.StreamsInfo.bindPairs]
        mov     edx, [ebx+_7z.StreamsInfo.numBindPairs]
@@:
        add     esi, 8
        dec     edx
        js      .link_to_in
        cmp     ecx, [esi-8]
        jnz     @b
        pop     ecx
        pop     edx
        mov     eax, [.pStreams]
        mov     eax, [eax+edx*4]
        lea     eax, [eax+streamInfo.size+ecx*4]
        push    edx
        inc     ecx
        push    ecx
        mov     edx, [esi-4]
        mov     ecx, [.pStreams]
        mov     ecx, [ecx+edx*4]
        mov     [eax], ecx
        cmp     dword [ecx+streamInfo.size], 0
        jnz     return.err
        xor     ecx, ecx
        jmp     .init_links
.link_to_in:
        mov     esi, [ebx+_7z.StreamsInfo.packedStreams]
        mov     edx, [ebx+_7z.StreamsInfo.numPackedStreams]
@@:
        dec     edx
        js      return.err
        cmp     ecx, [esi+edx*4]
        jnz     @b
        add     edx, [ebx+_7z.StreamsInfo.numCoders]
        mov     esi, [.pStreams]
        mov     eax, [esi+edx*4]
        pop     ecx
        pop     edx
        mov     esi, [esi+edx*4]
        mov     [esi+streamInfo.size+ecx*4], eax
        inc     ecx
        jmp     .init_links
.inited_links:
; initialize all decoders
; bool zzz_init_decoder(stream_info* ebp, esi->params, [esi-4]=params_size);
        xor     ecx, ecx
.init_decoders:
        mov     eax, [ebx+_7z.StreamsInfo.fsz+ecx*4]
        call    _7z.GetCoder
        movzx   esi, byte [eax+4]
        mov     ebp, [.pStreams]
        mov     ebp, [ebp+ecx*4]
        cmp     dword [esi+eax], 0
        jz      @f
        cmp     dword [ebp+streamInfo.size], 0
        jz      return.err
@@:
        lea     esi, [esi+eax+17]
        push    ebx ecx edi
        xor     ebx, ebx
        call    [_7z.InitTable+edx*4]
        pop     edi ecx ebx
        inc     ecx
        cmp     ecx, [ebx+_7z.StreamsInfo.numCoders]
        jb      .init_decoders
        ret

fillBuf:
        and     [eax+streamInfo.bufDataLen], 0
fillBufNozero:
        pushad
	mov	edx, [eax+streamInfo.bufSize]
	sub	edx, [eax+streamInfo.bufDataLen]
        cmp     dword [eax+streamInfo.fullSize+4], 0
        jnz     .full
        mov     ecx, dword [eax+streamInfo.fullSize]
        cmp     ecx, edx
        jbe     @f
.full:
        mov     ecx, edx
@@:
        sub     dword [eax+streamInfo.fullSize], ecx
        sbb     dword [eax+streamInfo.fullSize+4], 0
        mov     edi, [eax+streamInfo.bufPtr]
        add     edi, [eax+streamInfo.bufDataLen]
	add	[eax+streamInfo.bufDataLen], ecx
        jmp     [eax+streamInfo.fillBuf]

fillBufFromInStream:
        push    eax
        push    dword [eax+streamInfo.size+4]
        push    dword [eax+streamInfo.size]
        push    0
        push    [inStream]
        call    [seek]
        push    ecx
        push    edi
        push    [inStream]
        call    [read]
        cmp     eax, ecx
        pop     eax
        jnz     .error
        add     dword [eax+streamInfo.size], ecx
        adc     dword [eax+streamInfo.size+4], 0
        popad
        ret
.error:
        push    ContinueBtn
        push    1
        push    aReadError_ptr
        push    1
        call    [SayErr]
        jmp     return.clear

_7z.GetCoder:
; in: eax->coder
; out: edx = coder type
        xor     edx, edx
; Copy decoder: method ID = [size=01] 00, NumInStreams = NumOutStreams = 1
        cmp     word [eax+4], 0x0001
        jnz     @f
        cmp     dword [eax+6], 1
        jz      .ret
@@:
        inc     edx
; LZMA decoder: method ID = [size=03] 03 01 01, NumInStreams = NumOutStreams = 1
        cmp     dword [eax+4], 0x01010303
        jnz     @f
        cmp     dword [eax+8], 1
        jz      .ret
@@:
        inc     edx
; PPMD decoder: method ID = [size=03] 03 04 01, NumInStreams = NumOutStreams = 1
        cmp     dword [eax+4], 0x01040303
        jnz     @f
        cmp     dword [eax+8], 1
        jz      .ret
@@:
        inc     edx
; BCJ filter: method ID = [size=04] 03 03 01 03, NumInStreams = NumOutStreams = 1
        cmp     byte [eax+4], 4
        jnz     @f
        cmp     dword [eax+5], 0x03010303
        jnz     @f
        cmp     dword [eax+9], 1
        jz      .ret
@@:
        inc     edx
; BCJ2 filter: method ID = [size=04] 03 03 01 1B, NumInStreams = 4, NumOutStreams = 1
        cmp     byte [eax+4], 4
        jnz     @f
        cmp     dword [eax+5], 0x1B010303
        jnz     @f
        cmp     dword [eax+9], 4
        jz      .ret
@@:
        inc     edx
; 7z-AES cryptor: method ID = [size=04] 06 F1 07 01, NumInStreams = NumOutStreams = 1
        cmp     byte [eax+4], 4
        jnz     @f
        cmp     dword [eax+5], 0x0107F106
        jnz     @f
        cmp     dword [eax+9], 1
        jz      .ret
@@:
        inc     edx
; Deflate decoder: method ID = [size=03] 04 01 08, NumInStreams = NumOutStreams = 1
        cmp     dword [eax+4], 0x08010403
        jnz     @f
        cmp     dword [eax+8], 1
        jz      .ret
@@:
        inc     edx
; Deflate64 decoder: method ID = [size=03] 04 01 09, NumInStreams = NumOutStreams = 1
        cmp     dword [eax+4], 0x09010403
        jnz     @f
        cmp     dword [eax+8], 1
        jz      .ret
@@:
        stc
.ret:
        ret

iglobal
align 4
label _7z.GetBufSizeTable dword
        dd      copy_get_buf_size
        dd      lzma_get_buf_size
        dd      ppmd_get_buf_size
        dd      bcj_get_buf_size
        dd      bcj2_get_buf_size
        dd      aes7z_get_buf_size
        dd      deflate_get_buf_size
        dd      deflate_get_buf_size
label _7z.InitTable dword
        dd      copy_init_decoder
        dd      lzma_init_decoder
        dd      ppmd_init_decoder
        dd      bcj_init_decoder
        dd      bcj2_init_decoder
        dd      aes7z_init_decoder
        dd      deflate_init_decoder
        dd      deflate64_init_decoder
endg

copy_get_buf_size:
        mov     eax, streamInfo.size+8
        mov     edx, 0x10000
        ret

copy_init_decoder:
        mov     [ebp+streamInfo.fillBuf], copy_fillBuf
        and     dword [ebp+streamInfo.size+4], 0
        ret

copy_fillBuf:
        push    eax
        mov     esi, [eax+streamInfo.size+4]
        mov     eax, [eax+streamInfo.size]
.redo:
        push    ecx
        mov     edx, [eax+streamInfo.bufDataLen]
        sub     edx, esi
        cmp     ecx, edx
        jb      @f
        mov     ecx, edx
@@:
        sub     [esp], ecx
        add     esi, [eax+streamInfo.bufPtr]
        mov     edx, ecx
        shr     ecx, 2
        rep     movsd
        mov     ecx, edx
        and     ecx, 3
        rep     movsb
        pop     ecx
        jecxz   .done
        cmp     dword [eax+streamInfo.fullSize+4], 0
        jnz     @f
        cmp     dword [eax+streamInfo.fullSize], ecx
        jb      return.err
@@:
        call    fillBuf
        xor     esi, esi
        jmp     .redo
.done:
        sub     esi, [eax+streamInfo.bufPtr]
        pop     eax
        mov     [eax+streamInfo.size+4], esi
        popad
        ret

; ebp=hPlugin, eax->item, edi->info
getattr_7z:
        cmp     [eax+file_in_7z.bPseudoFolder], 0
        jnz     .pseudo
        lea     esi, [eax+file_in_7z.attr]
        movsd
        xor     eax, eax
        stosd
        add     esi, -(file_in_7z.attr+4) + file_in_7z.CreationTime
        mov     ecx, 8
        rep     movsd
        ret
.pseudo:
        push    0x10    ; attributes: folder
        pop     eax
        stosd
        xor     eax, eax
        stosd
        lea     esi, [ebp+handle_7z.host_datetime]
        push    6
        pop     ecx
        rep     movsd
        stosd
        stosd
        ret

virtual at 0
file_handle_7z:
.type   dd      ?
.context dd     ?
.pos    dq      ?
.base   dd      ?       ; handle of archive
.item   dd      ?       ; pointer to file_in_7z
.mainStream dd  ?       ; (pointer in context)
.bError db      ?
        rb      3
.size = $
end virtual

iglobal
align 4
handle_table_7z dd      handle_table_7z,handle_table_7z
endg

; ebp=hPlugin, eax->item, edi=mode
open_file_7z:
        cmp     [eax+file_in_7z.bHasStream], 0
        jnz     @f
        mov     eax, empty_7z_handle
        ret
@@:
        mov     ecx, [ebp+handle_7z.password_len]
        inc     ecx
        setnz   [bPasswordDefined]
        jz      @f
        dec     ecx
        lea     esi, [ebp+handle_7z.password]
        push    edi
        mov     edi, password_unicode
        shr     ecx, 1
        rep     movsd
        adc     ecx, ecx
        rep     movsw
        pop     edi
@@:
        mov     [hOut], ecx
        mov     [_7z.decode.mainbuf], ecx
        mov     ecx, [ebp+handle_7z.host]
        mov     [inStream], ecx
        mov     [_7z.decode.pStreams], _7z.decode.pStreams_def
        push    eax
        mov     [_esp], esp
        mov     [_ebp], ebp
        mov     [error_proc], .error
        mov     [clear_proc], .clear
        mov     ebx, [ebp+handle_7z.folders]
        mov     ecx, [eax+file_in_7z.folder]
        cmp     [ebp+handle_7z.last_folder], ecx
        jnz     .nolast
        mov     edx, dword [eax+file_in_7z.folderStart]
        sub     edx, dword [ebp+handle_7z.last_pos]
        mov     esi, dword [eax+file_in_7z.folderStart+4]
        sbb     esi, dword [ebp+handle_7z.last_pos+4]
        jb      .nolast
        xor     ecx, ecx
        xchg    ecx, [ebp+handle_7z.last_context]
        mov     [_7z.decode.mainbuf], ecx
        mov     eax, [ebp+handle_7z.last_main_stream]
        mov     dword [eax+streamInfo.fullSize], edx
        mov     dword [eax+streamInfo.fullSize+4], esi
        or      [ebp+handle_7z.last_folder], -1
        pop     esi
        push    esi
        jmp     .commonl
.nolast:
        mov     ebx, [ebx+ecx*4]
        mov     eax, [ebx+_7z.StreamsInfo.startPackedStream]
        mov     [_7z.CurInStream], eax
        mov     eax, dword [ebx+_7z.StreamsInfo.packOffset]
        mov     dword [_7z.StreamsInfo.dataOffset], eax
        mov     eax, dword [ebx+_7z.StreamsInfo.packOffset+4]
        mov     dword [_7z.StreamsInfo.dataOffset+4], eax
        mov     eax, [ebp+handle_7z.packSizes]
        mov     [_7z.StreamsInfo.packSizes], eax
        push    ebp
        call    _7z.decode.init
        pop     ebp
        mov     eax, [ebx+_7z.StreamsInfo.mainOutStream]
        mov     esi, [_7z.decode.pStreams]
        mov     eax, [esi+eax*4]
        pop     esi
        push    esi
        mov     ecx, dword [esi+file_in_7z.folderStart]
        mov     dword [eax+streamInfo.fullSize], ecx
        mov     ecx, dword [esi+file_in_7z.folderStart+4]
        mov     dword [eax+streamInfo.fullSize+4], ecx
.commonl:
        call    skip_7z
        mov     ecx, dword [esi+file_in_7z.UnPackSize]
        mov     dword [eax+streamInfo.fullSize], ecx
        mov     edx, dword [esi+file_in_7z.UnPackSize+4]
        mov     dword [eax+streamInfo.fullSize+4], edx
        test    edx, edx
        jnz     .nomemstream
if defined LIMIT_FOR_MEM_STREAM
        cmp     ecx, LIMIT_FOR_MEM_STREAM
        ja      .nomemstream
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      .nomemstream
; create memory stream and unpack to memory
        push    eax ecx
        add     ecx, mem_stream.buf
        call    [pgalloc]
        test    eax, eax
        jz      return.clear
        mov     edi, eax
        pop     ecx
        push    edi
        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
        mov     eax, [esp+4]
        push    esi
        push    ecx edi
        call    read_7z_to_buf
        pop     esi ecx
        mov     ebx, [esp+12]
        cmp     [ebx+file_in_7z.bCRCDefined], 0
        jz      @f
        call    crc
        cmp     eax, [ebx+file_in_7z.FileCRC]
        jnz     return.err
@@:
        pop     esi
        xor     ecx, ecx
        xchg    ecx, [_7z.decode.mainbuf]
        xchg    ecx, [ebp+handle_7z.last_context]
        jecxz   @f
        call    [pgfree]
@@:
        and     [hOut], 0
        mov     eax, [esi+file_in_7z.folder]
        mov     [ebp+handle_7z.last_folder], eax
        mov     eax, [esp+4]
        mov     [ebp+handle_7z.last_main_stream], eax
        mov     ecx, dword [esi+file_in_7z.UnPackSize]
        add     ecx, dword [esi+file_in_7z.folderStart]
        mov     dword [ebp+handle_7z.last_pos], ecx
        mov     ecx, dword [esi+file_in_7z.UnPackSize+4]
        adc     ecx, dword [esi+file_in_7z.folderStart+4]
        mov     dword [ebp+handle_7z.last_pos+4], ecx
        call    .done
        pop     eax edi ecx
        ret
.nomemstream:
        mov     edi, eax
        push    esi
        mov     esi, handle_table_7z
        push    file_handle_7z.size
        pop     ecx
        call    alloc_handle
        pop     esi
        test    eax, eax
        jz      .clear
        mov     [eax+file_handle_7z.type], type_7z
        xor     edx, edx
        mov     dword [eax+file_handle_7z.pos], edx
        mov     dword [eax+file_handle_7z.pos+4], edx
        mov     [eax+file_handle_7z.bError], dl
        xchg    edx, [_7z.decode.mainbuf]
        mov     [eax+file_handle_7z.context], edx
        mov     [eax+file_handle_7z.mainStream], edi
        mov     [eax+file_handle_7z.base], ebp
        mov     [eax+file_handle_7z.item], esi
        push    eax
        call    .done
        pop     eax ecx
        ret

.done:
        cmp     [bPasswordDefined], 0
        jz      @f
        mov     ecx, [password_size]
        mov     [ebp+handle_7z.password_len], ecx
        mov     esi, password_unicode
        lea     edi, [ebp+handle_7z.password]
        shr     ecx, 1
        rep     movsd
        adc     ecx, ecx
        rep     movsw
@@:
        jmp     .clear

.error:
        cmp     [bPasswordDefined], 0
        jz      .realerror
        push    CancelPassBtn
        push    2
        push    aArchiveDataErrorPass_ptr
        push    1
        call    [SayErr]
        cmp     al, 1
        jnz     .clear
; user wants to re-enter password
        call    .clear
        pop     eax
        jmp     open_file_7z
.realerror:
        push    ContinueBtn
        push    1
        push    aArchiveDataError_ptr
        push    1
        call    [SayErr]
.clear:
        mov     ecx, [_7z.decode.pStreams]
        cmp     ecx, _7z.decode.pStreams_def
        jz      @f
        call    [pgfree]
@@:
        mov     ecx, [_7z.decode.mainbuf]
        jecxz   @f
        call    [pgfree]
@@:
        mov     ecx, [hOut]
        jecxz   @f
        call    [pgfree]
@@:
        cmp     esp, [_esp]
        jnz     @f
        pop     eax
@@:
        xor     eax, eax
        ret

read_7z_to_buf:
        mov     esi, [eax+streamInfo.bufDataLen]
        cmp     esi, [eax+streamInfo.bufSize]
        jnz     @f
        xor     esi, esi
        mov     [eax+streamInfo.bufDataLen], esi
@@:
        call    fillBufNozero
        mov     ecx, [eax+streamInfo.bufDataLen]
        sub     ecx, esi
        add     esi, [eax+streamInfo.bufPtr]
        mov     edx, ecx
        shr     ecx, 2
        rep     movsd
        mov     ecx, edx
        and     ecx, 3
        rep     movsb
        cmp     dword [eax+streamInfo.fullSize], 0
        jnz     read_7z_to_buf
        ret

skip_7z:
        push    edx
        mov     edx, [eax+streamInfo.bufDataLen]
        cmp     edx, [eax+streamInfo.bufSize]
        jnz     @f
        and     [eax+streamInfo.bufDataLen], 0
@@:
        pop     edx
        call    fillBufNozero
        cmp     dword [eax+streamInfo.fullSize], 0
        jnz     skip_7z
        cmp     dword [eax+streamInfo.fullSize+4], 0
        jnz     skip_7z
        ret

; unsigned __stdcall read(HANDLE hFile, void* buf, unsigned size);
read_7z:
        mov     eax, [ebx+file_handle_7z.mainStream]
        test    eax, eax
        jz      .ret    ; empty stream - return 0 bytes read
        cmp     [ebx+file_handle_7z.bError], 0
        jnz     .reterr
        mov     ecx, [ebx+file_handle_7z.base]
        mov     ecx, [ecx+handle_7z.host]
        mov     [inStream], ecx
        mov     edi, [esp+8]
        mov     ecx, [esp+12]
        mov     dword [eax+streamInfo.fullSize], ecx
        and     dword [eax+streamInfo.fullSize+4], 0
        jecxz   .nodata
        mov     [_esp], esp
        mov     [_ebp], ebp
        mov     [error_proc], .error
        mov     [clear_proc], .clear    ; should not be called
        mov     esi, [ebx+file_handle_7z.item]
        mov     edx, dword [esi+file_in_7z.UnPackSize]
        mov     esi, dword [esi+file_in_7z.UnPackSize+4]
        sub     edx, dword [ebx+file_handle_7z.pos]
        sbb     esi, dword [ebx+file_handle_7z.pos+4]
        jnz     .do
        cmp     edx, ecx
        jae     .do
        mov     dword [eax+streamInfo.fullSize], edx
.do:
        call    read_7z_to_buf
.nodata:
        sub     edi, [esp+8]
        mov     eax, edi
        add     dword [ebx+file_handle_7z.pos], eax
        adc     dword [ebx+file_handle_7z.pos+4], 0
.ret:
        ret     12

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

; void __stdcall setpos(HANDLE hFile, __int64 pos);
setpos_7z:
        cmp     [ebx+file_handle_7z.context], 0
        jz      .ret
        mov     edi, [ebx+file_handle_7z.base]
        mov     ecx, [edi+handle_7z.host]
        mov     [inStream], ecx
        mov     [_esp], esp
        mov     [_ebp], ebp
        mov     [error_proc], read_7z.error
        mov     [clear_proc], read_7z.clear     ; should not be called
        cmp     [ebx+file_handle_7z.bError], 0
        jnz     .backward       ; if was error, force reinitialization
        mov     ecx, [esp+8]
        mov     edx, [esp+12]
        sub     ecx, dword [ebx+file_handle_7z.pos]
        sbb     edx, dword [ebx+file_handle_7z.pos+4]
        jb      .backward
; move forward - skip some data
        mov     eax, [ebx+file_handle_7z.mainStream]
        mov     dword [eax+streamInfo.fullSize], ecx
        mov     dword [eax+streamInfo.fullSize+4], edx
        call    skip_7z
        add     dword [ebx+file_handle_7z.pos], ecx
        adc     dword [ebx+file_handle_7z.pos], edx
.ret:
        ret     12
.backward:
; move backward - reinitialize and skip start data
        mov     [ebx+file_handle_7z.bError], 0
        mov     ebp, [ebx+file_handle_7z.context]
        mov     eax, [ebx+file_handle_7z.item]
        mov     ebx, [edi+handle_7z.folders]
        mov     eax, [eax+file_in_7z.folder]
        mov     ebx, [ebx+eax*4]
; initialize streamInfos for decoders
        xor     ecx, ecx
        mov     esi, [ebx+_7z.StreamsInfo.unpackSizes]
@@:
        lodsd
        mov     [ebp], eax
        lodsd
        mov     [ebp+4], eax
        and     [ebp+streamInfo.bufDataLen], 0
        push    esi
        mov     eax, [ebx+_7z.StreamsInfo.fsz+ecx*4]
        call    _7z.GetCoder
        movzx   esi, byte [eax+4]
        lea     esi, [esi+eax+17]
        push    ebx ecx edi
        mov     bl, 1
        call    [_7z.InitTable+edx*4]
        pop     edi ecx ebx esi
        mov     edx, [ebp+streamInfo.bufSize]
        mov     ebp, [ebp+streamInfo.bufPtr]
        add     ebp, edx
        inc     ecx
        cmp     ecx, [ebx+_7z.StreamsInfo.numCoders]
        jb      @b
; initialize streamInfos for input streams
        xor     ecx, ecx
        mov     esi, [ebx+_7z.StreamsInfo.startPackedStream]
        shl     esi, 3
        add     esi, [edi+handle_7z.packSizes]
        mov     edi, dword [ebx+_7z.StreamsInfo.packOffset]
        mov     edx, dword [ebx+_7z.StreamsInfo.packOffset+4]
@@:
        mov     dword [ebp+streamInfo.size], edi
        mov     dword [ebp+streamInfo.size+4], edx
        and     [ebp+streamInfo.bufDataLen], 0
        lodsd
        add     edi, eax
        mov     [ebp], eax
        lodsd
        adc     edx, eax
        mov     [ebp+4], eax
        add     ebp, 0x4000
        inc     ecx
        cmp     ecx, [ebx+_7z.StreamsInfo.numPackedStreams]
        jb      @b
        mov     ebx, [esp+4]
        mov     esi, [ebx+file_handle_7z.item]
        mov     eax, [ebx+file_handle_7z.mainStream]
        mov     ecx, dword [esi+file_in_7z.folderStart]
        add     ecx, [esp+8]
        mov     dword [eax+streamInfo.fullSize], ecx
        mov     ecx, dword [esi+file_in_7z.folderStart+4]
        adc     ecx, [esp+12]
        mov     dword [eax+streamInfo.fullSize+4], ecx
        and     dword [ebx+file_handle_7z.pos], 0
        and     dword [ebx+file_handle_7z.pos+4], 0
        and     [eax+streamInfo.bufDataLen], 0
        call    skip_7z
        mov     eax, [esp+8]
        mov     dword [ebx+file_handle_7z.pos], eax
        mov     eax, [esp+12]
        mov     dword [ebx+file_handle_7z.pos+4], eax
        ret     12

close_file_7z:
        mov     ecx, [ebx+file_handle_7z.context]
        jecxz   .ret
        cmp     [ebx+file_handle_7z.bError], 0
        jnz     @f
        push    ebp
        mov     ebp, [ebx+file_handle_7z.base]
        xchg    ecx, [ebp+handle_7z.last_context]
        mov     eax, [ebx+file_handle_7z.item]
        mov     edx, [eax+file_in_7z.folder]
        mov     [ebp+handle_7z.last_folder], edx
        mov     edx, [ebx+file_handle_7z.mainStream]
        mov     [ebp+handle_7z.last_main_stream], edx
        mov     edx, dword [eax+file_in_7z.folderStart+4]
        mov     eax, dword [eax+file_in_7z.folderStart]
        add     eax, dword [ebx+file_handle_7z.pos]
        adc     edx, dword [ebx+file_handle_7z.pos+4]
        mov     dword [ebp+handle_7z.last_pos], eax
        mov     dword [ebp+handle_7z.last_pos+4], edx
        pop     ebp
@@:
        jecxz   @f
        call    [pgfree]
@@:
        mov     esi, ebx
        call    free_handle
.ret:
        ret     4
