
.586
.MMX
.model flat,stdcall
option casemap:none
include C:\masm32\include\windows.inc
include C:\masm32\include\user32.inc
include C:\masm32\include\kernel32.inc
include C:\masm32\include\masm32.inc
includelib kernel32.lib
includelib user32.lib
includelib masm32.lib

.data
;qw insert for MMX
insMem11 equ 20646420h ;' dd '
insMem12 dq 6E2074657366666Fh ;'offset n'
insMem13 equ 2Ch ;','
insMem14 dq 64207366666F202Ch ;', offs d' 2C 20 6F 66 66 73 20 64 
insMem15 equ 0A0Dh ;CRLF
insMemN1 equ 6Eh ;'n'
insMemN2 dq 2720626420202020h ;    db '
insMemN3 dq 0A0D202020302C27h ;',0   [CRLF]
insMemD1 equ 64h ;'d'
insMemD2 equ 0A0Dh ;[CRLF]

;---------------------------------------------------------------------------------------------------------------------------------
;Схема:
;MEM1
;[insMem11][insMem12][Block][insMem13][Block][insMem14][Block][insMem15]
; dw ptr	     MMX q                  byte ptr                   MMX q                    w ptr
;================================================================
;MEMN
;[insMemN1][Block][insMemN2][Block][insMemN3]
; byte ptr                   MMX q                    MMX q
;================================================================
;MEMD
;[insMemD1][Block][insMemD2]
; byte ptr                  w ptr
;----------------------------------------------------------------------------------------------------------------------------------

.data?
MemNames db 8192 dup (?) ;для 'n[Block] db '[Block]'' 13,10
MemDesc db 8192 dup (?)    ;для 'd[Block]'13,10
.code
;Первым делом нужно открыть буфер и убедится что там вообще есть
;Валидный текст и только после этого выделять память и производить какие либо действия
Start:
	invoke OpenClipboard,0 ;откроем буффер
	test eax,eax
	je @error		;проверим небыло ли ошибке - да - выход
	invoke GetClipboardData,CF_TEXT
	test eax,eax
	jne @F		;если нет ошибки - на следующий этап обработки.
	invoke CloseClipboard ;иначе закрыть буфер
	jmp @error	;и на выход
.data?
hClipboard dd ?
.code
@@:	mov hClipboard,eax ;сохраним хэндл памяти буффера
	invoke GlobalLock,eax ;получим указатель
	test eax,eax
	je @ErMemAccess
	mov esi,eax	;esi = указатель на источник.
;Найдём первый валидный байт
;или 0. Самая "маленькая" буква A = 41h
	mov al,[esi]
	mov ah,[esi+1]
	cmp al,'A'
	jnc @found2
	cmp ah,'A'
	jnc @found1
	add esi,2
	test ah,al
	jne @B
	test ah,ah
	je @nodata
	test al,al
	je @nodata
	jmp @B

@found1: inc esi ; валидный байт найден в AH скорректируем esi чтобы показывал на [esi+1]
;Мы нашли начало данных теперь нужно
;1 Выделить память для Mem1
;Загрузить указатели для MemNames и MemDesc
@found2:
	invoke GlobalAlloc,GMEM_FIXED,10000h ;
	test eax,eax
	je @errmem
.data?
hMem1 dd ?
.code	
	mov hMem1,eax ;сохраним для GlobalUnlock
	mov ebx,eax ;ebx - указатель на Mem1
	mov edx,offset MemNames ;edx - указатель на MemName
	mov edi,offset MemDesc ;edi - указатель на MemDesc	

;Перед заполнением первых мест для блоков заполним необходимые "префиксы"
	movq mm(0),insMem12
	movq mm(1),insMem14
	movq mm(2),insMemN2
	movq mm(3),insMemN3
	xor ecx,ecx	;zero ecx	
@startfill:

	mov dword ptr [ebx],insMem11
	movq [ebx+4],mm(0)
	mov byte ptr [edx],insMemN1
	add ebx,12 ;edx = next byte to fill
	mov byte ptr [edi],insMemD1
	inc edx
	inc edi
;Сейчас вставляем первые блоки по одному в текущее положение ebx,edx,edi
;И вычисляем длину блока
	mov al,[esi]
@block1:
	mov [edx][ecx],al 
	mov [edi][ecx],al
	mov ah,[esi][ecx+1]
	mov [ebx][ecx],al
	cmp ah,30h
	lea ecx,[ecx+1]
	mov al,ah
	jnc @block1
;ecx = длина блока edi=MemDesc ebx = Mem1 edx = MemNames + ecx = текущий для заполнения
;адрес
;вставка окончания блока для MemDesc
	mov word ptr [edi][ecx],insMemD2 ;CRLF
	movq [edx][ecx],mm(2) ;вторая вставка в MemNames
	lea edi,[edi][ecx][2] ;скорректируем edi чтобы указывал на байт для заполнения
	mov byte ptr [ebx][ecx],insMem13 ;вставим в Mem1
	push edi		;сохраним указатель на MemDesc
	lea edx,[edx][ecx*2][8] ;edx указывает на следуещий за вторым блоком адрес
	lea ebx,[ebx][ecx*2][1] ;ebx указывает на конец 2го блока (адрес для insMem14)
	add esi,ecx
	lea edi,[ebx][ecx][8] ;edi указывает на адрес за 3им блоком (insMem15)
	neg ecx ;теперь значение [указатель][ecx] уменьшено на размер длины блока

@block2: 
	mov al,[esi][ecx]
	mov [edx][ecx],al
	mov [edi][ecx],al
	mov [ebx][ecx],al
	inc ecx
	jne @block2	
	mov word ptr [edi],insMem15
	movq [edx],mm(3)
	movq [ebx],mm(1)
	add edx,8
	lea ebx,[edi][2]
	pop edi
	
@@:	mov al,[esi]
	mov ah,[esi+1]
	cmp al,10
	je @fnd2
	cmp ah,10
	je @fnd1
	test al,ah
	lea esi,[esi+2]
	jne @B
	test al,al
	jne @B
	test ah,ah
	jne @B
	jmp @endfill
@fnd2:	dec esi
@fnd1:	cmp byte ptr [esi],0
	je  @endfill
@@:	mov al,[esi]
	mov ah,[esi+1]
	cmp al,'A'
	jnc  f2
	cmp ah,'A'
	jnc f1
	test ah,al
	jne @notzero
	test ah,ah
	je @endfill
	test al,al
	je @endfill
@notzero:
	add esi,2
	jmp @B
f1:	inc esi
f2:	jmp @startfill

@endfill:
;На этом этапе необходимо сделать соединение строк.
;Можно вычислить длину для MemNames и MemDesc через разницу между их офсетом
; и текущим положением указателей edi, edx, и использовать для этого rep movsd rep movsb

	mov ecx,edi
	mov esi,offset MemDesc ;esi = MemDesc
	sub ecx,offset MemDesc ;ecx = длина MemDesc
	mov edi,ebx ; edi = первый незаполненный байт в Mem1
	push ecx
	shr ecx,2
rep 	movsd
	pop ecx
	and ecx,3
rep	movsb
	mov ecx,edx
	mov esi,offset MemNames
	sub ecx,offset MemNames
	push ecx
	shr ecx,2
rep	movsd
	pop ecx
	and ecx,3
rep	movsb
	mov byte ptr [edi],0
	invoke GlobalUnlock,hClipboard
	invoke EmptyClipboard
	invoke SetClipboardData,CF_TEXT,hMem1
@ret:	invoke CloseClipboard
	call ExitProcess
.data
error 	db 'Нет возможности получить данные из буфера.',13,10
	db 'Возможно буффер пуст.',13,10
	db 13,10,0
.code
@error: invoke MessageBox,0,offset error,0,0
	jmp @ret
@ErMemAccess:
	invoke GetErrDescription,eax
	invoke CloseClipboard
	jmp @ret
@nodata:
	invoke GlobalUnlock,hClipboard
	invoke CloseClipboard
.data
nodata db 'Нет подходящих данных для обработки',0
.code
	invoke MessageBox,0,offset nodata,0,0
	jmp @ret
@errmem:
	invoke GlobalUnlock,hClipboard
	invoke CloseClipboard
	invoke GetErrDescription,0
	jmp @ret

end Start