format PE GUI 4.0
entry start

WM_SOCKET EQU WM_USER

BORDERTHICKNESS=16
MIN_CHAT_SIZE_X=180
MIN_CHAT_SIZE_Y=140
LIMIT_TEXT_TYPEEDIT=1024
NICK_MAX=15
NICK_LEFT_DELIMITER='['
NICK_RIGHT_DELIMITER=']'
CHAT_LEFT_MARGIN=20
CHAT_RIGHT_MARGIN=20
TYPE_LEFT_MARGIN=CHAT_LEFT_MARGIN
TYPE_RIGHT_MARGIN=CHAT_RIGHT_MARGIN
;------------------------------
HATCH_TYPE=HS_BDIAGONAL
;HATCH_TYPE=HS_CROSS
;HATCH_TYPE=HS_DIAGCROSS
;HATCH_TYPE=HS_FDIAGONAL
;HATCH_TYPE=HS_HORIZONTAL
;HATCH_TYPE=HS_VERTICAL
HATCH_LINE_COLOR=$ff0000
HATCH_BACKGROUND_COLOR=$800000
;------------------------------
DIALOG_BACKGROUND_COLOR=$0
TEXT_COLOR=$ff00
TYPE_TEXT_COLOR=$ffffff
;------------------------------
MIN_PRIV_PORT=49152
MAX_PRIV_PORT=65535
;----DLG1--------
IDC_ICON=100
IDC_CANCEL=1004
IDC_CONNECT=1006
IDC_CLIENT=1001
IDC_SERVER=1002
IDC_IP=1003
IDC_PORT=1007
IDC_NICK=1005
IDC_DIALOG=1000
;----DLG2--------
IDC_CHATDIALOG=1100
IDC_CHATEDIT=1101
IDC_TYPEEDIT=1102
;--------------

macro cmpje reg,val,label
{
	cmp reg,val
	je label
}

macro cmpjne reg,val,label
{
	cmp reg,val
	jne label
}

include "win32a.inc"

section '.text' code readable executable
  start:
	invoke GetModuleHandle,0
	mov [hInstance],eax

	mov [initcom.dwSize],sizeof.INITCOMMONCONTROLSEX
	mov [initcom.dwICC],ICC_INTERNET_CLASSES
	invoke InitCommonControlsEx,initcom
	test eax,eax
	jnz .comsuccess
	invoke MessageBox,0,comctrlerror,ErrorCpt,MB_OK+MB_ICONERROR
	jmp .die
   .comsuccess:
	invoke WSAStartup,$202,lpWSAData
	test eax,eax
	jnz .wsiniterror
	mov ax,[lpWSAData.wVersion]
	cmp ax,$202
	je .initgood
	invoke MessageBox,0,ws_nosupport,ErrorCpt,MB_OK+MB_ICONERROR
	jmp .deinit
   .wsiniterror:
	invoke MessageBox,0,ws_initerror,ErrorCpt,MB_OK+MB_ICONERROR
	jmp .die
   .initgood:
	invoke DialogBoxParam,[hInstance],IDC_DIALOG,0,DlgProc,0
	test eax,eax
	jz .deinit
	; if we came here we have a legal ip address and the user didn't cancel
	cmp eax,1
	je .isclient
	invoke DialogBoxParam,[hInstance],IDC_CHATDIALOG,0,DlgServerProc,0
	jmp .deinit
     .isclient:
	invoke DialogBoxParam,[hInstance],IDC_CHATDIALOG,0,DlgClientProc,0
     .deinit:
	invoke WSACleanup
     .die:
	invoke ExitProcess,0
;---------------------------------------------------------------------------------------------------------

proc DlgProc hwnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
local szport[12]:BYTE
     mov eax,[uMsg]
     cmpje eax,WM_COMMAND,.command
     cmpje eax,WM_CLOSE,.close
     cmpje eax,WM_INITDIALOG,.init
     ; return false to give internal dialog handler the call to handle message
     xor eax, eax
     ret

.command:
     mov eax,[wParam]
     mov ecx,eax
     shr eax,16
     cmp eax,BN_CLICKED
     jne .no_bn_clicked
     cmp cx,IDC_CANCEL
     jne .next0
     invoke EndDialog,[hwnd],0
     jmp .commandreturn
  .next0:
     cmp cx,IDC_CONNECT
     jne .next1
     invoke GetDlgItemText,[hwnd],IDC_NICK,nickname+1,NICK_MAX+1
     test eax,eax
     jz .badnick
     mov byte [eax+nickname+1],NICK_RIGHT_DELIMITER
     mov byte [eax+nickname+2],$20
     mov edx,eax
     add edx,3
     mov [nicklen],edx
     add eax,3
     add eax,nickname
     mov [nicktext],eax
     invoke GetDlgItem,[hwnd],IDC_IP
     invoke SendDlgItemMessage,[hwnd],IDC_IP,IPM_GETADDRESS,0,ip
     cmp eax,4
     jb .badip
     ; good ip if we came here, check if good port
     lea ecx,[szport]
     invoke GetDlgItemText,[hwnd],IDC_PORT,ecx,6
     test eax,eax
     jz .badport
     lea ecx,[szport]
     stdcall a2dw,ecx
     cmp eax,MIN_PRIV_PORT
     jb .badport
     cmp eax,MAX_PRIV_PORT
     ja .badport
     mov [port],eax
     invoke SendDlgItemMessage,[hwnd],IDC_SERVER,BM_GETCHECK,0,0
     cmp eax,BST_CHECKED
     jne .clientreturn
     invoke EndDialog,[hwnd],2
     jmp .commandreturn
  .clientreturn:
     invoke EndDialog,[hwnd],1
     jmp .commandreturn
  .badnick:
     invoke MessageBox,[hwnd],badnick,ErrorCpt,MB_OK+MB_ICONERROR
     jmp .commandreturn
  .badport:
     invoke MessageBox,[hwnd],BadPort,BadPortCpt,MB_OK+MB_ICONERROR
     jmp .commandreturn
  .badip:
     invoke SendDlgItemMessage,[hwnd],IDC_SERVER,BM_GETCHECK,0,0
     cmp eax,BST_CHECKED
     jne .badipclient
     invoke MessageBox,[hwnd],BadIPServer,ErrorCpt,MB_OK+MB_ICONERROR
     jmp .commandreturn
  .badipclient:
     invoke MessageBox,[hwnd],BadIPClient,ErrorCpt,MB_OK+MB_ICONERROR
     jmp .commandreturn
  .next1:
     cmp cx,IDC_SERVER
     jne .next2
     invoke SendDlgItemMessage,[hwnd],IDC_SERVER,BM_GETCHECK,0,0
     cmp eax,BST_CHECKED
     jne .commandreturn
     invoke SetDlgItemText,[hwnd],IDC_CONNECT,ServerTxt
     jmp .commandreturn
  .next2:
     cmp cx,IDC_CLIENT
     jne .next3
     invoke SendDlgItemMessage,[hwnd],IDC_CLIENT,BM_GETCHECK,0,0
     cmp eax,BST_CHECKED
     jne .commandreturn
     invoke SetDlgItemText,[hwnd],IDC_CONNECT,ClientTxt
     jmp .commandreturn
  .next3:
  .no_bn_clicked:
  .commandreturn:
     xor eax,eax
     ret

.close:
     invoke EndDialog,[hwnd],0
     xor eax,eax
     ret

.init:
     stdcall SetProgramIcon,[hwnd]
     invoke SendDlgItemMessage,[hwnd],IDC_CLIENT,BM_SETCHECK,BST_CHECKED,0
     invoke SendDlgItemMessage,[hwnd],IDC_PORT,EM_SETLIMITTEXT,5,0
     invoke SetDlgItemText,[hwnd],IDC_PORT,defport
     invoke SendDlgItemMessage,[hwnd],IDC_NICK,EM_SETLIMITTEXT,NICK_MAX,0
     invoke SetDlgItemText,[hwnd],IDC_NICK,defnick
     mov eax,TRUE
     ret

endp
;---------------------------------------------------------------------------------------------------------
; return 0 on failure
proc parse_fdaccept hwnd
local s sockaddr_in ?
local s_len dd sizeof.sockaddr_in
local szbuf rd 1
     cmpje [connected],1,.done ; only one client at the time
     lea eax,[s]
     lea ecx,[s_len]
     invoke accept,[sock],eax,ecx
     cmpjne eax,-1,.acceptok
     stdcall addtext,[hwnd],ws_accepterror,TRUE,FALSE
     xor eax,eax
     jmp .done
  .acceptok:
     mov [connected],1
     mov [clientsock],eax
     invoke GlobalAlloc,GPTR,sizeof.ServerConnect+15+1
     test eax,eax
     jz .converror
     mov [szbuf],eax
     stdcall catstr,eax,ServerConnect
     invoke inet_ntoa,[s.sin_addr]
     test eax,eax
     jz .converrorfree
     stdcall catstr,[szbuf],eax
     stdcall addtext,[hwnd],[szbuf],TRUE,FALSE
     stdcall sendwait,[hwnd],ServerWelcome,sizeof.ServerWelcome
  .converrorfree:
     invoke GlobalFree,[szbuf]
  .converror:
     mov eax,TRUE
  .done:
     ret
endp
;---------------------------------------------------------------------------------------------------------
; return 0 on failure
proc parse_fdread hwnd
     invoke recv,[clientsock],[readbuf],$ffff,0
     test eax,eax
     jz .connectiongracefullyclosed
     cmp eax,-1
     je .readsocketerror
     cmp eax,$ffff
     jae .readtoolarge
     mov ecx,[readbuf]
     add ecx,eax
     mov byte [ecx],0
     stdcall Rot13,[readbuf],eax
     stdcall addtext,[hwnd],[readbuf],TRUE,FALSE
     mov eax,TRUE
     jmp .done
  .connectiongracefullyclosed:
     stdcall addtext,[hwnd],graceerror,TRUE,FALSE
     jmp .fail
  .readsocketerror:
     stdcall addtext,[hwnd],readsockerror,TRUE,FALSE
     jmp .fail
  .readtoolarge:
     stdcall addtext,[hwnd],toolargeerror,TRUE,FALSE
  .fail:
     xor eax,eax
  .done:
     ret
endp
;---------------------------------------------------------------------------------------------------------
proc parse_fdwrite
     ret
endp
;---------------------------------------------------------------------------------------------------------
proc parse_fdclose hwnd
     mov [connected],0
     invoke closesocket,[clientsock]
     stdcall addtext,[hwnd],socketclosed,TRUE,FALSE
     ret
endp
;---------------------------------------------------------------------------------------------------------
proc parse_fdconnect hwnd
     mov [connected],1
     ret
endp
;---------------------------------------------------------------------------------------------------------
; send data in a wait state, will not return until data is sent or there is an error
; returns TRUE on success, FALSE on failure
proc sendwait uses ebx,hwnd,datatosend,lenofdata
local buf rd 1
     invoke GlobalAlloc,GMEM_FIXED,[lenofdata]
     test eax,eax
     jz .fail
     mov [buf],eax
     mov edx,[datatosend]
     mov ecx,[lenofdata]
     mov ebx,eax
  .copy:
     mov al,[edx]
     mov [ebx],al
     add ebx,1
     add edx,1
     sub ecx,1
     jnz .copy
     stdcall Rot13,[buf],[lenofdata]
     invoke send,[clientsock],[buf],[lenofdata],0
     mov ebx,eax
     invoke GlobalFree,[buf]
     cmp ebx,-1
     jne .datasent
     invoke WSAGetLastError
     cmp eax,10035 ; 10035=WSAEWOULDBLOCK (not defined)
     je .datasent
     stdcall addtext,[hwnd],senderror,TRUE,FALSE
     xor eax,eax
     jmp .end
  .fail:
     xor eax,eax
     jmp .end
  .datasent:
     mov eax,TRUE
  .end:
     ret
endp
;---------------------------------------------------------------------------------------------------------

proc SetFullScreen hwnd
     cmp [fullscreen],1
     je .end
     mov [fullscreen],1
     invoke GetWindowRect,[hwnd],oldscreen
     invoke SetWindowLong,[hwnd],GWL_EXSTYLE,$8
     invoke SetWindowLong,[hwnd],GWL_STYLE,$900B0800
     invoke GetSystemMetrics,SM_CXSCREEN
     push eax
     invoke GetSystemMetrics,SM_CYSCREEN
     pop ecx
     invoke MoveWindow,[hwnd],0,0,ecx,eax,TRUE
   .end:
     ret
endp
;---------------------------------------------------------------------------------------------------------
proc RestoreScreen hwnd
     cmp [fullscreen],1
     jne .end
     mov [fullscreen],0
     invoke SetWindowLong,[hwnd],GWL_EXSTYLE,$0
     invoke SetWindowLong,[hwnd],GWL_STYLE,$10CF0800
     mov eax,[oldscreen.right]
     mov ecx,[oldscreen.bottom]
     sub eax,[oldscreen.left]
     sub ecx,[oldscreen.top]
     invoke MoveWindow,[hwnd],[oldscreen.left],[oldscreen.top],eax,ecx,TRUE
   .end:
     ret
endp
;---------------------------------------------------------------------------------------------------------
proc ReallyQuit hwnd
     cmp [connected],1
     jne .close
     invoke MessageBox,[hwnd],really,reallycpt,MB_OKCANCEL or MB_ICONWARNING or MB_DEFBUTTON2
     cmp eax,IDOK
     jne .quit
   .close:
     mov eax,1
     jmp .bye
   .quit:
     xor eax,eax
   .bye:
     ret
endp
;---------------------------------------------------------------------------------------------------------

proc DlgServerProc hwnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
     mov eax,[uMsg]
     cmpje eax,WM_SOCKET,.socket
     cmpje eax,WM_SIZING,.sizing
     cmpje eax,WM_WINDOWPOSCHANGED,.windowposchanged
     cmpje eax,WM_CTLCOLORSTATIC,.ctlcolorstatic
     cmpje eax,WM_CTLCOLOREDIT,.ctlcoloredit
     cmpje eax,WM_CTLCOLORDLG,.ctlcolordlg
     cmpje eax,WM_COMMAND,.command
     cmpje eax,WM_CLOSE,.close
     cmpje eax,WM_INITDIALOG,.init
     ; return false to give internal dialog handler the call to handle message
     xor eax, eax
     ret

.socket:
     mov eax,[lParam]
     cmpjne ax,FD_ACCEPT,.checkread
     stdcall parse_fdaccept,[hwnd]
     jmp .sockdone
  .checkread:
     cmpjne ax,FD_READ,.checkclose
     stdcall parse_fdread,[hwnd]
     jmp .sockdone
  .checkclose:
     cmpjne ax,FD_CLOSE,.checkwrite
     stdcall parse_fdclose,[hwnd]
     jmp .sockdone
  .checkwrite:
     cmpjne ax,FD_WRITE,.sockdone
     stdcall parse_fdwrite
  .sockdone:
     mov eax,TRUE
     ret

.command:
     mov eax,[wParam]
     movzx ecx,ax
     shr eax,16
     cmp ax,EN_ERRSPACE
     jne .endcommand
     cmp cx,IDC_CHATEDIT
     jne .endcommand
     invoke SendDlgItemMessage,[hwnd],IDC_CHATEDIT,WM_SETTEXT,0,nostring
     stdcall addtext,[hwnd],errornomemory,TRUE,FALSE
  .endcommand:
     xor eax,eax
     ret

.close:
     stdcall ReallyQuit,[hwnd]
     test eax,eax
     jz .noclose
     cmp [connected],1
     jne .noclientconnected
     invoke closesocket,[clientsock]
  .noclientconnected:
     invoke closesocket,[sock]
     invoke DeleteObject,[editbrush]
     invoke DeleteObject,[dlgbrush]
     cmp [readbuf],0
     je .novirtualfree
     invoke VirtualFree,[readbuf],0,MEM_RELEASE
  .novirtualfree:
     invoke EndDialog,[hwnd],0
  .noclose:
     xor eax,eax
     ret

.windowposchanged:
     stdcall fixcontrols,[hwnd]
     xor eax,eax
     ret

.ctlcolorstatic:
     invoke SetTextColor,[wParam],TEXT_COLOR
     invoke SetBkColor,[wParam],DIALOG_BACKGROUND_COLOR
     mov eax,[editbrush]
     ret

.ctlcoloredit:
     invoke SetTextColor,[wParam],TYPE_TEXT_COLOR
     invoke SetBkColor,[wParam],DIALOG_BACKGROUND_COLOR
     mov eax,[editbrush]
     ret

.ctlcolordlg:
     invoke SetBkColor,[wParam],HATCH_BACKGROUND_COLOR
     mov eax,[dlgbrush]
     ret

.sizing:
     stdcall sizing,[hwnd],[wParam],[lParam]
     mov eax,TRUE
     ret

.init:
     stdcall initdialog,[hwnd]
     ; Print start text
     stdcall addtext,[hwnd],starttextserver,TRUE,FALSE
     invoke VirtualAlloc,NULL,$ffff,MEM_RESERVE or MEM_COMMIT,PAGE_READWRITE
     mov [readbuf],eax
     cmpjne eax,NULL,.createsocket
     stdcall addtext,[hwnd],virtualerror,TRUE,FALSE
     jmp .nolisten
  .createsocket:
     stdcall CreateListenSocket,[hwnd]
     test eax,eax
     jnz .listensuccess
     stdcall addtext,[hwnd],ws_listenerror,TRUE,FALSE
     jmp .nolisten
  .listensuccess:
     stdcall addtext,[hwnd],listengood,TRUE,FALSE
  .nolisten:
     mov eax,TRUE
     ret
endp
;---------------------------------------------------------------------------------------------------------

proc DlgClientProc hwnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
     mov eax,[uMsg]
     cmpje eax,WM_SOCKET,.socket
     cmpje eax,WM_SIZING,.sizing
     cmpje eax,WM_WINDOWPOSCHANGED,.windowposchanged
     cmpje eax,WM_CTLCOLORSTATIC,.ctlcolorstatic
     cmpje eax,WM_CTLCOLOREDIT,.ctlcoloredit
     cmpje eax,WM_CTLCOLORDLG,.ctlcolordlg
     cmpje eax,WM_COMMAND,.command
     cmpje eax,WM_CLOSE,.close
     cmpje eax,WM_INITDIALOG,.init
     ; return false to give internal dialog handler the call to handle message
     xor eax, eax
     ret

.socket:
     rept 50 {xor eax,eax}
     mov eax,[lParam]
     cmpjne ax,FD_READ,.checkclose
     stdcall parse_fdread,[hwnd]
     jmp .sockdone
  .checkclose:
     cmpjne ax,FD_CLOSE,.checkwrite
     stdcall parse_fdclose,[hwnd]
     jmp .sockdone
  .checkwrite:
     cmpjne ax,FD_WRITE,.checkconnect
     stdcall parse_fdwrite
  .checkconnect:
     cmpjne ax,FD_CONNECT,.sockdone
     stdcall parse_fdconnect,[hwnd]
  .sockdone:
     xor eax,eax
     ret

.command:
     mov eax,[wParam]
     movzx ecx,ax
     shr eax,16
     cmp ax,EN_ERRSPACE
     jne .endcommand
     cmp cx,IDC_CHATEDIT
     jne .endcommand
     invoke SendDlgItemMessage,[hwnd],IDC_CHATEDIT,WM_SETTEXT,0,nostring
     stdcall addtext,[hwnd],errornomemory,TRUE,FALSE
  .endcommand:
     xor eax,eax
     ret

.close:
     stdcall ReallyQuit,[hwnd]
     test eax,eax
     jz .noclose
     cmp [connected],1
     jne .noclientconnected
     invoke closesocket,[clientsock]
  .noclientconnected:
     invoke DeleteObject,[editbrush]
     invoke DeleteObject,[dlgbrush]
     cmp [readbuf],0
     je .novirtualfree
     invoke VirtualFree,[readbuf],0,MEM_RELEASE
  .novirtualfree:
     invoke EndDialog,[hwnd],0
  .noclose:
     xor eax,eax
     ret

.windowposchanged:
     stdcall fixcontrols,[hwnd]
     xor eax,eax
     ret

.ctlcolorstatic:
     invoke SetTextColor,[wParam],TEXT_COLOR
     invoke SetBkColor,[wParam],DIALOG_BACKGROUND_COLOR
     mov eax,[editbrush]
     ret

.ctlcoloredit:
     invoke SetTextColor,[wParam],TYPE_TEXT_COLOR
     invoke SetBkColor,[wParam],DIALOG_BACKGROUND_COLOR
     mov eax,[editbrush]
     ret

.ctlcolordlg:
     invoke SetBkColor,[wParam],HATCH_BACKGROUND_COLOR
     mov eax,[dlgbrush]
     ret

.sizing:
     stdcall sizing,[hwnd],[wParam],[lParam]
     mov eax,TRUE
     ret

.init:
     stdcall initdialog,[hwnd]
     ; Print start text
     stdcall addtext,[hwnd],starttextclient,TRUE,FALSE
     invoke VirtualAlloc,NULL,$ffff,MEM_RESERVE or MEM_COMMIT,PAGE_READWRITE
     mov [readbuf],eax
     cmpjne eax,NULL,.createsocket
     stdcall addtext,[hwnd],virtualerror,TRUE,FALSE
     jmp .nocreate
  .createsocket:
     stdcall CreateConnectSocket,[hwnd]
     test eax,eax
     jnz .createsuccess
     stdcall addtext,[hwnd],ws_createerror,TRUE,FALSE
     jmp .nocreate
  .createsuccess:
     stdcall addtext,[hwnd],connecting,TRUE,FALSE
  .nocreate:
     mov eax,TRUE
     ret
endp
;---------------------------------------------------------------------------------------------------------

proc TypeEditProc hwnd:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
local buflen rd 1
     mov eax,[uMsg]
     cmp eax,WM_GETDLGCODE
     jne .checkkeys
     mov eax,DLGC_WANTALLKEYS
     ret
  .checkkeys:
     cmp eax,WM_KEYDOWN
     je .checkfullscreen
     cmp eax,WM_CHAR
     jne .default
     cmp [wParam],VK_RETURN
     je .vk_return
     jmp .default
  .checkfullscreen:
     cmp [wParam],VK_F11
     je .fullscreen
     cmp [wParam],VK_ESCAPE
     jne .empty
     invoke PostMessage,[hwndglob],WM_CLOSE,0,0
     jmp .empty
     ;-----------------
  .fullscreen:
     cmp [fullscreen],1
     je .restorescreen
     stdcall SetFullScreen,[hwndglob]
     jmp .empty
  .restorescreen:
     stdcall RestoreScreen,[hwndglob]
     jmp .empty

  .vk_return:
     invoke SendDlgItemMessage,[hwndglob],IDC_TYPEEDIT,WM_GETTEXTLENGTH,0,0
     mov ecx,[nicklen]
     mov [buflen],eax
     add [buflen],ecx
     invoke SendDlgItemMessage,[hwndglob],IDC_TYPEEDIT,WM_GETTEXT,LIMIT_TEXT_TYPEEDIT+1,[nicktext]
     test eax,eax
     jz .empty
     cmp [connected],1
     jne .notconnected
     stdcall addtext,[hwndglob],nickname,TRUE,FALSE
     jmp .connected
  .notconnected:
     stdcall addtext,[hwndglob],notconnected,TRUE,FALSE
  .connected:
     invoke SendDlgItemMessage,[hwndglob],IDC_TYPEEDIT,WM_SETTEXT,0,nostring
     cmp [connected],1
     jne .empty
     stdcall sendwait,[hwndglob],nickname,[buflen]
     jmp .empty
     ;-----------------
  .empty:
    ; xor eax,eax
   ;  ret
  .default:
     invoke CallWindowProc,[oldproc],[hwnd],[uMsg],[wParam],[lParam]
     ret
endp

;------------------------------------------------------------------------------------------------------------

proc sizing hwnd,wParam,lParam
     mov eax,[wParam]
     mov ecx,[lParam]
     cmp eax,WMSZ_BOTTOM
     je .bottom
     cmp eax,WMSZ_BOTTOMLEFT
     je .bottom
     cmp eax,WMSZ_BOTTOMRIGHT
     je .bottom
     cmp eax,WMSZ_TOP
     je .top
     cmp eax,WMSZ_TOPLEFT
     je .top
     cmp eax,WMSZ_TOPRIGHT
     je .top
     jmp .checkx
  .bottom:
     mov edx,[ecx+RECT.bottom]
     sub edx,[ecx+RECT.top]
     cmp edx,MIN_CHAT_SIZE_Y
     ja .checkx
     mov edx,[ecx+RECT.top]
     add edx,MIN_CHAT_SIZE_Y
     mov [ecx+RECT.bottom],edx
     jmp .checkx
  .top:
     mov edx,[ecx+RECT.bottom]
     sub edx,[ecx+RECT.top]
     cmp edx,MIN_CHAT_SIZE_Y
     ja .checkx
     mov edx,[ecx+RECT.bottom]
     sub edx,MIN_CHAT_SIZE_Y
     mov [ecx+RECT.top],edx
     jmp .checkx
  .checkx:
     cmp eax,WMSZ_BOTTOMLEFT
     je .left
     cmp eax,WMSZ_BOTTOMRIGHT
     je .right
     cmp eax,WMSZ_TOPLEFT
     je .left
     cmp eax,WMSZ_TOPRIGHT
     je .right
     cmp eax,WMSZ_LEFT
     je .left
     cmp eax,WMSZ_RIGHT
     je .right
     jmp .sizingdone
  .left:
     mov edx,[ecx+RECT.right]
     sub edx,[ecx+RECT.left]
     cmp edx,MIN_CHAT_SIZE_X
     ja .sizingdone
     mov edx,[ecx+RECT.right]
     sub edx,MIN_CHAT_SIZE_X
     mov [ecx+RECT.left],edx
     jmp .sizingdone
  .right:
     mov edx,[ecx+RECT.right]
     sub edx,[ecx+RECT.left]
     cmp edx,MIN_CHAT_SIZE_X
     ja .sizingdone
     mov edx,[ecx+RECT.left]
     add edx,MIN_CHAT_SIZE_X
     mov [ecx+RECT.right],edx
  .sizingdone:
     ret
endp
;------------------------------------------------------------------------------------------------------------

proc SetProgramIcon hwnd
     invoke LoadImage,[hInstance],100,IMAGE_ICON,0,0,LR_DEFAULTCOLOR
     push eax
     invoke SendMessage,[hwnd],WM_SETICON,ICON_SMALL,eax
     pop eax
     invoke SendMessage,[hwnd],WM_SETICON,ICON_BIG,eax
     ret
endp
;------------------------------------------------------------------------------------------------------------

proc initdialog hwnd
     ; save window handle globally
     mov eax,[hwnd]
     mov [hwndglob],eax
     ; Set icon of dialog
     stdcall SetProgramIcon,[hwnd]
     ; Create brush for edit controls
     invoke CreateSolidBrush,DIALOG_BACKGROUND_COLOR
     mov [editbrush],eax
     ; Create brush for dialog background
     invoke CreateHatchBrush,HATCH_TYPE,HATCH_LINE_COLOR ; color for lines in hatch brush
     mov [dlgbrush],eax
     ; Set margins of chat edit control
     mov ax,CHAT_LEFT_MARGIN
     shl eax,16
     mov ax,CHAT_RIGHT_MARGIN
     invoke SendDlgItemMessage,[hwnd],IDC_CHATEDIT,EM_SETMARGINS,EC_LEFTMARGIN+EC_RIGHTMARGIN,eax
     ; Set margins of type edit control
     mov ax,TYPE_LEFT_MARGIN
     shl eax,16
     mov ax,TYPE_RIGHT_MARGIN
     invoke SendDlgItemMessage,[hwnd],IDC_TYPEEDIT,EM_SETMARGINS,EC_LEFTMARGIN+EC_RIGHTMARGIN,eax
     ; Fix controls so they become arranged in window
     stdcall fixcontrols,[hwnd]
     ; insert empty lines to get first line at bottom
     push ebx
     mov ebx,80
  .insertempty:
     invoke SendDlgItemMessage,[hwnd],IDC_CHATEDIT,EM_REPLACESEL,FALSE,emptyline
     sub ebx,1
     jnz .insertempty
     pop ebx

     ; set max text in type edit
     invoke SendDlgItemMessage,[hwnd],IDC_TYPEEDIT,EM_SETLIMITTEXT,LIMIT_TEXT_TYPEEDIT,0

     ; set max text in chat edit
     invoke SendDlgItemMessage,[hwnd],IDC_CHATEDIT,EM_SETLIMITTEXT,0,0

     ; subclass typeedit control
     invoke GetDlgItem,[hwnd],IDC_TYPEEDIT
     invoke SetWindowLong,eax,GWL_WNDPROC,TypeEditProc
     mov [oldproc],eax

     ret
endp
;------------------------------------------------------------------------------------------------------------

proc addtext hwnd,text,newlinebefore,newlineafter
     invoke SendDlgItemMessage,[hwnd],IDC_CHATEDIT,EM_SETSEL,$7FFFFFFF,$7FFFFFFF
     cmp [newlinebefore],1
     jne @F
     invoke SendDlgItemMessage,[hwnd],IDC_CHATEDIT,EM_REPLACESEL,FALSE,emptyline
@@:  invoke SendDlgItemMessage,[hwnd],IDC_CHATEDIT,EM_REPLACESEL,FALSE,[text]
     invoke SendDlgItemMessage,[hwnd],IDC_CHATEDIT,WM_VSCROLL,SB_BOTTOM,0
     cmp [newlineafter],1
     jne @F
     invoke SendDlgItemMessage,[hwnd],IDC_CHATEDIT,EM_REPLACESEL,FALSE,emptyline
@@:  ret
endp
;------------------------------------------------------------------------------------------------------------

proc fixcontrols uses ebx esi, hwnd
local r1 RECT
local r2 RECT

     lea eax,[r1]
     invoke GetClientRect,[hwnd],eax

     invoke GetDlgItem,[hwnd],IDC_TYPEEDIT
     push eax
     lea ecx,[r2]
     invoke GetWindowRect,eax,ecx

     mov eax,[r2.bottom]
     mov ecx,[r2.top]
     sub eax,ecx

     mov ecx,[r1.bottom]
     sub ecx,eax
     mov ebx,eax
     mov [r2.bottom],ecx
     mov [r1.top],ecx
     pop edx
     add [r1.left],BORDERTHICKNESS
     sub [r1.right],BORDERTHICKNESS*2
     sub ecx,BORDERTHICKNESS
     invoke MoveWindow,edx,[r1.left],ecx,[r1.right],eax,TRUE

     lea eax,[r1]
     invoke GetClientRect,[hwnd],eax
     sub [r1.bottom],ebx
     invoke GetDlgItem,[hwnd],IDC_CHATEDIT
     add [r1.left],BORDERTHICKNESS ; NY
     add [r1.top],BORDERTHICKNESS ; NY
     sub [r1.right],BORDERTHICKNESS*2 ; NY
     sub [r1.bottom],BORDERTHICKNESS*2 ; NY
     sub [r1.bottom],BORDERTHICKNESS
     invoke MoveWindow,eax,[r1.left],[r1.top],[r1.right],[r1.bottom],TRUE
     invoke SendDlgItemMessage,[hwnd],IDC_CHATEDIT,WM_VSCROLL,SB_BOTTOM,0
     ret
endp
;---------------------------------------------------------------------------------------------------------

; returns 0 on failure, non zero otherwise
proc CreateListenSocket uses edi,hwnd
    ; zero sockaddr_in structure
    cld
    mov edi,s_addr
    mov ecx,sizeof.sockaddr_in
    xor eax,eax
    rep stosb
    ; Create socket
    invoke socket,AF_INET,SOCK_STREAM,6 ;6=IPPROTO_TCP (not defined in wsock32.inc)
    cmp eax,-1 ;INVALID_SOCKET
    je .bad
    mov [sock],eax
    invoke WSAAsyncSelect,eax,[hwnd],WM_SOCKET,FD_ACCEPT or FD_READ or FD_WRITE or FD_CLOSE
    test eax,eax
    jnz .badclose
    mov [s_addr.sin_family],AF_INET
    mov eax,[port]
    invoke htons,eax
    mov [s_addr.sin_port],ax
    invoke htonl,[ip]
    mov [s_addr.sin_addr],eax
    invoke bind,[sock],s_addr,sizeof.sockaddr_in
    test eax,eax
    jnz .badclose
    invoke listen,[sock],1
    test eax,eax
    jnz .badclose
    jmp .good
  .badclose:
    invoke closesocket,[sock]
  .bad:
    xor eax,eax
    ret
  .good:
    mov eax,TRUE
    ret
endp
;---------------------------------------------------------------------------------------------------------

; returns 0 on failure, non zero otherwise
proc CreateConnectSocket uses edi,hwnd
    ; zero sockaddr_in structure
    cld
    mov edi,s_addr
    mov ecx,sizeof.sockaddr_in
    xor eax,eax
    rep stosb
    ; Create socket
    invoke socket,AF_INET,SOCK_STREAM,6 ;6=IPPROTO_TCP (not defined in wsock32.inc)
    cmp eax,-1 ;INVALID_SOCKET
    je .bad
    mov [clientsock],eax
    invoke WSAAsyncSelect,eax,[hwnd],WM_SOCKET,FD_READ or FD_WRITE or FD_CONNECT or FD_CLOSE
    test eax,eax
    jnz .badclose
    mov [s_addr.sin_family],AF_INET
    mov eax,[port]
    invoke htons,eax
    mov [s_addr.sin_port],ax
    invoke htonl,[ip]
    mov [s_addr.sin_addr],eax
    invoke connect,[clientsock],s_addr,sizeof.sockaddr_in
    test eax,eax
    jz .good
    invoke WSAGetLastError
    cmp eax,10035 ; 10035=WSAEWOULDBLOCK (not defined)
    je .good
  .badclose:
    invoke closesocket,[clientsock]
  .bad:
    xor eax,eax
    jmp .end
  .good:
    mov eax,TRUE
  .end:
    ret
endp
;---------------------------------------------------------------------------------------------------------

; count must be amount of bytes divided by 4
proc Rot13_4 Buf,Count
   mov ecx,[Count]
   push esi
   push edi
   push ebx
   mov esi,[Buf]
   mov edi,Rot13Buf
   push ebp
   mov ebp,ecx
align 16
.x:mov eax,[esi]   ; eax = 4 bytes of string data
   mov edx,eax	   ; edx = copy of eax
   movzx ebx,ah    ; ebx = ah (second byte in eax) zero extended
   bswap edx	   ; edx = eax swapped
   and eax,$ff	  ; delete H.W of eax
   movzx ecx,dh    ; ecx = third byte of eax (which is second byte of edx)
   and edx,$ff	  ; delete H.W of edx

   mov dl,[edi+edx]
   mov dh,[edi+ecx]
   bswap edx
   mov dh,[edi+ebx]
   mov dl,[edi+eax]

   mov [esi],edx
   lea esi,[esi+4]	; esi = next dword string data
   sub ebp,1
   jnz .x

   pop ebp
   pop ebx
   pop edi
   pop esi
   ret
endp
;---------------------------------------------------------------------------------------------------------

proc Rot13_1 uses esi edi,Buf,Count
   mov ecx,[Count]
   mov esi,[Buf]
   mov edi,Rot13Buf
   xor eax,eax
align 16
.x:mov al,[esi]
   mov dl,[edi+eax]
   mov [esi],dl
   add esi,1
   sub ecx,1
   jnz .x
   ret
endp
;---------------------------------------------------------------------------------------------------------

proc Rot13 uses ebx,Buf,Count
local fours rd 1
local oneoffs rd 1
   mov eax,[Count]
   mov ecx,4
   xor edx,edx
   div ecx
   mov [fours],eax
   mov ebx,edx
   shl eax,2
   mov [oneoffs],eax
   test eax,eax
   jz .nofour
   stdcall Rot13_4,[Buf],[fours]
 .nofour:
   test ebx,ebx
   jz .nosingle
   mov eax,[Buf]
   add eax,[oneoffs]
   stdcall Rot13_1,eax,ebx
 .nosingle:
   ret
endp


section '.res' resource from 'Chat.res' data readable

section '.data' data readable writeable
	align 4
	hInstance rd 1
	hwndglob  rd 1
	ip	  rd 1
	port	  rd 1
;        isserver  dd 0
	editbrush rd 1
	dlgbrush  rd 1
	oldproc   rd 1
	connected dd 0
	nicktext  rd 1	; tells where text is to be inserted in nickname string (after nickname, colon and space characters)
	nicklen   rd 1	; sizeof nick+delimiters+space
	clientsock rd 1
	sock	rd 1
	readbuf dd 0
	fullscreen dd 0
	oldscreen RECT ?
	lpWSAData WSADATA ?
	s_addr sockaddr_in ?
	align 16
	times 65 db %-1
	times 26 db (((%-1)+13) MOD 26) + 65
	times 6 db %+90
	times 26 db (((%-1)+13) MOD 26) + 97
	times 133 db %+122
	label Rot13Buf byte at $-256
	initcom INITCOMMONCONTROLSEX ?
	reallycpt	  db "Quit",0
	really		  db "Quitting now..",0
	ws_initerror	  db "Failed to initialize winsock.",0
	ws_nosupport	  db "Your system does not support winsock 2.2",0
	ws_listenerror	  db "Failed to create listening socket.",0
	ws_createerror	  db "Failed to create client socket",0
	ws_accepterror	  db "Failed to accept new connection.",0
	comctrlerror	  db "Failed to initialize common controls.",0
	virtualerror	  db "Failed to allocate memory for read buffer.",0
	graceerror	  db "Could not read data because connection was gracefully closed.",0
	readsockerror	  db "Socket error while recieving data.",0
	toolargeerror	  db "Could not recieve data, too large for buffer.",0
	senderror	  db "Data could not be sent.",0
	notconnected	  db "Not connected.",0
	socketclosed	  db "Connection closed.",0
	errornomemory	  db "Out of memory. Clearing chat window to free memory.",0
	ErrorCpt	  db "Error",0
	BadIPServer	  db "Local IP missing.",0
	BadIPClient	  db "External IP missing.",0
	BadPort 	  db "Invalid PORT number. 49152-65535 are private ports.",0
	BadPortCpt	  db "Bad port",0
	ServerTxt	  db "&Wait",0
	ClientTxt	  db "&Connect",0
	defport 	  db "52500",0
	defnick 	  db "nickname",0
	badnick 	  db "Invalid nickname",0
	nickname	  db NICK_LEFT_DELIMITER,NICK_MAX+3+LIMIT_TEXT_TYPEEDIT dup 0
	starttextserver   db "Program set to server mode. F11=Fullscreen ESC=Quit.",0
	starttextclient   db "Program set to client mode. F11=Fullscreen ESC=Quit.",0
	connecting	  db "Trying to connect..",0
	listengood	  db "Waiting for connections...",0
	ServerConnect	  db "New connection recieved from ",0
	sizeof.ServerConnect=$-ServerConnect
	ServerWelcome	  db "Welcome, this is an automated message from the chat server.",0
	sizeof.ServerWelcome=$-ServerWelcome-1
	emptyline	  db 13,10,0
	nostring	  db "",0

section '.idata' import data readable writeable
	library kernel32,'KERNEL32.DLL',\
		user32,'USER32.DLL',\
		wsock32,'WS2_32.DLL',\
		gdi32,'GDI32.DLL',\
		comctl32,'COMCTL32.DLL'

	include 'api\kernel32.inc'
	include 'api\user32.inc'
	include 'api\wsock32.inc'
	include 'api\gdi32.inc'
	include 'api\comctl32.inc'

include "%INCLUDE%\PRIVLIB\a2dw.asm"
include "%INCLUDE%\PRIVLIB\catstr.asm"
