
/*
 * static char *rcsid_sockets_c =
 *    "$Id: newsocket.c,v 1.3 1997/02/04 10:57:24 master Exp $";
 */

/*
    CrossFire, A Multiplayer game for X-windows

    Copyright (C) 1992 Frank Tore Johansen

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    The author can be reached via e-mail to mark@pyramid.com
*/

/* newsocket.c contains some base functions that both the client and server
 * can use.  As such, depending what we are being compiled for will
 * determine what we can include.  the client is designed have
 * CFCLIENT defined as part of its compile flags.
 */

#ifndef CFCLIENT
#include <global.h>
#else
#include <client.h>
void LOG (int logLevel, char *format, ...)
{
    va_list ap;
    va_start(ap, format);
    vfprintf(stderr, format, ap);
    va_end(ap);
}
/* We don't care what these values are in the client, since
 * we toss them
 */
#define llevDebug 0
#define llevError 0
#include <errno.h>

#endif
#include <newclient.h>

void SockList_Init(SockList *sl)
{
    sl->len=0;
    sl->buf=NULL;
}

void SockList_AddChar(SockList *sl, char c)
{
    sl->buf[sl->len]=c;
    sl->len++;
}

void SockList_AddShort(SockList *sl, uint16 data)
{
    sl->buf[sl->len++]= (data>>8)&0xff;
    sl->buf[sl->len++] = data & 0xff;
}


void SockList_AddInt(SockList *sl, uint32 data)
{
    sl->buf[sl->len++]= (data>>24)&0xff;
    sl->buf[sl->len++]= (data>>16)&0xff;
    sl->buf[sl->len++]= (data>>8)&0xff;
    sl->buf[sl->len++] = data & 0xff;
}

/* Basically does the reverse of SockList_AddInt, but on
 * strings instead.  Same for the GetShort, but for 16 bits.
 */
int GetInt_String(unsigned char *data)
{
    return ((data[0]<<24) + (data[1]<<16) + (data[2]<<8) + data[3]);
}

short GetShort_String(unsigned char *data) {
    return ((data[0]<<8)+data[1]);
}

/* this readsfrom fd and puts the data in sl.  We return true if we think
 * we have a full packet, 0 if we have a partial packet.  The only processing
 * we do is remove the intial size value.  len (As passed) is the size of the
 * buffer allocated in the socklist.  We make the assumption the buffer is
 * at least 2 bytes long.
 */
 
int SockList_ReadPacket(int fd, SockList *sl, int len)
{
    int stat,toread,readsome=0;
    extern int errno;

    /* We already have a partial packet */
    if (sl->len<2) {
	stat=read(fd, sl->buf + sl->len, 2-sl->len);
	if (stat<0) {
	    /* In non blocking mode, EAGAIN is set when there is no
	     * data available.
	     */
	    if (errno!=EAGAIN) {
		perror("ReadPacket got an error.");
		LOG(llevDebug,"ReadPacket got error %d, returning 0",errno);
	    }
	    return 0;	/*Error */
	}
	if (stat==0) return -1;
	sl->len += stat;
	if (stat<2) return 0;	/* Still don't have a full packet */
	readsome=1;
    }
    /* Figure out how much more data we need to read.  Add 2 from the
     * end of this - size header information is not included.
     */
    toread = 2+(sl->buf[0] << 8) + sl->buf[1] - sl->len;
    if ((toread + sl->len) > len) {
	LOG(llevError,"SockList_ReadPacket: Want to read more bytes than will fit in buffer.\n");
	abort();
    }
    do {
	stat = read(fd, sl->buf+ sl->len, toread);
	if (stat<0) {
	    if (errno!=EAGAIN) {
		perror("ReadPacket got an error.");
		LOG(llevDebug,"ReadPacket got error %d, returning 0",errno);
	    }
	    return 0;	/*Error */
	}
	if (stat==0) return -1;
	sl->len += stat;
	toread -= stat;
	if (toread==0) return 1;
	if (toread < 0) {
	    LOG(llevError,"SockList_ReadPacket: Read more bytes than desired.");
	    return 1;
	}
    } while (toread>0);
    return 0;
}

/* This writes data to the socket.  we precede the len information on the
 * packet.  Len needs to be passed here because buf could be binary
 * data
 */
static int write_socket(int fd, unsigned char *buf, int len)
{
    int amt=0;
    unsigned char *pos=buf;

    /* If we manage to write more than we wanted, take it as a bonus */
    while (len>0) {
	amt=write(fd, pos, len);
	if (amt < 0) { /* We got an error */
	    LOG(llevError,"New socket (fd=%d) write failed.\n", fd);
	    return -1;
	}
	if (amt==0) {
	    LOG(llevError,"Write_To_Socket: No data written out.\n");
	}
	len =- amt;
	pos += amt;
    }
    return 0;
}

/* Send With Handling - cnum is the client number, msg is what we want
 * to send.
 */
static int send_socklist(int fd,SockList  msg)
{
    unsigned char sbuf[2];

    sbuf[0] = ((uint32)(msg.len) >> 8) & 0xFF;
    sbuf[1] = ((uint32)(msg.len)) & 0xFF;

    write_socket(fd, sbuf, 2);
    return write_socket(fd, msg.buf, msg.len);
}

/* Takes a string of data, and writes it out to the socket. A very handy
 * shortcut function.
 */
int cs_write_string(int fd, char *buf, int len)
{
    SockList sl;

    sl.len = len;
    sl.buf = (unsigned char*)buf;
    return send_socklist(fd, sl);
}
