#pragma option w32c
#stack 4096
#argc TRUE

#define APPNAME "NetCPS"
#define VERSION "2.0"
#define _WINSOCK_ TRUE
#define _WINBASE_ TRUE
#include "windows.h--"
#include "msvcrt.h--"

int Port=4456;
dword Host=0;
DWORD StartTick;
DWORD Peek;
LPCSTR myname;
int Mbytes=1;
DWORD LastTick;
DWORD CountBytes;
DWORD BytesSent;
WSADATA WSAData;
int Server=0;
DWORD KbytesToSend;

void Send(SOCKET cSck);
dword Resiv(SOCKET cSck);

byte Usage=
"Usage: %s [options] [Remote host]\n"
"  -pn Port number. Default port is %d.\n"
"  -s  Server mode. Default is client.\n"
"  -mn Megabytes to transfer. Default is %d.\n"
"Note: 'n' represents a number.\n";

void Error(dword str)
{
	printf(str,WSAGetLastError());
	EXIT(-1);
}

void main()
{
	myname=PARAMSTR(0);
	strrchr(EAX,'\\');
	IF(EAX!=0)myname=EAX+1;
	IF(WSAStartup(0x101,#WSAData)!=0)Error("WSAStartup() failed with error code %d");
	for(EBX=1;@PARAMCOUNT()>EBX;EBX++){
		PARAMSTR(EBX);
		EAX><ECX;
		IF(DSBYTE[ECX]=='-'){
			SWITCH(DSBYTE[ECX+1]){
				CASE 'm':
					Mbytes=atoi(ECX+2);
					BREAK;
				CASE 's':
					Server++;
					BREAK;
				CASE 'p':
					Port=atoi(ECX+2);
					BREAK;
				CASE 'h':
				CASE '?':
					usage(0);
				default:
					usage(ECX);
			}
		}
		ELSE{
			IF(Host)usage(ECX);
			Host=ECX;
		}
	}
	IF(Server)DoServer();
	ELSE{
		IF(Host)DoClient();
		ELSE usage(0);
	}
	WSACleanup();
}

void usage(LPCSTR Tag)
{
	printf("%s Version %s %s\n", APPNAME, VERSION,__DATE__);
	IF(Tag)printf("%s: Unknown parameter\nUse \"%s -help\" for options.",Tag,myname);
	ELSE printf(#Usage,myname,Port,Mbytes);
	EXIT(-1);
}

void DoServer()
{
//char buf[1024];
SOCKET lSck,cSck;
SOCKADDR_IN sin;
int nlen;
	printf("%s %s - Entering server mode. Press ^C to quit\n",APPNAME,VERSION);
	IF(socket(PF_INET,SOCK_STREAM,0)==INVALID_SOCKET)Error("ERROR: Failed to create listening socket %d.\n");
	lSck=EAX;
	sin=0;
	sin.sin_family=AF_INET;
	sin.sin_port=htons(Port);
	sin.s_addr=INADDR_ANY;
	IF(bind(lSck,#sin,sizeof(sockaddr))!=0)Error("ERROR: Failed to bind() listening socket %d.\n");
	IF(listen(lSck,0)!=0)Error("ERROR: Failed to listen() socket %d.\n");
	for(;;){
		printf("*** Waiting for new connection... ***");
		nlen=sizeof(sockaddr);
		IF(accept(lSck,#sin,#nlen)==-1)Error("\nERROR - accept() failed %d.\n");
		cSck=EAX;
		nlen=sizeof(sockaddr);
		getpeername(cSck,#sin,#nlen);
		printf("\nClient connected from %d.%d.%d.%d\nLoading...\n",
			SSBYTE[#sin.s_addr],
			SSBYTE[#sin.s_addr+1],
			SSBYTE[#sin.s_addr+2],
			SSBYTE[#sin.s_addr+3]);
		IF(Resiv(cSck)==1){
			printf("Saving...\n");
			KbytesToSend=BytesSent;
			Send(cSck);
			ResInfo();
		}
		closesocket(cSck);
	}
}

void DoClient()
{
char buf[12];
SOCKET cSck;
SOCKADDR_IN sin;
int nlen=sizeof(sockaddr);
	KbytesToSend=Mbytes*1024*1024;
	printf("%s %s - Entering client mode. Press ^C to quit\n",APPNAME,VERSION);
	IF(socket(PF_INET,SOCK_STREAM,0)==INVALID_SOCKET)Error("ERROR: Failed to create socket %d.\n");
	cSck=EAX;
	sin=0;
	sin.sin_family=AF_INET;
	sin.sin_port=htons(Port);
	sin.s_addr=GetAddr(Host);
	IF ((EAX == INADDR_ANY)||(EAX == INADDR_NONE)){
		printf("ERROR: Host lookup to '%s' failed %d.\n",
			Host, WSAGetLastError());
		EXIT(-1);
	}
	printf("Connecting to %d.%d.%d.%d port %d...",
		SSBYTE[#sin.s_addr],
		SSBYTE[#sin.s_addr+1],
		SSBYTE[#sin.s_addr+2],
		SSBYTE[#sin.s_addr+3],
		Port);
	IF (connect(cSck, #sin, nlen)!=0)Error("\nERROR: Failed to connect() socket %d.\n");
	printf(" Connected!\nSaving...\n");
	Send(cSck);
	buf[0]=-1;
	send(cSck,#buf,12,0);
	ResInfo();
	printf("Loading...\n");
	Resiv(cSck);
	closesocket(cSck);
}

void Send(SOCKET cSck)
{
char buf[1024];
	buf[0]=0;
	GetCps(0);
	for(;;){
		send(cSck,#buf,min(KbytesToSend,sizeof(buf)),0);
		IF(int EAX<=0)BREAK;
		BytesSent+=EAX;
		KbytesToSend-=EAX;
		$jbe BR
//		IF(MINUSFLAG)||(ZEROFLAG)BREAK; // Done
		GetCps(EAX);
	}
BR:
}

dword Resiv(SOCKET cSck)
{
char buf[1024];
	GetCps(0);
	for(;;){
		recv(cSck,#buf,sizeof(buf),0);
		IF(int EAX<=0)BREAK;
		IF(EAX==12)&&(buf[0]==-1){
			EAX=1;
			BREAK;
		}
		BytesSent+=EAX;
		GetCps(EAX);
	}
	$push EAX
	IF(EAX==-1)printf("\nERROR - recv() failed %d.\n",WSAGetLastError());
	ResInfo();
	$pop EAX
}

void ResInfo()
{
	EAX=GetTickCount()-StartTick;
	EBX><EAX;
	EAX=BytesSent/EBX*1000;
	PrintCPS("\nAvrg ",EAX);
	PrintCPS("\nPeek ",Peek);
	printf("\n%d Kb transferred.\n",BytesSent/1024);
}

void GetCps(DWORD Bytes)
{
	IF(Bytes==0){
		LastTick=GetTickCount();
		CountBytes=0;
		Peek=0;
		StartTick=LastTick;
		BytesSent=0;
	}
	ELSE{
		CountBytes+=Bytes;
		GetTickCount();
		EBX=EAX-LastTick;
		IF(EBX>=1000){
			LastTick=EAX;
			EAX=CountBytes/EBX*1000;
			IF(EAX>Peek)Peek=EAX;
			PrintCPS("\r---> ",EAX);
			CountBytes=0;
		}
	}
}

void PrintCPS(LPCSTR Tag; DWORD CPS)
{
	printf("%sCPS KPS: %u    ", Tag,CPS/1024);
}

/*-----------------------------------------------------------
 * Function: GetAddr()
 *
 * Description: Given a string, it will return an IP address.
 *   - first it tries to convert the string directly
 *   - if that fails, it tries o resolve it as a hostname
 *
 * WARNING: gethostbyname() is a blocking function
 */
DWORD GetAddr(dword szHost)
{
  /* check that we have a string */
	EAX=INADDR_ANY;
  IF(szHost){
    /* check for a dotted-IP address string */
    inet_addr(szHost);
    /* If not an address, then try to resolve it as a hostname */
		$push EAX
    IF((EAX==INADDR_NONE)&&(strcmp(szHost,"255.255.255.255"))){
			$pop EAX
      gethostbyname(szHost);
      IF(EAX){  /* success */
//        lAddr=*((u_long FAR *)(lpstHost->h_addr));
				EAX=DSDWORD[EAX+12];
				EAX=DSDWORD[EAX];
				EAX=DSDWORD[EAX];
      }
			ELSE EAX=INADDR_ANY;   /* failure */
    }
		ELSE $pop EAX
  }
} /* end GetAddr() */
