;Filter template
;converts input and write it to stdout

include '..\fasm'
;------------------------------------------------------------------------
InputHandle	dd 0
OutputHandle	dd 0
bytesread	dd 0
byteswritten	dd 0

;Help messages
SyntaxError	db 'Program to write file from input to console, converting #ascii to binary',13,10
		db 'Usage: Filter input',13,10
		db 'Input: will read redirected input if filename not specified on commandline',0

;------------------------------------------------------------------------
Start:
	;Get output device
	;-----------------
	invoke	GetStdHandle, STD_OUTPUT_HANDLE
		;kernel32.GetStdHandle(nStdHandle)
		;nStdHandle can be
		;   STD_INPUT_HANDLE  0FFFFFFF6 (-10)	console input buffer
		;   STD_OUTPUT_HANDLE 0FFFFFFF5 (-11)	console active screen buffer
		;   STD_ERROR_HANDLE  0FFFFFFF4 (-12)	console active screen buffer
		;returns EAX = INVALID_HANDLE_VALUE (0FFFFFFFF or -1) if function fails
		;
		;All handles returned have GENERIC_READ and GENERIC_WRITE access unless
		;the SetStdHandle function has been used to set a standard handle to be
		;some handle with a lesser access. 
		;
		;The standard handles of a process may be redirected by a call to
		;SetStdHandle, in which case GetStdHandle returns the redirected handle.
		;If a standard handle has been redirected, you can specify the CONIN$
		;value in a call to the CreateFile function to get a handle of a
		;console's input buffer or specify the CONOUT$ value to get a handle of
		;a console's active screen buffer. 
	mov	[OutputHandle],eax

.getarg:
	;Get input parameters
	;--------------------
	invoke	GetCommandLine
		;kernel32.GetCommandLine()
		;EAX -> command line string made of program invocation name + arguments terminated by nul (no preceding length byte as in DOS).
		;The invocation name will be LFN path\prog.exe if ran via explorer

	;Move to argument
	;----------------
	;While length of argument not zero
	;.1 skip invocation name as follows:
	;   if first letter is quote 
	;	then skip until quote
	; 	else skip until space 
	;.2 skip blanks between invocation name and argument
	;	skip spaces

	call	strlen		;ECX = length of str in EAX
	add	ecx,eax		;ECX = end of string
	mov	dl,'"'
	cmp	[eax],dl
	je	.1
	mov	dl,' '

.1:	inc	eax
	cmp	[eax],dl
	jne	.1

.2:	inc	eax
	cmp	b [eax],' '
	je	.2

	cmp	eax,ecx
	jae	noarg

        ;Open input file
	;---------------
	invoke	CreateFile,eax,GENERIC_READ,FILE_SHARE_READ + FILE_SHARE_WRITE,0,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,0
	mov	[InputHandle],eax
	inc	eax			;cmp eax,INVALID_FILE_HANDLE (-1)
go_on:	mov	edi,inputbuffer		;prepare for read
	jnz	read

noarg:	;Try read input device if problem with commandline
	;-------------------------------------------------
	invoke	GetStdHandle, STD_INPUT_HANDLE
	mov	[InputHandle],eax
	invoke	GetFileType, eax
	cmp	al,FILE_TYPE_CHAR	;if has redirected input device
	jne	go_on			;then read from redirected device
	mov	eax,SyntaxError

error:	call	strlen			;ecx:=length of [eax]
	call	write			;invoke	WriteFile, [OutputHandle], eax, ecx, ADDR byteswritten, 0

quit:   invoke	ExitProcess,1

;-------------------------- MAIN PROGRAM --------------------------------
done:	call	writebuffer
	jmp	quit

flush:	call	writebuffer		;edi=inputbuffer
					;invoke	WriteFile, [OutputHandle], edi, ecx-inputbuffer, ADDR byteswritten, 0
	pop	ecx			;load end of buffer
	sub	ecx,esi			;copy the number of chars not processed to front of inputbuffer
	rep	movsb

read:	invoke	ReadFile, [InputHandle], edi, 32768, bytesread, 0
	mov	ecx,[bytesread]
	jecxz	done
	add	ecx,edi
	push	ecx			;save end of buffer
	sub	ecx,6			;ECX = set limit so reads are always within inputbuffer
	mov	esi,inputbuffer		;ESI = start of read
	
	;GBK processing
	;--------------
.lod:	cmp	esi,ecx
	jae	flush
	sub	eax,eax
	lodsb				;GBK character set
	stosb
	cmp	al,'#'
	jne	.lod
	dec	edi
	call	getdec
	xchg	edx,eax
	stosw
	jmp	.lod

;-------------------------------------------------------------------------------------------------------------
;SUBROUTINES
;-------------------------------------------------------------------------------------------------------------
getdec:
;SI -> start of decimal string (eg. 100) to convert => start of invalid string
;EDX = number converted
;AL destroyed (last digit - '0')
	sub	edx,edx
.sto:	sub	eax,eax
	lodsb
	sub	al,'0'
	cmp	al,10
	jnb	.z
	add	edx,edx	; 1 + 1 = 2
	add	eax,edx   
	shl	edx,2	; 2 * 4 = 8	;ADD CX,CX  ADD CX,CX if not 286+
	add	edx,eax	; 8 + 2 = 10
	jmp	.sto
.z:	dec	esi
	ret

strlen:	mov	ecx,-1
.count:	inc	ecx
	cmp	b [eax + ecx],0
	jne	.count
	ret

writebuffer:
	mov	eax,inputbuffer
	mov	ecx,edi
	sub	ecx,eax
	mov	edi,eax
write:	invoke	WriteFile, [OutputHandle], eax, ecx, byteswritten, 0
		;kernel32.WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped)
		;Writes n bytes to the handle from Buffer, returns number of bytes written, EAX success (non-zero) or fail (0).
		;It also adjusts file pointer unless the file is opened with FILE_FLAG_OVERLAPPED.
		;
		;hFile		Handle to write to. Handle must have GENERIC_WRITE access
		;lpBuffer	pointer to source data
		;nBytesToWrite	if zero, only updates time stamp, but unlike MSDOS,
		;		does not truncate or end the file. Use SetEndOfFile
		;		to do so. Named pipe operations across a netword is
		;		limited to 65535 bytes.
		;lpBytesWritten	number of bytes written
		;lpOverlapped	pointer to structure needed for overlapped I/O
	ret

;------------------------------------------------------------------------
.end Start

;Uninitialized buffers
inputbuffer	rb 34000

