%include "util.mac"
%include "icedump.inc"
%include "wiat.inc"
%include "win32n.inc"


global Parse_Clip


extern sdata
extern Parser.return
extern Parser.error
extern Parser.errorMsg
extern Error_V86
extern Error_PM16
extern Error_PMR0
extern GetAPIs
extern API.oGlobalAlloc
extern API.oGlobalLock
extern API.oGlobalUnlock
extern API.oGlobalFree
extern API.oOpenClipboard
extern API.oGetClipboardData
extern API.oSetClipboardData
extern API.oCloseClipboard
extern API.oEmptyClipboard
extern CheckWCRS
extern ReinforceINT3
extern wcrs
extern ParseAddress
extern ParseExpression
extern Dword2Hex


bits 32


;-------------------------------------------------------------------------------
; CLIP [SubCommand] [Value]
;-------------------------------------------------------------------------------
segment _LTEXT
Parse_Clip:
	mov	edi,Error_V86
	mov	ebp,[dClient_EFLAGS]
	test	byte [ebp+2],2		; is client in V86 mode?
	jnz	near Parser.errorMsg

	mov	edi,Error_PM16
	mov	ebp,[dClient_CS]
	lar	eax,[ebp]		; is client 32 bit?
	bt	eax,22
	jnc	near Parser.errorMsg

	mov	edi,Error_PMR0
	test	byte [ebp],3		; is client in ring-0?
	jz	near Parser.errorMsg

	cld
	call	[pSkipWhiteSpace]	; skip to subcommand
	mov	edx, 0
	jz	.Sub2			; default (no subcommand)

	mov	al, [esi]		; get subcommand
	and	al,0x5F			; upcase

	cmp	al, 'A'
	je	.Sub0

	cmp	al, 'G'
	je	.Sub1

	cmp	al, 'P'
	je	.Sub2

	cmp	al, 'S'
	je	.Sub3

.Help:
	mov	ebp, [pPrintToCommandWindow]
	mov	esi, .TableHelp

.HelpShow:
	lodsd
	test	eax, eax
	jz	near Parser.return

	push	esi
	mov	esi, eax
	call 	ebp
	pop	esi
	jmp	short .HelpShow

.Sub3:	inc	edx
.Sub2:	inc	edx
.Sub1:	inc	edx
.Sub0:
	call	[pSkipWord]			; skip subcommand
	mov	ebx, [tKeyboardBuffer_2]	; default argument: winice clipboard
	mov	ecx, [dKeyboardBufferLength_2]
	mov	ecx, [ecx]
	jz	.ParamsDone			; no subcommand argument - use default

	mov	al, [esi]			; get first char of argument
	cmp	al, "'"				; check if it's a quote
	je	.ParamString
	cmp	al, '"'
	je	.ParamString			; yes - search of the end of quotes

	call	ParseAddress			; no - argument is address
	mov	edi, .errBadAddress
	jc	near Parser.errorMsg
	mov	ebx, eax			; address of memory block to pass to clipboard

	call	[pSkipWhiteSpace]		; skip to length
	jz	.ParamValue			; there's no length Arg --> Arg is not an address in fact

	call	ParseExpression			; parse length of memory block
	mov	edi, .errBadLength
	jc	near Parser.errorMsg
	mov	ecx, eax
	test	ecx, ecx			; is length non-zero?
	jnz	.ParamsDone			; yes, so it's the real length - use it

	mov	edi, ebx			; length is zero - search for end of string
	dec	ecx
	repnz	scasb
	not	ecx
	dec	ecx				; this is the real length
	jmp	.ParamsDone

.ParamString:
	inc	esi				; skip quote char
	xor	ecx, ecx			; no usable chars found yet
	mov	ebx, esi			; here the string starts
	mov	ah, al				; store our quote char in AH

.ParamStrLen:
	lodsb					; get next char
	test	al, al				; end of string?
	jz	.ParamsDone			; yes - get out
	cmp	al, ah				; is the char our quote?
	je	.ParamsDone			; yes - get out
	inc	ecx				; increment string length
	jmp	short .ParamStrLen

.ParamValue:					; no length argument --> argument is a value
	mov	ebx, .DwordBuf			; convert it to Hex
	push	ebx
	push	eax
	call	Dword2Hex
	mov	ecx, 8

.ParamsDone:
	mov	ebp, [dClient_ESI]		; pointer to new content
	mov	[ebp], ebx
	mov	ebp, [dClient_EDI]		; new content length
	mov	[ebp], ecx
	mov	ebp, [dClient_EBX]		; subcommand (client EBX)
	mov	[ebp], edx

	mov	ebp, [dClient_EIP]		; set client (E)IP
	mov	dword [ebp], Clipboard

	call	CheckWCRS
	call	ReinforceINT3

	mov	dword [wcrs+WCRS.oINT3], Clipboard.return
	mov	dword [wcrs+WCRS.valid], 1

	mov	ebp, [fExecuteMoreCommands]	; set internal Winice flag to 0
	mov	byte [ebp], 0

	popad
	retn

; ------------------------------------------------------------------------------
segment _LDATA
.TableHelp:
	dd	.helpCA
	dd	.helpCG
	dd	.helpCP
	dd	.helpCS
	dd	0
.helpCA:	db 'CLIP A [<text>] - Append SI clipboard / <text> to Win clipboard', 0
.helpCG:	db 'CLIP G          - Get Win clipboard and copy it to SI clipboard', 0
.helpCP:	db 'CLIP P          - Print Win clipboard content to console window', 0
.helpCS:	db 'CLIP S [<text>] - Set Win clipboard to SI clipboard / <text>', 0
.errBadAddress:	db 'invalid address.', 0
.errBadLength:	db 'invalid length.', 0
.DwordBuf:	db '00000000', 0


; ------------------------------------------------------------------------------
; this is where memory is allocated and windows clipboard set
; ------------------------------------------------------------------------------
Clipboard:
						; EBX = subcommand
						; ESI = pointer to new content (valid for cmds A/S)
						; EDI = new content length (valid for cmds A/S)
	cld
	push	byte 0
	call	[API.oOpenClipboard]		; open clipboard
	test	eax, eax
	jnz	.ClipOpened

	mov	esi, .msgErrOpen		; 'Error opening clipboard'
	jmp	.ErrMsg

.ClipOpened:
	xor	ecx, ecx			; length of Windows clipboard content to keep (copy) - 0 by default
	xor	edx, edx			; pointer to clipboard content to keep (copy) - none by default
	xor	ebp, ebp			; locked memory (none at the start)
	cmp	ebx, 3
	je	near .SetWClip			; "set windows clipboard" subcommand

						; subcommands 0, 1, 2
	push	byte CF_TEXT
	call	[API.oGetClipboardData]		; try to get clipboard data (as text)
	test	eax, eax
	jnz	.ClipGotData
	mov	esi, .msgErrGet			; 'Error getting clipboard data'
	jmp	.CloseClip

.ClipGotData:
	mov	ebp, eax			; hMem (to be locked)
	push	eax
	call	[API.oGlobalLock]		; lock clipboard memory
	test	eax, eax
	jnz	.ClipLockedData

	mov	esi, .msgErrLock		; 'Error locking clipboard memory'
	jmp	.CloseClip
	

.ClipLockedData:
	cmp	ebx, 2				; "print windows clipboard" subcommand
	jne	.GetLen

	push	ebp
	push	eax				; subcommand 2
	mov	edi, esp
	mov	esi, .msgStr			; print windows clipboard content
	mov	ax, 0x73			; DS_Printf
	int	0x41
	pop	eax
	pop	ebp
	xor	esi, esi			; no error
	jmp	.unlock

.GetLen:					; subcommands copying clipboard (0,1 - i.e. A,G)
	mov	edx, eax			; store pointer to Windows clipboard memory block
	xor	ecx, ecx
	dec	ecx
	push	edi				; store edi (length of content to add)
	mov	edi, eax			; points windows clipboard content
	xor	eax, eax			; search for the end of the content (i.e. string)
	repnz	scasb				; find the end of clipboard
	pop	edi				; restore length of content to add
	not	ecx
	dec	ecx				; ecx = length of current clipboard content

	cmp	ebx, 1
	jne	.SetWClip			; not "get windows clipboard"

	cmp	ecx, 160			; SoftIce Clipboard buffer length (SI 4.05!)
	jb	.Copy2Ice
	mov	ecx, 160

.Copy2Ice:
	mov	esi, edx			; pointer to Windows clipboard content
	mov	edi, [dKeyboardBufferLength_2]
	mov	[edi], ecx			; set the length of SoftIce clipboard content
	mov	edi, [tKeyboardBuffer_2]	; copy windows clipboard to softice clipboard
	rep	movsb				; copy Windows --> SoftIce
	xor	esi, esi			; no error
	jmp	.unlock

.SetWClip:
	mov	ebx, edx			; pointer to (current) windows clipboard content
	push	edi				; length of new clipboard content (to append)
	push	ecx				; length of current windows clipboard content (to keep)
	lea	ecx, [ecx+edi+3]		; size of memory to allocate (for new clipboard content)
	push	ecx
	push	dword GMEM_MOVEABLE + GMEM_DDESHARE
	call	[API.oGlobalAlloc]		; allocate new (moveable) memory
	test	eax, eax
	jnz	.WClipAllocated

	mov	esi, .msgErrAlloc		; 'Error allocating memory'
	jmp	.unlock

.WClipAllocated:
	mov	edi, eax			; hMem
	push	eax
	call	[API.oGlobalLock]
	test	eax, eax
	jnz	.WClipLocked

	mov	esi, .msgErrLock2		; 'Error locking new memory'
	jmp	.dealloc

.WClipLocked:
	xchg	eax, edi			; eax = handle of new memory block, edi = new memory block
	pop	ecx				; length of current windows clipboard contect (to keep)
	test	ebx, ebx
	xchg	esi, ebx			; ebx = pointer to the new block to be copied, esi = original memory block
	jz	.WCopied

	rep	movsb				; if there's something to keep in clipboard, copy it now

.WCopied:
	test	ebp, ebp			; was there anything kept from old clipboard context?
	xchg	eax, ebp			; eax = handle of original memory block, ebp = handle of new memory block
	jz	.WOrigDone

	push	eax				; yes - unlock the (original) memory
	call	[API.oGlobalUnlock]

.WOrigDone:
	pop	ecx				; length of new clipboard content (to append)
	mov	esi, ebx			; esi = pointer to new memry block (to append) again

	xor	edx, edx			; default: no error during moving the data
	xor	eax, eax
	call	.AfterSEH			; install SEH
.SEH:
	mov	eax, [esp+0Ch]
	mov	dword [eax+CONTEXT.cx_Ecx], 0	; stop moving and set error message
	mov	dword [eax+CONTEXT.cx_Edx], .msgErrEx
	xor	eax, eax			; continue execution
	retn

.AfterSEH:
	push	dword [fs:eax]
	mov	[fs:eax], esp
	rep	movsb				; copy new content (SEH protected)
	pop	dword [fs:eax]			; remove SEH
	pop	eax

	mov	word [edi], 0a0dh		; add CRLF
	mov	byte [edi+2], 0			; and final 0
	mov	esi, edx			; copy error msg (if any)

	push	ebp
	call	[API.oGlobalUnlock]		; unlock newly created buffer

	call	[API.oEmptyClipboard]		; empty clipboard and grab ownership
						; can anyone tell me if this is necessary? damned M$...

	push	ebp				; set new clipboard data
	push	byte CF_TEXT
	call	[API.oSetClipboardData]
	test	eax, eax
	jnz	near .CloseClip

	mov	esi, .msgErrSet			; 'Error setting clipboard'
	mov	edi, ebp			; deallocate memory (new clipboard buffer)
	xor	ebp, ebp			; nothing to unlock

.dealloc:
	push	edi				; dealloc memory (only in case of error)
	call	[API.oGlobalFree]

.unlock:
	test	ebp, ebp			; is there any memory locked?
	jz	.CloseClip

	push	ebp				; yes - unlock it
	call	[API.oGlobalUnlock]

.CloseClip:
	call	[API.oCloseClipboard]		; close clipboard

.ErrMsg:
	test	esi, esi			; is there any error to report?
	jz	.return
	mov	ax, 0x73			; DS_Printf
	int	0x41

.return:
	int3
	

; ------------------------------------------------------------------------------
segment _LDATA
.msgStr:	db '%s', CRLF0
.msgErrOpen:	db 'Error opening windows clipboard!', CRLF0
.msgErrGet:	db 'Error getting windows clipboard data!', CRLF0
.msgErrLock:	db 'Error locking windows clipboard memory!', CRLF0
.msgErrAlloc:	db 'Error allocating memory!', CRLF0
.msgErrLock2:	db 'Error locking newly allocated memory!', CRLF0
.msgErrSet:	db 'Error setting windows clipboard!', CRLF0
.msgErrEx:	db 'Exception occured while copying the data!', CRLF0
