; Copyright (c) 2008-2009, diamond
; All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions are met:
;       * Redistributions of source code must retain the above copyright
;       notice, this list of conditions and the following disclaimer.
;       * Redistributions in binary form must reproduce the above copyright
;       notice, this list of conditions and the following disclaimer in the
;       documentation and/or other materials provided with the distribution.
;       * Neither the name of the <organization> nor the
;       names of its contributors may be used to endorse or promote products
;       derived from this software without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka <Lrz> ''AS IS'' AND ANY
; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
; DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;*****************************************************************************

restore_usa:
; Update Sequence Array restore
; in: ds:bx -> USA-protected structure
	push	bx
	lea	di, [bx+1feh]
	mov	cx, [bx+6]
	add	bx, [bx+4]
	dec	cx
@@:
	mov	ax, [bx+2]
	mov	[di], ax
	inc	bx
	inc	bx
	add	di, 200h
	loop	@b
	pop	bx
	ret

find_attr:
; in: ds:di->file record, ax=attribute
; out: ds:di->attribute or di=0 if not found
	add	di, [di+14h]
.1:
; attributes' codes are formally dwords, but all of them fit in word
	cmp	word [di], -1
	jz	.notfound
	cmp	word [di], ax
	jnz	.continue
; for $DATA attribute, scan only unnamed
	cmp	ax, 80h
	jnz	.found
	cmp	byte [di+9], 0
	jz	.found
.continue:
	add	di, [di+4]
	jmp	.1
.notfound:
	xor	di, di
.found:
	ret

process_mcb_nonres:
; in: ds:si->attribute, es:di->buffer
; out: es:di->buffer end
	pushad
	pop	di
	add	si, [si+20h]
	xor	ebx, ebx
.loop:
	lodsb
	test	al, al
	jz	.done
	push	invalid_read_request_string
	movzx	cx, al
	shr	cx, 4
	jz	find_error_sp
	xchg	ax, dx
	and	dx, 0Fh
	jz	find_error_sp
	add	si, cx
	add	si, dx
	pop	ax
	push	si
	dec	si
	movsx	eax, byte [si]
	dec	cx
	jz	.l1e
.l1:
	dec	si
	shl	eax, 8
	mov	al, [si]
	loop	.l1
.l1e:
	xchg	ebp, eax
	dec	si
	movsx	eax, byte [si]
	mov	cx, dx
	dec	cx
	jz	.l2e
.l2:
	dec	si
	shl	eax, 8
	mov	al, byte [si]
	loop	.l2
.l2e:
	pop	si
	add	ebx, ebp
; eax=length, ebx=disk block
	stosd
	mov	eax, ebx
	stosd
	cmp	di, 0x8000 - 12
	jbe	.loop
..attr_overflow:
	mov	si, fragmented_string
	jmp	find_error_si
.done:
	xor	ax, ax
	stosw
	stosw
	push	di
	popad
	ret

load_attr:
; in: ax=attribute, ds:bx->base record
; out: if found: CF=0, attribute loaded to [freeattr], [freeattr] updated,
;	edx=size of attribute in bytes
; out: if not found: CF=1
	mov	di, [bp + freeattr - dat]
	push	ss
	pop	es
	mov	byte [es:di], 1
	inc	di
	cmp	di, 0x8000 - 12
	ja	..attr_overflow
	or	edx, -1		; file size is not known yet
; scan for attribute
	push	di
	mov	di, bx
	add	di, [di+14h]
@@:
	call	find_attr.1
	test	di, di
	jz	.notfound1
	cmp	byte [di+8], 0
	jnz	.nonresident
	mov	si, di
	pop	di
	push	ds
	jmp	.resident
.aux_resident:
	mov	ax, ds
	mov	si, di
	pop	di ds bx ds edx
	push	ss
	pop	es
	push	ds
	mov	ds, ax
; resident attribute
.resident:
	dec	di
	mov	al, 0
	stosb
	mov	ax, [si+10h]
	stosw
	push	di
	add	di, ax
	cmp	di, 0x8000 - 12
	pop	di
	ja	..attr_overflow
	movzx	edx, ax		; length of attribute
	xchg	ax, cx
	add	si, [si+14h]
	rep	movsb
	mov	[bp + freeattr - dat], di
	pop	ds
	ret
.nonresident:
; nonresident attribute
	cmp	dword [di+10h], 0
	jnz	@b
; read start of data
	mov	si, di
	mov	edx, [di+30h]	; size of attribute
	pop	di
	call	process_mcb_nonres
	sub	di, 4
	push	di
.notfound1:
	pop	di
	push	edx
; $ATTRIBUTE_LIST is always in base file record
	cmp	ax, 20h
	jz	.nofragmented
; try to load $ATTRIBUTE_LIST = 20h
	push	ax
	mov	ax, 20h
	push	[bp + freeattr - dat]
	mov	[bp + freeattr - dat], di
	push	di
	call	load_attr
	pop	di
	pop	[bp + freeattr - dat]
	pop	ax
	jc	.nofragmented
	push	ds bx
	pusha
	mov	si, di
	push	ss
	pop	ds
	push	0x8100
	pop	es
	xor	ecx, ecx
	mov	cl, 0x78
	xor	bx, bx
	push	es
	call	read_file_chunk
	pop	ds
	jc	..found_disk_error
	test	cx, cx
	jz	..attr_overflow
	popa
	push	ss
	pop	es
	xor	bx, bx
.1:
	cmp	[bx], ax
	jnz	.continue1
; only unnamed $DATA attributes!
	cmp	ax, 80h
	jnz	@f
	cmp	byte [bx+6], 0
	jnz	.continue1
@@:
	cmp	dword [bx+10h], 0
	jz	.continue1
	cmp	dword [bx+8], 0
	jnz	@f
	dec	di
	cmp	di, [bp + freeattr - dat]
	lea	di, [di+1]
	jnz	.continue1
@@:
	push	ds di
	push	ax
	mov	eax, [bx+10h]
	mov	ecx, [bx+8]
	call	read_file_record
	pop	ax
	mov	di, [14h]
.2:
	call	find_attr.1
	cmp	byte [di+8], 0
	jz	.aux_resident
	cmp	dword [di+10h], ecx
	jnz	.2
	mov	si, di
	mov	di, sp
	cmp	dword [ss:di+8], -1
	jnz	@f
	push	dword [si+30h]	; size of attribute
	pop	dword [ss:di+8]
@@:
	pop	di
	call	process_mcb_nonres
	sub	di, 4
	pop	ds
.continue1:
	add	bx, [bx+4]
	cmp	bx, dx
	jb	.1
	pop	bx ds
.nofragmented:
	pop	edx
	dec	di
	cmp	di, [bp + freeattr - dat]
	jnz	@f
	stc
	ret
@@:
	inc	di
	xor	ax, ax
	stosw
	stosw
	mov	[bp + freeattr - dat], di
	ret

read_file_record:
; in: eax = index of record
; out: ds:0 -> record
; find place in cache
	push	di
	push	si
	mov	si, cache1head
	call	cache_lookup
	pop	si
	pushf
	sub	di, 3400h
	shl	di, 10-3
	add	di, 0x6000
	mov	ds, di
	popf
	pop	di
	jnc	.noread
; read file record <eax> to ds:0
	pushad
	push	ds
	push	es
	movzx	ecx, [bp + frs_size - dat]
	shr	cx, 9
	mul	ecx
	push	ds
	pop	es
	push	ss
	pop	ds
	mov	si, 0x4000
	xor	bx, bx
	push	[bp + cur_obj - dat]
	mov	[bp + cur_obj - dat], mft_string
	push	es
	call	read_attr
; initialize cache for $INDEX_ALLOCATION for this record
	pop	si
	push	si
	sub	si, 0x6000
	mov	ax, si
	shr	si, 10-3
	shr	ax, 2
	add	si, 3480h
	add	ax, 3500h
	mov	[si], si
	mov	[si+2], si
	mov	[si+4], ax
	pop	ds
	call	restore_usa
	pop	[bp + cur_obj - dat]
	pop	es
	pop	ds
	popad
.noread:
	ret

read_attr:
; in: eax = offset in sectors, ecx = size in sectors (<10000h), es:bx -> buffer, ds:si -> attribute
	push	invalid_read_request_string
	cmp	byte [si], 0
	jnz	.nonresident
	cmp	eax, 10000h shr 9
	jae	find_error_sp
	shl	ax, 9
	shl	cx, 9
	cmp	ax, [si+2]
	jae	find_error_sp
	cmp	cx, [si+2]
	ja	find_error_sp
	add	si, 3
	add	si, ax
	mov	di, bx
	rep	movsb
	pop	ax
	ret
.nonresident:
	inc	si
.loop:
	mov	edx, dword [si]
	add	si, 8
	test	edx, edx
	jz	find_error_sp
	imul	edx, [bp + sect_per_clust - dat]
	sub	eax, edx
	jnc	.loop
	add	eax, edx
	sub	edx, eax
	push	cx
	cmp	ecx, edx
	jb	@f
	mov	cx, dx
@@:
	push	bx
	mov	ebx, [si-4]
	imul	ebx, [bp + sect_per_clust - dat]
	add	eax, ebx
	pop	bx
	call	read
	jc	..found_disk_error
	mov	dx, cx
	pop	cx
	xor	eax, eax
	sub	cx, dx
	jnz	.loop
	pop	ax
	ret

load_file_ntfs:
; in: ss:bp = 0:dat
; in: es:bx = address to load file
; in: ds:si -> ASCIIZ name
; in: cx = limit in sectors
; out: bx = status: bx=0 - ok, bx=1 - file is too big, only part has been loaded, bx=2 - file not found
; out: dx:ax = file size (0xFFFFFFFF if file not found)
	push	es bx cx
	mov	eax, 5		; root cluster
	mov	[bp + cur_obj - dat], root_string
.parse_dir_loop:
	push	ds si
	call	read_file_record
; find attributes $INDEX_ROOT, $INDEX_ALLOCATION, $BITMAP
	mov	ax, [bp + freeattr - dat]
	mov	[bp + index_root - dat], ax
	mov	ax, 90h		; $INDEX_ROOT
	xor	bx, bx
	call	load_attr
	mov	si, noindex_string
	jc	find_error_si
	mov	ax, [bp + freeattr - dat]
	mov	[bp + index_alloc - dat], ax
	mov	ax, 0A0h	; $INDEX_ALLOCATION
	call	load_attr
	jnc	@f
	mov	[bp + index_alloc - dat], bx
@@:
	push	ds
; search for entry
	mov	si, [bp + index_root - dat]
	push	ss
	pop	ds
	push	0x8100
	pop	es
	xor	ecx, ecx
	mov	cl, 0x78
	xor	bx, bx
	push	es
	call	read_file_chunk
	pop	ds
	jc	..found_disk_error
	test	cx, cx
	jz	..attr_overflow
	mov	si, invalid_read_request_string
	cmp	word [bx+10], 0
	jnz	find_error_si
; calculate number of items in cache
	mov	di, [bx+8]	; subnode_size
	mov	ax, 0x4000
	sub	ax, word [bp + frs_size - dat]
	cwd
	div	di
	test	ax, ax
	jz	find_error_si
	mov	si, invalid_volume_msg
	test	di, 0x1FF
	jnz	find_error_si
	pop	cx
	mov	[bp + cur_index_seg - dat], cx
	shl	ax, 3
	sub	cx, 6000h
	mov	si, cx
	shr	cx, 2
	shr	si, 10-3
	add	cx, ax
	add	si, 3480h
	mov	[bp + cur_index_cache - dat], si
	add	cx, 3500h
	mov	[ss:si+6], cx
	mov	dx, di
	add	bx, 10h
.scan_record:
	add	bx, [bx]
.scan:
	test	byte [bx+0Ch], 2
	jnz	.look_child
	movzx	cx, byte [bx+50h]	; namelen
	lea	di, [bx+52h]		; name
	push	ds
	pop	es
	pop	si ds
	push	ds si
	xor	ax, ax
.1:
	lodsb
	cmp	al, '/'
	jnz	@f
	mov	al, 0
@@:
	cmp	al, 'A'
	jb	.nocapital
	cmp	al, 'Z'
	ja	.nocapital
	or	al, 20h
.nocapital:
	cmp	al, 'a'
	jb	.notletter
	cmp	al, 'z'
	ja	.notletter
	or	byte [es:di], 20h
.notletter:
	scasw
	loopz	.1
	jb	.look_child
	ja	@f
	cmp	byte [si], 0
	jz	.file_found
	cmp	byte [si], '/'
	jz	.file_found
@@:
	push	es
	pop	ds
	add	bx, [bx+8]
	jmp	.scan
.look_child:
	push	es
	pop	ds
	test	byte [bx+0Ch], 1
	jz	.not_found
	mov	si, [bp + index_alloc - dat]
	test	si, si
	jz	.not_found
	add	bx, [bx+8]
	mov	eax, [bx-8]
	mov	es, [bp + cur_index_seg - dat]
	push	si
	mov	si, [bp + cur_index_cache - dat]
	call	cache_lookup
	pop	si
	pushf
	mov	bx, di
	mov	bh, 0
	shr	bx, 3
	imul	bx, dx
	add	bx, [bp + frs_size - dat]
	popf
	jnc	.noread
	push	es
	push	dx
	push	ss
	pop	ds
	movzx	ecx, dx
	shr	cx, 9
	mul	[bp + sect_per_clust - dat]
	call	read_attr
	pop	dx
	pop	es
	push	es
	pop	ds
	call	restore_usa
.noread:
	push	es
	pop	ds
	add	bx, 18h
	jmp	.scan_record
.not_found:
	pop	[bp + cur_obj - dat]
	mov	si, error_not_found
	jmp	find_error_si
.file_found:
	pop	[bp + cur_obj - dat]
	pop	cx
	mov	ax, [bp + index_root - dat]
	mov	[bp + freeattr - dat], ax
	mov	eax, [es:bx]
	test	byte [es:bx+48h+3], 10h
	jz	.regular_file
	cmp	byte [si], 0
	jz	..directory_error
	inc	si
	jmp	.parse_dir_loop
.regular_file:
	cmp	byte [si], 0
	jnz	..notdir_error
; read entry
	call	read_file_record
	xor	bx, bx
	mov	ax, 80h
	call	load_attr
	mov	si, nodata_string
	jc	find_error_si
	mov	si, [bp + index_root - dat]
	mov	[bp + freeattr - dat], si
	push	ss
	pop	ds
	jmp	load_file_common_end
