delete_file_worker:
; in: ecx=flags: 1=deleting directory
; out: eax=0 - OK, eax=1 - retry, eax=2 - skip, eax=-1 - cancel,
;      PF, ZF, CF and SF set accordingly to 'cmp eax,2' (or 'cmp al,2')
        push    ebx
        push    70
        pop     eax
        mov     ebx, delinfo
        int     0x40
        pop     ebx
        test    eax, eax
        jz      .ret
        cmp     [del_bSkipAll], 0
        jz      @f
        push    2
        pop     eax
        jmp     .ret
@@:
        push    execdata
        push    aCannotDeleteFolder
        test    cl, 1
        jnz     @f
        mov     dword [esp], aCannotDeleteFile
@@:
        call    get_error_msg
        push    eax
        mov     eax, esp
        push    DeleteErrorBtn
        push    4
        push    eax
        push    3
        call    SayErr
        add     esp, 3*4
        cmp     al, -1
        jz      @f
        inc     eax
        cmp     al, 4   ; "cancel" button
        jnz     @f
        or      eax, -1
@@:
        cmp     al, 3   ; "skip all" button
        jnz     .ret
        mov     [del_bSkipAll], 1
        dec     eax
.ret:
        cmp     al, 2
        ret

delete_file:
; in: eax->BDFE block
; out: CF and ZF not set <=> cancel job ("ja cancel_label")
        pushad
        mov     [del_dir_stack_ptr], del_dir_stack
        lea     esi, [ebp + panel1_dir - panel1_data]
        mov     edi, execdata
@@:
        lodsb
        test    al, al
        jz      @f
        stosb
        jmp     @b
@@:
        mov     esi, [esp+28]
        mov     ecx, esi
        add     esi, 40
        cmp     byte [edi-1], '/'
        jz      .l1
        mov     al, '/'
        stosb
.l1:
        lodsb
        cmp     edi, execdataend
        jb      @f
        call    panels_OnKey.bigfilename
        popad
        ret
@@:
        stosb
        test    al, al
        jnz     .l1
        mov     ecx, [esp+28]
        test    byte [ecx], 10h
        jnz     .delete_dir
.retrydel:
        xor     ecx, ecx
        call    delete_file_worker
        jae     @f
        jp      .retrydel
@@:
        popad
        ret

.delete_dir:
; recursive delete of directory
        xor     ebp, ebp        ; ebp will contain number of undeletable items
.return_from_recursion:
        mov     ebx, dirinfo
        mov     [ebx+dirinfo.first-dirinfo], ebp
        mov     [ebx+dirinfo.size-dirinfo], del_dir_query_size
        mov     [ebx+dirinfo.dirdata-dirinfo], del_dir_query_area
        mov     [ebx+dirinfo.name-dirinfo], execdata
        push    70
        pop     eax
        int     0x40
; if we get read error, the best available action is try to delete directory itself
        test    eax, eax
        jz      @f
        cmp     eax, 6
        jnz     .do_delete_dir
@@:
; loop through a directory and delete items
        mov     edx, del_dir_query_area+32
        imul    ebx, 304
        add     ebx, edx
.delete_dir_entry_loop:
        cmp     edx, ebx
        jb      .do_delete_dir_entry
        cmp     ebx, del_dir_query_area+32+304*del_dir_query_size
        jnz     .delete_dir_entry_done
        jmp     .return_from_recursion
.do_delete_dir_entry:
; ignore special entries "." and ".."
        inc     ebp
        cmp     word [edx+40], '.'
        jz      .delete_dir_entry_continue
        cmp     word [edx+40], '..'
        jnz     @f
        cmp     byte [edx+42], 0
        jz      .delete_dir_entry_continue
@@:
        dec     ebp
        mov     esi, execdata
@@:
        lodsb
        test    al, al
        jnz     @b
        mov     byte [esi-1], '/'
        mov     edi, esi
        lea     esi, [edx+40]
@@:
        cmp     edi, execdataend
        jae     .fullname_big
        lodsb
        stosb
        test    al, al
        jnz     @b
        test    byte [edx], 10h
        jnz     .entry_is_folder
.retry2:
        xor     ecx, ecx
        call    delete_file_worker
        ja      .cancel
        jz      .skip
        jp      .retry2
        jmp     .restore_name
.entry_is_folder:
; allocate new item in directory stack
        mov     eax, [del_dir_stack_ptr]
        mov     [eax], ebp
        add     eax, 4
        mov     [del_dir_stack_ptr], eax
; do recursive deleting
        jmp     .delete_dir
.fullname_big:
; we will just ignore such files and continue - in real life this situation can not happen
        inc     ebp
        mov     esi, execdataend-1
        jmp     .do_restore_name
.skip:
        inc     ebp
.restore_name:
        mov     esi, execdata
@@:
        lodsb
        test    al, al
        jnz     @b
        dec     esi
        dec     esi
.do_restore_name:
        call    delete_last_name
.delete_dir_entry_continue:
        add     edx, 304
        jmp     .delete_dir_entry_loop
.delete_dir_entry_done:
.do_delete_dir:
        mov     cl, 1
        call    delete_file_worker
        ja      .cancel
        jz      @f
        jp      .delete_dir
@@:
; al=0 - OK, al=2 - skip this directory
; return to previous directory
; pop item from directory stack
        mov     ecx, [del_dir_stack_ptr]
        cmp     ecx, del_dir_stack
        jbe     .done
        sub     ecx, 4
        mov     [del_dir_stack_ptr], ecx
        mov     ebp, [ecx]
        cmp     al, 2
        sbb     ebp, -1
; restore prev directory name
        mov     esi, execdata
        call    delete_last_name_from_end
        jmp     .return_from_recursion
.done:
.cancel:
        mov     [dirinfo.first], 0      ; do not destroys flags
        popad
        ret

makedir:
; create directory with name from CopyDestEditBuf+12
; destroys eax
        push    ebx
        push    70
        pop     eax
        mov     ebx, mkdirinfo
        int     0x40
        pop     ebx
        test    eax, eax
        jz      .ret
        cmp     dword [esp+8], DeleteErrorBtn
        jnz     @f
        cmp     [copy_bSkipAll], 0
        jz      @f
        push    1
        pop     eax
        jmp     .ret
@@:
        push    dword CopyDestEditBuf+12
        push    dword aCannotMakeFolder
        call    get_error_msg
        push    eax
        mov     eax, esp
        push    dword [eax+20]
        push    dword [eax+16]
        push    eax
        push    3
        call    SayErr
        add     esp, 3*4
        test    eax, eax
        jz      makedir
.ret:
        ret     8

copy_file_worker:
; in: execdata = source name, CopyDestEditBuf+12 = destination name, edx = BDFE block for source
; out: CF and ZF not set <=> cancel job ("ja cancel_label")
; destroys eax,esi,edi
        lea     edi, [edx+40]
        and     [source_hFile], 0
copy_file_worker2:
        push    CopyDestEditBuf+12+513
        cmp     [bDestIsFolder], 0
        jz      .noaddtoname
        mov     esi, CopyDestEditBuf+12
@@:
        lodsb
        test    al, al
        jnz     @b
        pop     eax
        dec     esi
        push    esi
        cmp     byte [esi-1], '/'
        jz      @f
        mov     byte [esi], '/'
        inc     esi
@@:
        xchg    esi, edi
@@:
        cmp     edi, CopyDestEditBuf+12+513
        jae     .overflow
        lodsb
        stosb
        test    al, al
        jnz     @b
        jmp     .noaddtoname
.overflow:
.ret_zf:
        pop     esi
        and     byte [esi], 0           ; ZF=1
        ret
.noaddtoname:
; Нельзя скопировать файл поверх самого себя!
        mov     esi, execdata
        mov     edi, CopyDestEditBuf+12
        cmp     [source_hModule], 0
        jnz     @f
        push    esi edi
        call    strcmpi
        pop     edi esi
        jnz     @f
        push    esi
        push    aCannotCopyToSelf
        mov     eax, esp
        push    ContinueBtn
        push    1
        push    eax
        push    2
        call    SayErr
        pop     eax
        pop     eax
        jmp     .ret_zf
@@:
; Собственно, копируем
; esi->source name, edi->destination name
        push    ebx
        mov     [writeinfo.code], 2
        mov     [writeinfo.name], edi
        and     dword [writeinfo.first], 0
        and     dword [writeinfo.first+4], 0
        mov     [writeinfo.data], copy_buffer
        mov     ebx, readinfo
        and     dword [ebx+readinfo.first-readinfo], 0
        and     dword [ebx+readinfo.first+4-readinfo], 0
        mov     [ebx+readinfo.size-readinfo], copy_buffer_size
        mov     [ebx+readinfo.data-readinfo], copy_buffer
        mov     [ebx+readinfo.name-readinfo], esi
        mov     eax, [source_hFile]
        push    eax
        test    eax, eax
        jnz     .copyloop
.source_reopen:
        mov     eax, [source_hModule]
        test    eax, eax
        jz      .copyloop
        pushad
        push    O_READ+O_SEQUENTIAL_ONLY
        push    esi
        push    [source_hPlugin]
        call    [eax+PluginInfo.open]
        mov     [source_hFile], eax
        popad
.copyloop:
        mov     ebx, readinfo
        mov     eax, [source_hModule]
        test    eax, eax
        jz      .native
        mov     ecx, [source_hFile]
        jecxz   .readerr
        pushad
        push    [ebx+readinfo.size-readinfo]
        push    [ebx+readinfo.data-readinfo]
        push    ecx
        call    [eax+PluginInfo.read]
        mov     [esp+28], eax
        popad
        cmp     eax, -1
        jz      .readerr
        mov     ebx, eax
        jmp     .copyreadok
.native:
        push    70
        pop     eax
        int     0x40
        test    eax, eax
        jz      .copyreadok
        cmp     eax, 6
        jz      .copyreadok
.readerr:
        cmp     [copy_bSkipAll2], 0
        jnz     .copyfailed_del2
        push    esi
        push    dword aCannotReadFile
        call    get_error_msg
        push    eax
        mov     eax, esp
        push    dword DeleteErrorBtn
        push    4
        push    eax
        push    3
        call    SayErr
        add     esp, 3*4
        test    eax, eax
        jnz     .copyfailed_parseuser
        cmp     [source_hModule], 0
        jz      .copyloop
        cmp     [source_hFile], 0
        jz      .source_reopen
        jmp     .copyloop
.copyreadok:
        add     dword [readinfo.first], ebx
        adc     dword [readinfo.first+4], 0
        mov     [writeinfo.size], ebx
        test    ebx, ebx
        jnz     .copywrite
        cmp     byte [writeinfo.code], 2
        jnz     .copydone
.copywrite:
        mov     ebx, writeinfo
        push    70
        pop     eax
        int     0x40
        test    eax, eax
        jz      .copywriteok
        cmp     [copy_bSkipAll2], 0
        jnz     .copyfailed_del2
        push    edi
        push    dword aCannotWriteFile
        call    get_error_msg
        push    eax
        mov     eax, esp
        push    dword DeleteErrorBtn
        push    4
        push    eax
        push    3
        call    SayErr
        add     esp, 3*4
        test    eax, eax
        jz      .copywrite
.copyfailed_parseuser:
        cmp     al, 2
        jnz     @f
        mov     [copy_bSkipAll2], 1
        dec     eax
@@:
        cmp     al, 1
        pushf
        jmp     .copyfailed
.copywriteok:
        mov     ecx, [writeinfo.size]
        add     dword [writeinfo.first], ecx
        adc     dword [writeinfo.first+4], 0
        mov     [writeinfo.code], 3
        cmp     ecx, copy_buffer_size
        jz      .copyloop
.copydone:
        pop     ecx
        test    ecx, ecx
        jnz     @f
        mov     eax, [source_hModule]
        test    eax, eax
        jz      @f
        mov     ecx, [source_hFile]
        jecxz   @f
        push    edx
        push    ecx
        call    [eax+PluginInfo.close]
        pop     edx
@@:
; now try to set attributes from source, ignore errors
        mov     edi, attrinfo.attr
        mov     esi, edx
        push    8
        pop     ecx
        rep     movsd
; replace zero dates with default values
        mov     eax, [default_attr]
        cmp     dword [edi-32+8], 0
        jnz     @f
        mov     ecx, [eax+8]
        mov     [edi-32+8], ecx
        mov     ecx, [eax+12]
        mov     [edi-32+12], ecx
@@:
        cmp     dword [edi-32+16], 0
        jnz     @f
        mov     ecx, [eax+16]
        mov     [edi-32+16], ecx
        mov     ecx, [eax+20]
        mov     [edi-32+20], ecx
@@:
        cmp     dword [edi-32+24], 0
        jnz     @f
        mov     ecx, [eax+24]
        mov     [edi-32+24], ecx
        mov     ecx, [eax+28]
        mov     [edi-32+28], ecx
@@:
        mov     ebx, attrinfo
        mov     [ebx+attrinfo.name-attrinfo], CopyDestEditBuf+12
        inc     dword [ebx]
        push    70
        pop     eax
        push    ebx
        int     0x40
        pop     ebx
        dec     dword [ebx]
        xor     eax, eax        ; ZF=1
.ret:
        pop     ebx
        pop     esi
        mov     byte [esi], 0
        ret
.copydone2:
        popf
        jmp     .ret
.copyfailed:
        pop     ecx
        test    ecx, ecx
        jnz     @f
        mov     eax, [source_hModule]
        test    eax, eax
        jz      @f
        mov     ecx, [source_hFile]
        jecxz   @f
        push    ecx
        call    [eax+PluginInfo.close]
@@:
        cmp     [bConfirmDeleteIncomplete], 0
        jz      .copyfailed_del
        cmp     [writeinfo.code], 2
        jz      .copydone2
        push    dword aIncompleteFile
        mov     eax, esp
        push    dword DeleteOrKeepBtn
        push    2
        push    eax
        push    1
        push    dword aCopyCaption
        call    Message
        add     esp, 4
        test    eax, eax
        jnz     .copydone2
.copyfailed_del:
        mov     ebx, delinfo
        push    dword [ebx+21]
        mov     dword [ebx+21], edi
        push    70
        pop     eax
        int     0x40
; ignore errors
        pop     dword [delinfo+21]
        jmp     .copydone2
.copyfailed_del2:
        xor     eax, eax
        pushf
        jmp     .copyfailed_del

copy_file:
; in: eax->BDFE block for source, CopyDestEditBuf+12 contains ASCIIZ full name for destination
; out: CF and ZF not set <=> cancel job ("ja cancel_label")
        pushad
        mov     [copy_dir_stack_ptr], copy_dir_stack
        mov     [bNeedRestoreName], 0
        lea     esi, [ebp + panel1_dir - panel1_data]
        mov     edi, execdata
@@:
        lodsb
        test    al, al
        jz      @f
        stosb
        jmp     @b
@@:
        mov     esi, [esp+28]
        add     esi, 40
        cmp     byte [edi-1], '/'
        jz      .l1
        mov     al, '/'
        stosb
.l1:
        lodsb
        cmp     edi, execdataend
        jb      @f
        call    panels_OnKey.bigfilename
        stc
        popad
        ret
@@:
        stosb
        test    al, al
        jnz     .l1
        mov     edx, [esp+28]
        test    byte [edx], 10h
        jnz     .copy_dir
        call    copy_file_worker
.popad_ret:
        popad
        ret

.biiig:
        mov     byte [edi-1], 0
        jmp     .popad_ret

.copy_dir:
; recursive copy of directory
        cmp     [bDestIsFolder], 0
        mov     [bDestIsFolder], 0
        jz      .target_created
        mov     [bNeedRestoreName], 1
        mov     esi, CopyDestEditBuf+12
@@:
        lodsb
        test    al, al
        jnz     @b
        dec     esi
        cmp     byte [esi-1], '/'
        jz      @f
        mov     byte [esi], '/'
        inc     esi
@@:
        mov     edi, esi
        lea     esi, [edx+40]
@@:
        cmp     edi, CopyDestEditBuf+12+513
        jae     .biiig
        lodsb
        stosb
        test    al, al
        jnz     @b
.create_target:
.enter_recursion:
        push    DeleteErrorBtn
        push    4
        call    makedir
        jz      .target_created
        cmp     al, 2
        jnz     @f
        dec     eax
        mov     [copy_bSkipAll], 1
@@:
        cmp     al, 1
        jz      .copy_dir_entry_done
        jmp     .cancel2
.target_created:
        mov     edx, [source_hModule]
        test    edx, edx
        jz      .nosetdir
        mov     esi, execdata
        push    esi     ; absolute_path
@@:
        lodsb
        test    al, al
        jnz     @b
@@:
        dec     esi
        cmp     byte [esi-1], '/'
        jnz     @b
        push    esi     ; relative_path
        push    [source_hPlugin]        ; hPlugin
        call    [edx+PluginInfo.SetFolder]
        test    al, al
        jnz     .nosetdir
        cmp     [copy_bSkipAll3], 0
        jz      .skip2
        push    execdata
        push    aCannotSetFolder
        mov     eax, esp
        push    DeleteErrorBtn
        push    4
        push    eax
        push    2
        call    SayErr
        pop     ecx ecx
        test    al, al
        jz      .target_created
        cmp     al, 2
        setz    [copy_bSkipAll3]
        ja      .cancel2
        jmp     .skip2
.nosetdir:
        xor     ebp, ebp        ; ebp will contain number of copied items
.return_from_recursion:
.read_retry:
        mov     ebx, dirinfo
        mov     [ebx+dirinfo.first-dirinfo], ebp
        mov     [ebx+dirinfo.size-dirinfo], copy_dir_query_size
        mov     [ebx+dirinfo.dirdata-dirinfo], copy_dir_query_area
        mov     [ebx+dirinfo.name-dirinfo], execdata
        mov     eax, [source_hModule]
        test    eax, eax
        jz      .readfolder_native
        push    ebp
        push    [ebx+dirinfo.dirdata-dirinfo]
        push    [ebx+dirinfo.size-dirinfo]
        push    [ebx+dirinfo.first-dirinfo]
        push    [source_hPlugin]
        call    [eax+PluginInfo.ReadFolder]
        pop     ebp
        mov     ebx, dword [copy_dir_query_area+4]
        jmp     @f
.readfolder_native:
        push    70
        pop     eax
        int     0x40
@@:
        test    eax, eax
        jz      .readok
        cmp     eax, 6
        jz      .readok
; read error
        cmp     [copy_bSkipAll], 0
        jnz     .skip1
        push    execdata
        push    aCannotReadFolder
        call    get_error_msg
        push    eax
        mov     eax, esp
        push    DeleteErrorBtn
        push    4
        push    eax
        push    3
        call    SayErr
        add     esp, 3*4
        test    al, al
        jz      .read_retry
        cmp     al, 2
        jnz     @f
        dec     eax
        mov     [copy_bSkipAll], 1
@@:
        cmp     al, 1
        jz      .skip1
        jmp     .cancel
.readok:
; loop through a directory and copy items
        mov     edx, copy_dir_query_area+32
        imul    ebx, 304
        add     ebx, edx
.copy_dir_entry_loop:
        cmp     edx, ebx
        jb      .do_copy_dir_entry
        cmp     ebx, copy_dir_query_area+32+copy_dir_query_size*304
        jz      .return_from_recursion
        jmp     .copy_dir_entry_done
.do_copy_dir_entry:
        inc     ebp
; ignore special entries "." and ".."
        cmp     word [edx+40], '.'
        jz      .copy_dir_entry_continue
        cmp     word [edx+40], '..'
        jnz     @f
        cmp     byte [edx+42], 0
        jz      .copy_dir_entry_continue
@@:
        mov     esi, execdata
@@:
        lodsb
        test    al, al
        jnz     @b
        dec     esi
        cmp     byte [esi-1], '/'
        jz      @f
        mov     byte [esi], '/'
        inc     esi
@@:
        mov     edi, esi
        lea     esi, [edx+40]
@@:
        cmp     edi, execdataend
        jae     .fullname_big
        lodsb
        stosb
        test    al, al
        jnz     @b
        mov     esi, CopyDestEditBuf+12
@@:
        lodsb
        test    al, al
        jnz     @b
        dec     esi
        cmp     byte [esi-1], '/'
        jz      @f
        mov     byte [esi], '/'
        inc     esi
@@:
        mov     edi, esi
        lea     esi, [edx+40]
@@:
        cmp     edi, CopyDestEditBuf+513
        jae     .fullname2_big
        lodsb
        stosb
        test    al, al
        jnz     @b
        test    byte [edx], 10h
        jnz     .entry_is_folder
        call    copy_file_worker
        ja      .cancel3
        jmp     .restore_name
.entry_is_folder:
; allocate new item in directory stack
        mov     eax, [copy_dir_stack_ptr]
        mov     [eax], ebp
        add     eax, 4
        mov     [copy_dir_stack_ptr], eax
; do recursive copying
        jmp     .enter_recursion
.fullname_big:
; we will just ignore such files and continue - in real life this situation can not happen
        mov     esi, execdataend-1
        jmp     .do_restore_name2
.fullname2_big:
        mov     esi, CopyDestEditBuf+12+512
        jmp     .restore_name2
.restore_name:
        mov     esi, CopyDestEditBuf+12
@@:
        lodsb
        test    al, al
        jnz     @b
        dec     esi
        dec     esi
.restore_name2:
        call    delete_last_name
        mov     esi, execdata
@@:
        lodsb
        test    al, al
        jnz     @b
        dec     esi
        dec     esi
.do_restore_name2:
        call    delete_last_name
.copy_dir_entry_continue:
        add     edx, 304
        jmp     .copy_dir_entry_loop
.skip1:
.copy_dir_entry_done:
; return to previous directory
        mov     esi, execdata
        call    delete_last_name_from_end
        mov     eax, [source_hModule]
        test    eax, eax
        jz      @f
        push    execdata
        push    aDotDot
        push    [source_hPlugin]
        call    [eax+PluginInfo.SetFolder]
        jmp     @f
.skip2:
        mov     esi, execdata
        call    delete_last_name_from_end
@@:
; pop item from directory stack
        mov     ecx, [copy_dir_stack_ptr]
        cmp     ecx, copy_dir_stack
        jbe     .done
        sub     ecx, 4
        mov     [copy_dir_stack_ptr], ecx
        mov     ebp, [ecx]
; restore prev directory name
        mov     esi, CopyDestEditBuf+12
        call    delete_last_name_from_end
        jmp     .return_from_recursion
.done:
        mov     [dirinfo.first], 0      ; do not destroys flags
        popad
        ret
.cancel2:
        sub     [copy_dir_stack_ptr], 4
.cancel3:
        mov     esi, execdata
        call    delete_last_name_from_end
.cancel:
        mov     eax, [source_hModule]
        test    eax, eax
        jz      .cancel.ret
        cmp     [copy_dir_stack_ptr], copy_dir_stack
        jb      .cancel.ret
        push    execdata
        push    aDotDot
        push    [source_hPlugin]
        call    [eax+PluginInfo.SetFolder]
        jmp     .cancel2
.cancel.ret:
        xor     eax, eax
        inc     eax
        popad
        ret

delete_last_name_from_end:
        lodsb
        test    al, al
        jnz     delete_last_name_from_end
        dec     esi
        dec     esi
delete_last_name:
        std
@@:
        lodsb
        cmp     al, '/'
        jnz     @b
        cld
        inc     esi
        cmp     esi, execdata
        jz      @f
        cmp     esi, CopyDestEditBuf+12
        jz      @f
        dec     esi
@@:
        mov     byte [esi+1], 0
        ret

copy_AddDir:
        push    1
        pop     eax                     ; for "return true"
        pushad
        mov     esi, CopyDestEditBuf+12
@@:
        lodsb
        test    al, al
        jnz     @b
        cmp     byte [esi-2], '/'
        jnz     @f
        dec     esi
@@:
        push    dword [esi-1]
        push    esi
        mov     byte [esi-1], '/'
        mov     edi, esi
        mov     esi, [esp+28h+4]
.0:
        lodsb
        stosb
        cmp     edi, CopyDestEditBuf+12+512
        jae     .done
        test    al, al
        jnz     .0
        push    RetryOrCancelBtn
        push    2
        call    makedir
        jz      .done
        and     dword [esp+8+1Ch], 0
.done:
        pop     esi
        pop     dword [esi-1]
        popad
        ret     8

copy_AddFile:
        pushad
        mov     eax, [esp+20h+12]
        mov     [source_hFile], eax
        mov     edx, [esp+20h+8]
        mov     edi, [esp+20h+4]
        call    copy_file_worker2
        popad
        setna   al
        ret     12

virtual at 0
_FILE:
.pos            dq      ?
.bufpos         dq      ?
.bufsize        dd      ?
.mode           dd      ?
.hPlugin        dd      ?
.hFile          dd      ?
.fileinfo:
.fimode         dd      ?
.fioffset       dq      ?
.fisize         dd      ?
.fibuf          dd      ?
.finame         rb      1024
.attr           rb      40
align 512
.buf            rb      2048
.size = $
end virtual

O_READ = 1      ; allows read from file
O_WRITE = 2     ; allows write to file
O_CREATE = 4    ; if file does not exist and this flag is set, create file;
                ; if file does not exist and this flag is not set, fail
O_TRUNCATE = 8  ; truncate file if it exists
O_SEQUENTIAL_ONLY = 10h ; there will be no 'seek'/'setpos' calls

; HANDLE __stdcall open(const char* name, int mode);
; Opens physical file
open:
        pushad
        mov     ecx, _FILE.size
        call    xpgalloc
        test    eax, eax
        jz      .ret0z
        mov     [esp+28], eax
        mov     ecx, eax
        mov     esi, [esp+36]
        lea     edi, [eax+_FILE.finame]
        lea     edx, [eax+_FILE.finame+1024]
@@:
        lodsb
        stosb
        test    al, al
        jz      @f
        cmp     edi, edx
        jb      @b
.ret0:
        call    pgfree
.ret0z:
        popad
        xor     eax, eax
        ret     8
@@:
        mov     eax, [esp+40]
        mov     [ecx+_FILE.mode], eax
.getattr:
        lea     edi, [ecx+_FILE.fileinfo]
        mov     ebx, edi
        push    5
        pop     eax
        stosd
        xor     eax, eax
        stosd
        stosd
        stosd
        lea     eax, [ecx+_FILE.attr]
        stosd
        push    70
        pop     eax
        int     0x40
        test    eax, eax
        jz      .found
        cmp     eax, 5
        jnz     .ret0
; file does not exist
        test    [ecx+_FILE.mode], O_CREATE
        jz      .ret0
.truncate:
        lea     ebx, [ecx+_FILE.fileinfo]
        mov     byte [ebx], 2
        push    70
        pop     eax
        int     0x40
        test    eax, eax
        jz      .getattr
        jmp     .ret0
.found:
        test    [ecx+_FILE.mode], O_TRUNCATE
        jz      @f
        cmp     dword [ecx+_FILE.attr+36], eax
        jnz     .truncate
        cmp     dword [ecx+_FILE.attr+32], eax
        jnz     .truncate
@@:
        mov     dword [ecx+_FILE.pos], eax
        mov     dword [ecx+_FILE.pos+4], eax
        mov     dword [ecx+_FILE.bufpos], eax
        mov     dword [ecx+_FILE.bufpos+4], eax
        mov     [ecx+_FILE.bufsize], eax
        mov     [ecx+_FILE.hPlugin], eax
        mov     [ecx+_FILE.hFile], eax
        mov     dword [ecx+_FILE.fioffset], eax
        mov     dword [ecx+_FILE.fioffset+4], eax
        mov     [esp+28], ecx
        popad
        ret     8

; HANDLE __stdcall open2(int plugin_id, HANDLE plugin_instance, const char* name, int mode);
; Opens file on plugin panel
open2:
        cmp     dword [esp+4], 0
        jnz     .plugin
        pop     eax
        add     esp, 8
        push    eax
        jmp     open
.plugin:
        pushad
        mov     ecx, _FILE.size
        call    xpgalloc
        test    eax, eax
        jz      .ret0z
        mov     [esp+28], eax
        mov     ecx, eax
        mov     esi, [esp+44]
        lea     edi, [eax+_FILE.finame]
        lea     edx, [eax+_FILE.finame+1024]
@@:
        lodsb
        stosb
        test    al, al
        jz      @f
        cmp     edi, edx
        jb      @b
.ret0:
        call    pgfree
.ret0z:
        popad
        xor     eax, eax
        ret     8
@@:
        mov     edx, [esp+36]
        mov     [ecx+_FILE.hPlugin], edx
        mov     ebx, [esp+40]
        mov     eax, [esp+48]
        mov     [ecx+_FILE.mode], eax
        push    ebx ecx
        push    eax
        lea     eax, [ecx+_FILE.finame]
        push    eax
        push    ebx
        call    [edx+PluginInfo.open]
        pop     ecx ebx
        test    eax, eax
        jz      .ret0
        mov     [ecx+_FILE.hFile], eax
        mov     edx, [esp+36]
        push    ecx
        lea     edi, [ecx+_FILE.fileinfo]
        push    edi
        xor     eax, eax
        push    ecx
        push    10
        pop     ecx
        rep     stosd
        pop     ecx
        lea     eax, [ecx+_FILE.finame]
        push    eax
        push    ebx
        call    [edx+PluginInfo.getattr]
        pop     ecx
        xor     eax, eax
        mov     dword [ecx+_FILE.pos], eax
        mov     dword [ecx+_FILE.pos+4], eax
        mov     dword [ecx+_FILE.bufpos], eax
        mov     dword [ecx+_FILE.bufpos+4], eax
        mov     [ecx+_FILE.bufsize], eax
        mov     dword [ecx+_FILE.fioffset], eax
        mov     dword [ecx+_FILE.fioffset+4], eax
        mov     [esp+28], ecx
        popad
        ret     16

; unsigned __stdcall read(HANDLE hFile, void* buf, unsigned size);
read:
        xor     eax, eax
        pushad
        mov     ecx, [esp+36]
        test    [ecx+_FILE.mode], O_READ
        jnz     @f
.ret:
        popad
        ret     12
@@:
        cmp     dword [esp+44], eax
        jz      .ret
        mov     [ecx+_FILE.fimode], eax
        mov     ebx, [ecx+_FILE.bufsize]
        mov     eax, dword [ecx+_FILE.pos]
        and     eax, 2047
        sub     ebx, eax
        jbe     .nobuf0
        cmp     ebx, [esp+44]
        jbe     @f
        mov     ebx, [esp+44]
@@:
        push    ecx
        lea     esi, [ecx+eax+_FILE.buf]
        mov     ecx, ebx
        mov     edi, [esp+44]
        rep     movsb
        pop     ecx
        mov     [esp+40], edi
        add     [esp+28], ebx
        add     dword [ecx+_FILE.pos], ebx
        adc     dword [ecx+_FILE.pos+4], 0
        test    dword [ecx+_FILE.pos], 2047
        jnz     @f
        and     [ecx+_FILE.bufsize], 0
@@:
        sub     [esp+44], ebx
        jz      .ret
.nobuf0:
        test    dword [ecx+_FILE.pos], 2047
        jz      .aligned
        cmp     dword [ecx+_FILE.bufsize], 0
        jnz     .ret
        lea     ebx, [ecx+_FILE.fileinfo]
        mov     dword [ebx+12], 2048
        lea     eax, [ecx+_FILE.buf]
        mov     dword [ebx+16], eax
        mov     eax, dword [ecx+_FILE.fioffset]
        mov     dword [ecx+_FILE.bufpos], eax
        mov     eax, dword [ecx+_FILE.fioffset+4]
        mov     dword [ecx+_FILE.bufpos+4], eax
        call    .doread
        test    eax, eax
        jnz     .ret
        mov     [ecx+_FILE.bufsize], ebx
        mov     eax, dword [ecx+_FILE.pos]
        and     eax, 2047
        sub     ebx, eax
        jbe     .ret
        cmp     ebx, [esp+44]
        jbe     @f
        mov     ebx, [esp+44]
@@:
        push    ecx
        lea     esi, [ecx+eax+_FILE.buf]
        mov     ecx, ebx
        mov     edi, [esp+44]
        rep     movsb
        pop     ecx
        add     dword [ecx+_FILE.pos], ebx
        adc     dword [ecx+_FILE.pos+4], 0
        mov     [esp+40], edi
        add     [esp+28], ebx
        sub     [esp+44], ebx
        jz      .ret
        test    dword [ecx+_FILE.pos], 2047
        jnz     .ret
.aligned:
        lea     ebx, [ecx+_FILE.fileinfo]
        mov     eax, [esp+44]
        and     eax, not 2047
        jz      .finish
        and     [ecx+_FILE.bufsize], 0
        mov     [ebx+12], eax
        mov     eax, [esp+40]
        mov     [ebx+16], eax
        call    .doread
        test    eax, eax
        jnz     .ret
        add     dword [ecx+_FILE.pos], ebx
        adc     dword [ecx+_FILE.pos+4], 0
        add     [esp+28], ebx
        add     [esp+40], ebx
        sub     [esp+44], ebx
        jz      .ret
        cmp     ebx, [ecx+_FILE.fisize]
        jb      .ret
.finish:
        lea     ebx, [ecx+_FILE.fileinfo]
        mov     dword [ebx+12], 2048
        lea     eax, [ecx+_FILE.buf]
        mov     [ebx+16], eax
        and     [ecx+_FILE.bufsize], 0
        mov     eax, dword [ecx+_FILE.fioffset]
        mov     dword [ecx+_FILE.bufpos], eax
        mov     eax, dword [ecx+_FILE.fioffset+4]
        mov     dword [ecx+_FILE.bufpos+4], eax
        call    .doread
        test    eax, eax
        jnz     .ret
        mov     [ecx+_FILE.bufsize], ebx
        cmp     ebx, [esp+44]
        jb      @f
        mov     ebx, [esp+44]
@@:
        add     [esp+28], ebx
        add     dword [ecx+_FILE.pos], ebx
        adc     dword [ecx+_FILE.pos+4], 0
        lea     esi, [ecx+_FILE.buf]
        mov     edi, [esp+40]
        mov     ecx, ebx
        rep     movsb
        popad
        ret     12
.doread:
        mov     eax, [ecx+_FILE.hPlugin]
        test    eax, eax
        jz      .native
        push    ecx
        push    [ecx+_FILE.fisize]
        push    [ecx+_FILE.fibuf]
        push    [ecx+_FILE.hFile]
        call    [eax+PluginInfo.read]
        pop     ecx
        cmp     eax, -1
        jz      @f
        mov     ebx, eax
        xor     eax, eax
        jmp     .addpos
@@:
        ret
.native:
        push    70
        pop     eax
        int     0x40
        test    eax, eax
        jz      .addpos
        cmp     eax, 6
        jnz     @b
        xor     eax, eax
.addpos:
        add     dword [ecx+_FILE.fioffset], ebx
        adc     dword [ecx+_FILE.fioffset+4], 0
        ret

; void __stdcall seek(HANDLE hFile, int method, __int64 newpos);
seek:
        pushad
        mov     ecx, [esp+36]
        mov     eax, [esp+44]
        mov     edx, [esp+48]
        cmp     dword [esp+40], 1
        jb      .set
        ja      .end
        add     eax, dword [ecx+_FILE.pos]
        adc     edx, dword [ecx+_FILE.pos+4]
        jmp     .set
.end:
        add     eax, dword [ecx+_FILE.attr+32]
        adc     edx, dword [ecx+_FILE.attr+36]
.set:
        mov     dword [ecx+_FILE.pos], eax
        mov     dword [ecx+_FILE.pos+4], edx
        and     eax, not 2047
        cmp     eax, dword [ecx+_FILE.bufpos]
        jnz     @f
        cmp     edx, dword [ecx+_FILE.bufpos+4]
        jz      .bufposok
@@:
        and     [ecx+_FILE.bufsize], 0
        mov     dword [ecx+_FILE.bufpos], eax
        mov     dword [ecx+_FILE.bufpos+4], edx
.bufposok:
        cmp     [ecx+_FILE.bufsize], 0
        jnz     .ret
        cmp     eax, dword [ecx+_FILE.fioffset]
        jnz     @f
        cmp     edx, dword [ecx+_FILE.fioffset+4]
        jz      .ret
@@:
        mov     dword [ecx+_FILE.fioffset], eax
        mov     dword [ecx+_FILE.fioffset+4], edx
        mov     eax, [ecx+_FILE.hPlugin]
        test    eax, eax
        jz      @f
        push    dword [ecx+_FILE.fioffset+4]
        push    dword [ecx+_FILE.fioffset]
        push    [ecx+_FILE.hFile]
        call    [eax+PluginInfo.setpos]
@@:
.ret:
        popad
        ret     16

setpos_default:
        push    dword [esp+12]
        push    dword [esp+12]
        push    0
        push    dword [esp+16]
        call    seek
        ret     12

; __int64 __stdcall tell(HANDLE hFile);
tell:
        mov     eax, [esp+4]
        mov     edx, dword [eax+_FILE.pos+4]
        mov     eax, dword [eax+_FILE.pos]
        ret     4

; __int64 __stdcall filesize(HANDLE hFile);
filesize:
        mov     eax, [esp+4]
        mov     edx, dword [eax+_FILE.attr+36]
        mov     eax, dword [eax+_FILE.attr+32]
        ret     4

; void __stdcall close(HANDLE hFile);
close:
        pushad
        mov     ecx, [esp+24h]
        mov     eax, [ecx+_FILE.hPlugin]
        test    eax, eax
        jz      @f
        push    ecx
        push    [ecx+_FILE.hFile]
        call    [eax+PluginInfo.close]
        pop     ecx
@@:
        call    pgfree
        popad
        ret     4

getattr_default:
        mov     eax, 2
        ret     12
