kolibrios/programs/fs/kfar/trunk/kfar_arc/kfar_arc.asm
Evgeny Grechnikov (Diamond) cadcbb20ef KFar_Arc 0.17: fixed incorrect handling of some archives
git-svn-id: svn://kolibrios.org@1148 a494cfbc-eb01-0410-851d-a64ba20cac60
2009-09-03 18:46:38 +00:00

1563 lines
46 KiB
NASM
Raw Blame History

;
; project name: KFar_Arc - plugin for KFar, which supports various archives
; target platform: KolibriOS
; compiler: FASM 1.67.14
; version: 0.17
; last update: 2009-09-03 (Sep 03, 2009)
; minimal KFar version: 0.43
; minimal kernel: no limit
;
; author: Diamond
; email: diamondz@land.ru
; web: http://diamond.kolibrios.org
;
; standard start of Kolibri dynamic library
format MS COFF
public EXPORTS
section '.flat' code readable align 16
; include auxiliary procedures
include 'kglobals.inc' ; iglobal/uglobal
include 'lang.inc' ; define language for localized strings
include 'crc.inc' ; CRC32 calculation
include 'sha256.inc' ; SHA-256 hash algorithm
include 'aes.inc' ; AES crypto algorithm
; include main code for archives loading
include '7z.inc' ; *.7z
include 'lzma.inc' ; LZMA-decoder for *.7z
include 'ppmd.inc' ; PPMD-decoder for *.7z
include '7zbranch.inc' ; branch filters for *.7z
include '7zaes.inc' ; AES cryptor for *.7z
include 'zip.inc' ; *.zip
include 'deflate.inc' ; Deflate[64] decoder for *.7z and *.zip
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;; Interface for KFar ;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
virtual at 0
kfar_info_struc:
.lStructSize dd ?
.kfar_ver dd ?
.open dd ?
.open2 dd ?
.read dd ?
.write dd ?
.seek dd ?
.tell dd ?
.flush dd ?
.filesize dd ?
.close dd ?
.pgalloc dd ?
.pgrealloc dd ?
.pgfree dd ?
.getfreemem dd ?
.pgalloc2 dd ?
.pgrealloc2 dd ?
.pgfree2 dd ?
.menu dd ?
.menu_centered_in dd ?
.DialogBox dd ?
.SayErr dd ?
.Message dd ?
.cur_console_size dd ?
end virtual
; int __stdcall plugin_load(kfar_info* info);
; Initialization of plugin + Save used KFar functions.
plugin_load:
mov eax, [esp+4]
mov [kfar_info], eax
push [eax+kfar_info_struc.open2]
pop [open2]
push [eax+kfar_info_struc.filesize]
pop [filesize]
push [eax+kfar_info_struc.read]
pop [read]
push [eax+kfar_info_struc.seek]
pop [seek]
push [eax+kfar_info_struc.close]
pop [close]
lea esi, [eax+kfar_info_struc.DialogBox]
mov edi, DialogBox
movsd
movsd
movsd
movsd
lea esi, [eax+kfar_info_struc.pgalloc]
mov edi, pgalloc
movsd
movsd
movsd
movsd
call init_crc_table
call init_aes
call init_ppmd
xor eax, eax ; success
ret 4
; HANDLE __stdcall OpenFilePlugin(HANDLE basefile,
; const void* attr, const void* data, int datasize,
; int baseplugin_id, HANDLE baseplugin_instance, const char* name);
; This function is called when user presses Enter (or Ctrl+PgDn) on file.
; Plugin tests whether given file is of supported type
; and if so, loads information and returns
; handle to be used in subsequent calls to ReadFolder, SetFolder and so on.
OpenFilePlugin:
mov [bPasswordDefined], 0
mov esi, [esp+12]
mov ebp, [esp+4]
; test for 7z archive
cmp dword [esp+16], 20h ; minimal size of 7z archive is 20h bytes
jb .no_7z
cmp word [esi], '7z' ; signature, part 1
jnz .no_7z
cmp dword [esi+2], 0x1C27AFBC ; signature, part 2
jnz .no_7z
call open_7z
ret 28
.no_7z:
; test for zip archive
cmp dword [esp+16], 22 ; minimal size of zip archive is 22 bytes
jb .no_zip
cmp word [esi], 0x4B50
jnz .no_zip
cmp word [esi+2], 0x0403
jz .zip
cmp word [esi+2], 0x0201
jz .zip
cmp word [esi+2], 0x0606
jz .zip
cmp word [esi+2], 0x0706
jz .zip
cmp word [esi+2], 0x0605
jnz .no_zip
.zip:
call open_zip
ret 28
.no_zip:
xor eax, eax
ret 28
; Handle of plugin in kfar_arc is as follow:
virtual at 0
handle_common:
.type dd ?
.root.subfolders dd ?
.root.subfolders.end dd ?
.root.subfiles dd ?
.root.subfiles.end dd ?
.root.NumSubItems dd ?
.curdir dd ?
.NumFiles dd ?
; ... some plugin-specific data follows ...
end virtual
; and for each archive item there is one file info structure, which begins as follow:
virtual at 0
file_common:
.fullname dd ? ; pointer to cp866 string
.name dd ? ; name without path (end of .fullname)
.namelen dd ? ; strlen(.name)
.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 in list of subfolders/files
.prev dd ? ; previous item in list of subfolders/files
.stamp dd ? ; stamp for GetFiles
end virtual
; void __stdcall ClosePlugin(HANDLE hPlugin);
; This function frees all resources allocated in OpenFilePlugin.
ClosePlugin:
mov eax, [esp+4] ; get hPlugin
mov eax, [eax] ; hPlugin is pointer to internal data structure
; first dword is archive type (type_xxx constants)
dec eax ; types start from 1
jmp dword [ClosePluginTable+eax*4]
; int __stdcall ReadFolder(HANDLE hPlugin,
; unsigned dirinfo_start, unsigned dirinfo_size, void* dirdata);
ReadFolder:
; init header
mov edi, [esp+16]
mov ecx, 32/4
xor eax, eax
rep stosd
mov byte [edi-32], 1 ; version
mov ebp, [esp+4]
; get current directory
lea ebx, [ebp+handle_common.root.subfolders]
cmp [ebp+handle_common.curdir], 0
jz @f
mov ebx, [ebp+handle_common.curdir]
add ebx, file_common.subfolders
@@:
mov ecx, [ebx+16]
mov [edi-24], ecx ; number of files
; edi points to BDFE
push 6 ; assume EOF
pop eax
sub ecx, [esp+8]
ja @f
and dword [edi-28], 0 ; number of files read
ret 10h
@@:
cmp ecx, [esp+12]
jb @f
mov ecx, [esp+12]
xor eax, eax ; OK
@@:
mov [edi-28], ecx
push eax
; copy files data
jecxz .done
; seek to required item
mov eax, [esp+8+4]
mov esi, [ebx]
.0:
test esi, esi
jnz .1
mov esi, [ebx+8]
.1:
add esi, ebp
dec eax
js .2
mov esi, [esi+file_common.next]
jmp .0
.2:
.copy:
pushad
mov eax, esi
mov ecx, [ebp]
call dword [getattrTable+(ecx-1)*4]
pop edi esi
push esi edi
add edi, 40
mov ecx, [esi+file_common.namelen]
mov esi, [esi+file_common.name]
rep movsb
mov byte [edi], 0
popad
add edi, 304
mov esi, [esi+file_common.next]
test esi, esi
jnz @f
mov esi, [ebx+8]
@@:
add esi, ebp
loop .copy
.done:
pop eax
ret 10h
; bool __stdcall SetFolder(HANDLE hPlugin,
; const char* relative_path, const char* absolute_path);
SetFolder:
mov ebp, [esp+4]
mov edx, [ebp+handle_common.curdir]
mov esi, [esp+8]
cmp dword [esi], '..'
jz .toparent
xor ecx, ecx
@@:
inc ecx
cmp byte [esi+ecx], 0
jnz @b
mov ebx, [ebp+handle_common.root.subfolders]
test edx, edx
jz .scan
mov ebx, [edx+file_common.subfolders]
.scan:
test ebx, ebx
jz .err
add ebx, ebp
cmp [ebx+file_common.namelen], ecx
jnz .cont
push ecx esi
mov edi, [ebx+file_common.name]
repz cmpsb
pop esi ecx
jz .set
.cont:
mov ebx, [ebx+file_common.next]
jmp .scan
.toparent:
test edx, edx
jz .err
mov ebx, [edx+file_common.parent]
test ebx, ebx
jz @f
add ebx, ebp
@@:
.set:
mov [ebp+handle_common.curdir], ebx
mov al, 1
ret 12
.err:
xor eax, eax
ret 12
iglobal
cur_stamp dd 0
endg
uglobal
tmp_bdfe rb 304
endg
; void __stdcall GetFiles(HANDLE hPlugin, int NumItems, void* items[],
; void* addfile, void* adddir);
; bool __stdcall addfile(const char* name, void* bdfe_info, HANDLE hFile);
; bool __stdcall adddir(const char* name, void* bdfe_info);
GetFiles:
mov ebp, [esp+4]
mov ecx, [ebp+handle_common.NumFiles]
test ecx, ecx
jz .ret
mov ebx, ebp
mov eax, [ebx]
add ebx, [basesizes+(eax-1)*8]
inc [cur_stamp]
.loop:
push ecx
mov esi, [ebx+file_common.fullname]
mov edx, [ebp+handle_common.curdir]
test edx, edx
jz .incur
mov eax, [cur_stamp]
mov [edx+file_common.stamp], eax
mov edi, [edx+file_common.fullname]
mov ecx, [edx+file_common.namelen]
add ecx, [edx+file_common.name]
sub ecx, edi
repz cmpsb
jnz .cont
.incur:
cmp byte [esi], '/'
jnz @f
inc esi
@@:
mov ecx, [esp+12] ; NumItems
mov edx, [esp+16] ; items
cmp ecx, -1
jz .ok
.check:
sub ecx, 1
js .cont
push esi
mov edi, [edx]
add edi, 40
@@:
lodsb
scasb
jnz @f
test al, al
jz .ok2
jmp @b
@@:
pop esi
cmp al, '/'
jnz @f
cmp byte [edi-1], 0
jz .ok
@@:
add edx, 4
jmp .check
.ok2:
pop esi
.ok:
; add all parents directories if needed
.parloope:
mov ecx, [ebx+file_common.parent]
jecxz .pardone
add ecx, ebp
mov eax, [cur_stamp]
cmp [ecx+file_common.stamp], eax
jz .pardone
.parloopi:
mov edx, ecx
mov ecx, [ecx+file_common.parent]
jecxz @f
add ecx, ebp
cmp [ecx+file_common.stamp], eax
jnz .parloopi
@@:
mov [edx+file_common.stamp], eax
push esi
mov ecx, [edx+file_common.name]
add ecx, [edx+file_common.namelen]
xor eax, eax
xchg al, [ecx]
push eax ecx
mov eax, edx
mov edi, tmp_bdfe
push edi
sub esi, [ebx+file_common.fullname]
add esi, [edx+file_common.fullname]
push esi
mov ecx, [ebp]
call dword [getattrTable+(ecx-1)*4]
mov eax, [esp+24+20]
call eax
pop ecx edx
mov [ecx], dl
pop esi
test al, al
jz .forced_exit
jmp .parloope
.pardone:
cmp [ebx+file_common.bIsDirectory], 0
jz .addfile
mov eax, [cur_stamp]
cmp [ebx+file_common.stamp], eax
jz .cont
mov [ebx+file_common.stamp], eax
push esi
mov ecx, [ebx+file_common.name]
add ecx, [ebx+file_common.namelen]
xor eax, eax
xchg al, [ecx]
push eax ecx
mov eax, ebx
mov edi, tmp_bdfe
push edi
push esi
mov ecx, [ebp]
call dword [getattrTable+(ecx-1)*4]
mov eax, [esp+24+20]
call eax
pop ecx edx
mov [ecx], dl
pop esi
test al, al
jz .forced_exit
jmp .cont
.addfile:
push ebx esi ebp
push 11h
pop edi
mov eax, ebx
mov ecx, [ebp]
call dword [openTable+(ecx-1)*4]
pop ebp esi ebx
test eax, eax
jz .cont
push eax
push eax
mov edi, tmp_bdfe
push edi
push esi
mov eax, ebx
mov ecx, [ebp]
call dword [getattrTable+(ecx-1)*4]
mov eax, [esp+20+16]
call eax
pop ecx
push eax ebp
push ebx
push ecx
call myclose
pop ebx
pop ebp eax
test al, al
jz .forced_exit
.cont:
mov eax, [ebp]
add ebx, [basesizes+(eax-1)*8+4]
pop ecx
dec ecx
jnz .loop
.ret:
ret 20
.forced_exit:
pop ecx
jmp .ret
; void __stdcall GetOpenPluginInfo(HANDLE hPlugin, OpenPluginInfo* info);
GetOpenPluginInfo:
mov eax, [esp+8] ; get info ptr
mov byte [eax], 3 ; flags: add non-existing '..' entry automatically
; use GetFiles for copying
ret 8
; int __stdcall getattr(HANDLE hPlugin, const char* filename, void* info);
mygetattr:
call lookup_file_name
test eax, eax
jz @f
mov edx, [ebp]
dec edx
mov edi, [esp+12] ; info ptr
call dword [getattrTable+edx*4]
xor eax, eax
ret 12
@@:
mov al, 5 ; ERROR_FILE_NOT_FOUND
ret 12
; HANDLE __stdcall open(HANDLE hPlugin, const char* filename, int mode);
myopen:
call lookup_file_name
test eax, eax
jz @f
mov edx, [ebp]
dec edx
mov edi, [esp+12] ; mode
call dword [openTable+edx*4]
@@:
ret 12
; unsigned __stdcall read(HANDLE hFile, void* buf, unsigned size);
myread:
mov ebx, [esp+4]
mov eax, [ebx]
jmp dword [readTable+eax*4]
; void __stdcall setpos(HANDLE hFile, __int64 pos);
mysetpos:
mov ebx, [esp+4]
mov eax, [ebx]
jmp dword [setposTable+eax*4]
; void __stdcall close(HANDLE hFile);
myclose:
mov ebx, [esp+4]
mov eax, [ebx]
jmp dword [closeTable+eax*4]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;; Auxiliary procedures ;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; return.err and return.clear are labels to jmp if something is invalid
; the caller must previously define [_esp], [_ebp] and [error_proc], [clear_proc]
return.err:
mov esp, [_esp]
mov ebp, [_ebp]
jmp [error_proc]
return.clear:
mov esp, [_esp]
mov ebp, [_ebp]
jmp [clear_proc]
; data for following routine
iglobal
align 4
_24 dd 24
_60 dd 60
_10000000 dd 10000000
days400year dd 365*400+100-4+1
days100year dd 365*100+25-1
days4year dd 365*4+1
days1year dd 365
months dd 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
months2 dd 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
_400 dd 400
_100 dd 100
endg
; Convert QWORD FILETIME to BDFE format.
ntfs_datetime_to_bdfe:
; edx:eax = number of 100-nanosecond intervals since January 1, 1601, in UTC
push eax
mov eax, edx
xor edx, edx
div [_10000000]
xchg eax, [esp]
div [_10000000]
pop edx
; edx:eax = number of seconds since January 1, 1601
push eax
mov eax, edx
xor edx, edx
div [_60]
xchg eax, [esp]
div [_60]
mov [edi], dl
pop edx
; edx:eax = number of minutes
div [_60]
mov [edi+1], dl
; eax = number of hours (note that 2^64/(10^7*60*60) < 2^32)
xor edx, edx
div [_24]
mov [edi+2], dl
mov [edi+3], byte 0
; eax = number of days since January 1, 1601
xor edx, edx
div [days400year]
imul eax, 400
add eax, 1601
mov [edi+6], ax
mov eax, edx
xor edx, edx
div [days100year]
cmp al, 4
jnz @f
dec eax
add edx, [days100year]
@@:
imul eax, 100
add [edi+6], ax
mov eax, edx
xor edx, edx
div [days4year]
shl eax, 2
add [edi+6], ax
mov eax, edx
xor edx, edx
div [days1year]
cmp al, 4
jnz @f
dec eax
add edx, [days1year]
@@:
add [edi+6], ax
push esi edx
mov esi, months
movzx eax, word [edi+6]
test al, 3
jnz .noleap
xor edx, edx
push eax
div [_400]
pop eax
test edx, edx
jz .leap
xor edx, edx
div [_100]
test edx, edx
jz .noleap
.leap:
mov esi, months2
.noleap:
pop edx
xor eax, eax
inc eax
@@:
sub edx, [esi]
jb @f
add esi, 4
inc eax
jmp @b
@@:
add edx, [esi]
pop esi
inc edx
mov [edi+4], dl
mov [edi+5], al
add edi, 8
ret
; By given array of files information, initialize links between them
; ("[folder] contains [item]" relations).
; Information structure must be compatible with 'file_common'.
; Size of information structure is in [esp+4].
init_file_links:
; in: edx->file infos, ebx = number of files, [esp+4] = size,
; edi->{dd root.subfolders, dd root.subfolders.end,
; dd root.subfiles, dd root.subfiles.end, dd root.NumItems}
xor eax, eax
mov [.free], eax
push edi
stosd
stosd
stosd
stosd
stosd
pop edi
; Loop through all files
.mainloop:
dec ebx
js .mainloopdone
; Parse file name
; esi->current character in name
; dword [esp] = start of current component in file name
; ecx->{dd curdir.subfolders, dd curdir.subfolders.end,
; dd curdir.subfiles, dd curdir.subfiles.end}
mov esi, [edx+file_common.fullname]
mov ecx, edi
.parseloop:
push esi
.parsename:
lodsb
test al, al
jz @f
cmp al, '/'
jnz .parsename
@@:
; we have found next component of current name; look for it in current directory
sub esi, [esp]
dec esi ; esi = strlen(component)
cmp esi, 259
jbe @f
push ContinueBtn
push 1
push aNameTooLong_ptr
push 1
call [SayErr]
jmp return.clear
@@:
push ecx
mov eax, [ecx] ; eax->subfolders list
mov ecx, esi
.scansubfolders:
test eax, eax
jz .nofolder
add eax, [hOut]
cmp [eax+file_common.namelen], ecx
jnz .scancont
mov esi, [esp+4]
push ecx edi
mov edi, [eax+file_common.name]
repz cmpsb
pop edi ecx
jz .subfound
.scancont:
mov eax, [eax+file_common.next]
jmp .scansubfolders
.subfound:
; found subfolder, set it as current and continue parsing name
add [esp+4], ecx
pop ecx
lea ecx, [eax+file_common.subfolders]
pop esi
lodsb
test al, al
jnz .parseloop
; that was the last component of the name, and we have found subfolder
; so found subfolder is a virtual subfolder and must be replaced with current
; name
mov eax, [ecx-file_common.subfolders+file_common.namelen]
mov [edx+file_common.namelen], eax
sub esi, eax
dec esi
mov [edx+file_common.name], esi
sub edx, [hOut] ; convert pointer to relative
; replace item in L2-list
mov eax, [ecx-file_common.subfolders+file_common.prev]
test eax, eax
jnz .1
mov eax, [ecx-file_common.subfolders+file_common.parent]
sub eax, file_common.next - file_common.subfolders
jnc .1
lea eax, [edi-file_common.next]
jmp .2
.1:
add eax, [hOut]
.2:
mov [eax+file_common.next], edx
mov eax, [ecx-file_common.subfolders+file_common.next]
test eax, eax
jnz .3
mov eax, [ecx-file_common.subfolders+file_common.parent]
sub eax, file_common.prev - file_common.subfolders.end
jnc .3
lea eax, [edi-file_common.prev+4]
jmp .4
.3:
add eax, [hOut]
.4:
mov [eax+file_common.prev], edx
; correct parent links in childrens
mov eax, [ecx]
@@:
test eax, eax
jz @f
add eax, [hOut]
mov [eax+file_common.parent], edx
mov eax, [eax+file_common.next]
jmp @b
@@:
mov eax, [ecx+8]
@@:
test eax, eax
jz @f
add eax, [hOut]
mov [eax+file_common.parent], edx
mov eax, [eax+file_common.next]
jmp @b
@@:
add edx, [hOut]
; set children links
mov eax, [ecx]
mov [edx+file_common.subfolders], eax
mov eax, [ecx+4]
mov [edx+file_common.subfolders.end], eax
mov eax, [ecx+8]
mov [edx+file_common.subfiles], eax
mov eax, [ecx+12]
mov [edx+file_common.subfiles.end], eax
mov eax, [ecx+16]
mov [edx+file_common.NumSubItems], eax
; set prev/next links
mov eax, [ecx-file_common.subfolders+file_common.next]
mov [edx+file_common.next], eax
mov eax, [ecx-file_common.subfolders+file_common.prev]
mov [edx+file_common.prev], eax
; add old item to list of free items
uglobal
align 4
init_file_links.free dd ?
endg
sub ecx, file_common.subfolders
mov eax, [.free]
mov [ecx], eax
sub ecx, [hOut]
mov [.free], ecx
jmp .mainloopcont
.nofolder:
mov eax, edx
mov esi, [esp+4]
cmp byte [esi+ecx], 0
jz .newitem
; the current item is as 'dir1/item1' and 'dir1' has not been found
; allocate virtual subfolder 'dir1'
mov eax, [init_file_links.free]
test eax, eax
jz .realloc
add eax, [hOut]
push dword [eax]
pop [init_file_links.free]
jmp .allocated
.realloc:
; there is no free space, so reallocate [hOut] block
mov eax, [hOut]
sub [esp], eax ; make pointers relative
sub edx, eax
sub edi, eax
push ecx
mov ecx, [hOut.allocated]
add ecx, [esp+12+4]
mov [hOut.allocated], ecx
push ecx
and ecx, 0xFFF
cmp ecx, [esp+16+4]
pop ecx
ja @f
push edx
mov edx, eax
call [pgrealloc]
pop edx
test eax, eax
jnz @f
mov ecx, [hOut]
call [pgfree]
mov esp, [_esp]
or eax, -1
ret
@@:
pop ecx
mov [hOut], eax
add [esp], eax ; make pointers absolute
add edx, eax
add edi, eax
add eax, [hOut.allocated]
sub eax, [esp+8+4]
.allocated:
; eax -> new item
mov [eax+file_common.bIsDirectory], 1
mov [eax+file_common.bPseudoFolder], 1
.newitem:
mov [eax+file_common.namelen], ecx
; !!! in this case .fullname is not null-terminated !!!
mov ecx, [edx+file_common.fullname]
mov [eax+file_common.fullname], ecx
push edi eax
lea edi, [eax+file_common.parent]
xor eax, eax
push 7
pop ecx
rep stosd
pop eax edi
pop ecx
pop esi
; ecx = parent item, eax = current item
mov [eax+file_common.name], esi
inc dword [ecx+16] ; new item in parent folder
push ecx
; add new item to end of L2-list
cmp [eax+file_common.bIsDirectory], 0
jnz @f
add ecx, 8
@@:
push eax
sub eax, [hOut]
cmp dword [ecx], 0
jnz @f
mov [ecx], eax
@@:
xchg eax, [ecx+4]
xchg eax, ecx
pop eax
mov [eax+file_common.prev], ecx
jecxz @f
add ecx, [hOut]
sub eax, [hOut]
mov [ecx+file_common.next], eax
add eax, [hOut]
@@:
pop ecx
; set parent link
cmp ecx, edi
jz @f
sub ecx, file_common.subfolders
sub ecx, [hOut]
mov [eax+file_common.parent], ecx
@@:
; set current directory to current item
lea ecx, [eax+file_common.subfolders]
; if that was not last component, continue parse name
add esi, [eax+file_common.namelen]
lodsb
test al, al
jnz .parseloop
.mainloopcont:
; continue main loop
add edx, [esp+4]
jmp .mainloop
.mainloopdone:
; Loop done.
ret 4
; This subroutine is called by getattr and open.
; This subroutine looks for file name and returns NULL or pointer to file info record.
lookup_file_name:
mov ebp, [esp+8] ; hPlugin
mov esi, [esp+12] ; filename
lea edi, [ebp+handle_common.root.subfolders]
xor eax, eax
; KFar operates with absolute names, skip first '/'
cmp byte [esi], '/'
jnz .notfound
inc esi
.mainloop:
; get next component of name
push -1
pop ecx
@@:
inc ecx
cmp byte [esi+ecx], '/'
jz @f
cmp byte [esi+ecx], 0
jnz @b
@@:
; esi->component, ecx=length
; scan for required item in subfolders list
push -1
mov eax, [edi] ; .subfolders
.scan1:
test eax, eax
jz .notfound1
add eax, ebp
cmp [eax+file_common.namelen], ecx
jnz .cont1
push ecx esi edi
mov edi, [eax+file_common.name]
repz cmpsb
pop edi esi ecx
jz .found1
.cont1:
mov eax, [eax+file_common.next]
jmp .scan1
.notfound1:
pop edx
; if this is last component in file name, scan in subfiles list
cmp byte [esi+ecx], al
jnz .notfound
inc edx
jnz .notfound
mov eax, [edi+8] ; .subfiles
push edx
jmp .scan1
.found1:
pop edi
; item is found, go to next component
lea edi, [eax+file_common.subfolders]
lea esi, [esi+ecx+1]
cmp byte [esi-1], 0
jnz .mainloop
; this was the last component
.notfound:
ret
; Memory streams handling.
; Archive handlers create memory stream for small files:
; size of which is not greater than (free RAM size)/4 and
; not greater than following constant...
;LIMIT_FOR_MEM_STREAM = 2*1024*1024
; ...if it is defined. Now the definition is commented:
; if user has many physical memory, why not to use it?
virtual at 0
mem_stream:
.type dd ? ; type_mem_stream
.size dd ?
.pos dd ?
.buf:
end virtual
; unsigned __stdcall read(ebx = HANDLE hFile, void* buf, unsigned size);
read_mem_stream:
mov eax, [esp+12]
mov ecx, [ebx+mem_stream.size]
sub ecx, [ebx+mem_stream.pos]
jnc @f
xor ecx, ecx
@@:
cmp eax, ecx
jb @f
mov eax, ecx
@@:
mov ecx, eax
lea esi, [ebx+mem_stream.buf]
add esi, [ebx+mem_stream.pos]
add [ebx+mem_stream.pos], eax
mov edi, [esp+8]
mov edx, ecx
shr ecx, 2
rep movsd
mov ecx, edx
and ecx, 3
rep movsb
ret 12
; void __stdcall setpos(ebx = HANDLE hFile, __int64 pos);
setpos_mem_stream:
mov eax, [esp+8]
mov [ebx+mem_stream.pos], eax
ret 12
; void __stdcall close(ebx = HANDLE hFile);
close_mem_stream:
mov ecx, ebx
call [pgfree]
ret 4
; Allocate handle for file
; esi -> handle table, ecx = size of handle
alloc_handle:
; Handle table is L2-list of allocated pages.
; Scan for free entry
mov edx, esi
@@:
mov edx, [edx]
cmp edx, esi
jz .alloc_new
mov eax, [edx+8] ; head of L1-list of free entries
test eax, eax ; has free entry?
jz @b
; we have found allocated page with free entry; allocate entry and return
inc dword [edx+12] ; number of busy entries
push dword [eax]
pop dword [edx+8]
.ret:
ret
.alloc_new:
; no free pages; get new page and initialize
push ecx
mov ecx, 0x1000
call [pgalloc]
pop ecx
test eax, eax
jz .ret
; insert new page to start of L2-list
mov edx, [esi]
mov [eax], edx
mov [esi], eax
mov [eax+4], esi
mov [edx+4], eax
mov dword [eax+12], 1 ; 1 allocated entry
; initialize list of free entries
lea edx, [eax+16]
push edx ; save return value
add edx, ecx
mov [eax+8], edx
add eax, 0x1000
@@:
mov esi, edx
add edx, ecx
mov [esi], edx
cmp edx, eax
jb @b
and dword [esi], 0
pop eax
ret
; Free handle allocated in previous procedure
; esi = handle
free_handle:
mov ecx, esi
and ecx, not 0xFFF ; get page
; add entry to head of L1-list of free entries
mov eax, [ecx+8]
mov [esi], eax
mov [ecx+8], esi
dec dword [ecx+12] ; decrement number of allocated entries
jnz .ret
; delete page from common L2-list
mov eax, [ecx]
mov edx, [ecx+4]
mov [eax+4], edx
mov [edx], eax
; free page
call [pgfree]
.ret:
ret
; Ask user to enter password.
; Out: ZF set <=> user pressed Esc
; 'password_ansi', 'password_unicode', 'password_size' filled
query_password:
cmp [bPasswordDefined], 0
jnz .ret
mov edi, password_data
mov eax, password_maxlen
stosd ; maximum length
xor eax, eax
stosd ; start of visible part
stosd ; position of cursor
stosb ; initial state: empty string
mov eax, [cur_console_size]
mov eax, [eax] ; get current console width
sub eax, 12
mov edi, password_dlg
mov [edi+password_dlg.width-password_dlg], eax
dec eax
dec eax
mov [edi+password_dlg.width1-password_dlg], eax
mov [edi+password_dlg.width2-password_dlg], eax
push edi
call [DialogBox]
inc eax
jz .ret
; convert ANSI-cp866 to UNICODE string; also calculate 'password_size'
mov esi, password_ansi
mov edi, password_unicode
or [password_size], -1
.cvt:
inc [password_size]
lodsb
mov ah, 0
; 0x00-0x7F - trivial map
cmp al, 0x80
jb .symb
; 0x80-0xAF -> 0x410-0x43F
cmp al, 0xB0
jae @f
add ax, 0x410-0x80
jmp .symb
@@:
; 0xE0-0xEF -> 0x440-0x44F
cmp al, 0xE0
jb .unk
cmp al, 0xF0
jae @f
add ax, 0x440-0xE0
jmp .symb
@@:
; 0xF0 -> 0x401
; 0xF1 -> 0x451
cmp al, '<27>'
jz .yo1
cmp al, '<27>'
jz .yo2
.unk:
mov al, '_'
jmp .symb
.yo1:
mov ax, 0x401
jmp .symb
.yo2:
mov ax, 0x451
.symb:
stosw
test al, al
jnz .cvt
inc [bPasswordDefined] ; clears ZF flag
.ret:
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;; API for other programs ;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; void* __stdcall deflate_unpack(void* packed_data, unsigned* pLength)
deflate_unpack:
push dword [esp+8]
push esp
push deflate_unpack_cb
call deflate_unpack2
ret 8
deflate_unpack_cb:
mov edx, [esp+4]
push edx
mov edx, [edx+12]
mov edx, [edx]
mov eax, [esp+8+4]
mov [eax], edx
pop edx
xor eax, eax
xchg eax, [edx+8]
ret 8
; void* __stdcall deflate_unpack2(void* get_next_chunk, void* parameter, unsigned* pUnpackedLength)
; void* __stdcall get_next_chunk(void* parameter, unsigned* pLength)
deflate_unpack2:
pusha
mov ecx, 0x11000
call mypgalloc
test eax, eax
jz .ret
or dword [eax+streamInfo.fullSize], -1
or dword [eax+streamInfo.fullSize+4], -1
and dword [eax+streamInfo.bufSize], 0
mov dword [eax+streamInfo.fillBuf], .fillBuf
mov edx, [esp+32+4]
mov [eax+streamInfo.size], edx
mov edx, [esp+32+8]
mov [eax+streamInfo.size+4], edx
mov [eax+streamInfo.size+8+deflate_decoder.inStream], eax
add eax, streamInfo.size+8
lea edi, [eax+deflate_decoder.size]
mov [eax+streamInfo.bufPtr], edi
mov ebp, eax
call deflate_init_decoder
xor edx, edx
mov ecx, 10000h
.loop:
push @f
pushad
mov [_esp], esp
mov [_ebp], ebp
mov [error_proc], .error
mov ecx, 10000h
jmp [eax+streamInfo.fillBuf]
@@:
push 68
pop eax
push 20
pop ebx
int 0x40
test eax, eax
jz .nomem
mov edx, eax
mov eax, ebp
mov esi, edi
push edi
lea edi, [edx+ecx-0x10000]
push ecx
mov ecx, 0x10000/4
rep movsd
pop ecx
pop edi
add ecx, 0x10000
jmp .loop
.error:
pop eax
push edi
popad
pop eax
mov eax, ebp
lea esi, [eax+deflate_decoder.size]
sub ecx, 0x10000
sub edi, esi
add ecx, edi
mov eax, [esp+32+12]
test eax, eax
jz @f
mov [eax], ecx
@@:
test ecx, ecx
jnz @f
inc ecx
@@:
push 68
pop eax
push 20
pop ebx
int 0x40
test eax, eax
jz .nomem
sub ecx, edi
mov edx, edi
lea edi, [eax+ecx]
mov ecx, edx
shr ecx, 2
rep movsd
mov ecx, edx
and ecx, 3
rep movsb
push eax
push 68
pop eax
push 13
pop ebx
lea ecx, [ebp-streamInfo.size-8]
int 40h
pop eax
jmp .ret
.nomem:
push 68
pop eax
push 13
pop ebx
test edx, edx
jz @f
mov ecx, edx
push eax
int 0x40
pop eax
@@:
lea ecx, [ebp-streamInfo.size-8]
int 0x40
xor eax, eax
.ret:
mov [esp+28], eax
popa
ret 12
.fillBuf:
push eax
push eax
push esp
push dword [eax+streamInfo.size+4]
call dword [eax+streamInfo.size]
pop edx
pop ebp
mov [ebp+streamInfo.bufPtr], eax
and [ebp+streamInfo.bufDataLen], 0
test eax, eax
jz @f
mov [ebp+streamInfo.bufDataLen], edx
@@:
popad
ret
mypgalloc:
push 68
pop eax
push 12
pop ebx
int 0x40
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;; Initialized data ;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; export table
align 4
EXPORTS:
dd aVersion, 3
dd aPluginLoad, plugin_load
dd aOpenFilePlugin,OpenFilePlugin
dd aClosePlugin, ClosePlugin
dd aReadFolder, ReadFolder
dd aSetFolder, SetFolder
dd aGetFiles, GetFiles
dd aGetOpenPluginInfo, GetOpenPluginInfo
dd aGetattr, mygetattr
dd aOpen, myopen
dd aRead, myread
dd aSetpos, mysetpos
dd aClose, myclose
dd aDeflateUnpack, deflate_unpack
dd aDeflateUnpack2,deflate_unpack2
dd 0
; exported names
aVersion db 'version',0
aPluginLoad db 'plugin_load',0
aOpenFilePlugin db 'OpenFilePlugin',0
aClosePlugin db 'ClosePlugin',0
aReadFolder db 'ReadFolder',0
aSetFolder db 'SetFolder',0
aGetFiles db 'GetFiles',0
aGetOpenPluginInfo db 'GetOpenPluginInfo',0
aGetattr db 'getattr',0
aOpen db 'open',0
aRead db 'read',0
aSetpos db 'setpos',0
aClose db 'close',0
aDeflateUnpack db 'deflate_unpack',0
aDeflateUnpack2 db 'deflate_unpack2',0
; common strings
if lang eq ru
aContinue db '<27><EFBFBD><E0AEA4><EFBFBD><EFBFBD><EFBFBD><EFBFBD>',0
aCancel db '<27><EFBFBD><E2ACA5>',0
aHeaderError db '<27><EFBFBD><E8A8A1> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>',0
aReadError db '<27><EFBFBD><E8A8A1> <20><EFBFBD><E2A5AD>',0
aNoFreeRam db '<27><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>筮 ᢮<><E1A2AE><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E2A8A2><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>',0
aEncodingProblem db '<27><EFBFBD><E0AEA1><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><E0AEA2><EFBFBD>',0
aEncodingProblem_str db '<27><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><><E4A0A9><EFBFBD> <20> <20><><EFBFBD><><EFBFBD><><E1A8AC><EFBFBD><EFBFBD>,',0
.2 db '<27><> <20><EFBFBD><EFBFBD><E2A0A2><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><E0AEA2> cp866.',0
.3 db '<27><><EFBFBD><><E1A8AC><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E0AAA8><EFBFBD><EFBFBD><EFBFBD>.',0
aEnterPassword db '<27><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>:',0
aEnterPasswordLen = $ - aEnterPassword - 1
aEnterPasswordTitle db '<27><><EFBFBD><EFBFBD> <20><><EFBFBD>',0
aArchiveDataError db '<27><EFBFBD><E8A8A1> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>',0
aArchiveDataErrorPass db '<27><EFBFBD><E8A8A1> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>',0
aChangePass db '<27><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>',0
aNameTooLong db '<27><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>',0
aCannotOpenFile db '<27><> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><>',0
else
aContinue db 'Continue',0
aCancel db 'Cancel',0
aHeaderError db 'Invalid archive header',0
aReadError db 'Read error',0
aNoFreeRam db 'There is not enough of free RAM',0
aEncodingProblem db 'Encoding problem',0
aEncodingProblem_str db 'The names of some files in the archive contain',0
.2 db 'characters which can not be represented in cp866.',0
.3 db 'Such characters will be replaced to underscores.',0
aEnterPassword db 'Enter password:',0
aEnterPasswordLen = $ - aEnterPassword - 1
aEnterPasswordTitle db 'Get password',0
aArchiveDataError db 'Error in archive data',0
aArchiveDataErrorPass db 'Error in archive data or incorrect password',0
aChangePass db 'Enter password',0
aNameTooLong db 'Name is too long',0
aCannotOpenFile db 'Cannot open file',0
end if
; kfar_arc supports many archive types.
; OpenFilePlugin looks for supported archive signature and gives control
; to concrete handler if found.
; Other functions just determine type of opened archive and jump to corresponding handler.
type_mem_stream = 0 ; memory stream - for file handles (returned from 'open')
type_7z = 1
type_zip = 2
; archive functions (types start from type_7z)
align 4
ClosePluginTable:
dd close_7z
dd close_zip
getattrTable:
dd getattr_7z
dd getattr_zip
openTable:
dd open_file_7z
dd open_file_zip
basesizes:
dd handle_7z.basesize, file_in_7z.size
dd handle_zip.basesize, file_in_zip.size
; file functions (types start from type_mem_stream)
readTable:
dd read_mem_stream
dd read_7z
dd read_zip
setposTable:
dd setpos_mem_stream
dd setpos_7z
dd setpos_zip
closeTable:
dd close_mem_stream
dd close_file_7z
dd close_file_zip
; pointers for SayErr and Message
ContinueBtn dd aContinue
HeaderError_ptr dd aHeaderError
aReadError_ptr dd aReadError
aNoFreeRam_ptr dd aNoFreeRam
aEncodingProblem_str_ptr:
dd aEncodingProblem_str
dd aEncodingProblem_str.2
dd aEncodingProblem_str.3
aNameTooLong_ptr dd aNameTooLong
aArchiveDataError_ptr dd aArchiveDataError
aArchiveDataErrorPass_ptr dd aArchiveDataErrorPass
CancelPassBtn dd aCancel
dd aChangePass
; "enter password" dialog for KFar
password_dlg:
dd 1 ; use standard dialog colors
dd -1 ; center window by x
dd -1 ; center window by y
.width dd ? ; width (will be filled according to current console width)
dd 2 ; height
dd 4, 2 ; border size
dd aEnterPasswordTitle ; title
dd ? ; colors (will be set by KFar)
dd 0 ; used internally by dialog manager, ignored
dd 0, 0 ; reserved for DlgProc
dd 2 ; 2 controls
; the string "enter password"
dd 1 ; type: static
dd 1,0 ; upper-left position
.width1 dd ?,0 ; bottom-right position
dd aEnterPassword ; data
dd 0 ; flags
; editbox for password
dd 3 ; type: edit
dd 1,1 ; upper-left position
.width2 dd ?,0 ; bottom-right position
dd password_data ; data
dd 2Ch ; flags
IncludeIGlobals
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;; Uninitialized data ;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
section '.udata' data readable writable align 16
kfar_info dd ?
crc_table rd 256
_esp dd ?
_ebp dd ?
bufsize dd ?
bufptr dd ?
bufend dd ?
buffer rb 1024
inStream dd ?
hOut dd ?
.allocated dd ?
error_proc dd ?
clear_proc dd ?
; import from kfar
open2 dd ?
filesize dd ?
read dd ?
seek dd ?
close dd ?
pgalloc dd ?
pgrealloc dd ?
pgfree dd ?
getfreemem dd ?
DialogBox dd ?
SayErr dd ?
Message dd ?
cur_console_size dd ?
; data for editbox in kfar dialog
password_maxlen = 512
password_data:
.maxlen dd ?
.pos dd ?
.start dd ?
password_ansi rb password_maxlen+1
bPasswordDefined db ?
; converted password
password_unicode rw password_maxlen+1
password_size dd ?
IncludeUGlobals
bWasWarning db ?