#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> 
#include <errno.h>
extern int errno;
#include <time.h>
#include <sys/time.h>
#include <signal.h>
#include <math.h>
#include <sys/stat.h>
#include <unistd.h>

#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>          /* For inet_ntoa */
#include <sys/ioctl.h>
#include <fcntl.h>

#include "../include/string.h"

#include "ytypes.h"
#include "soundpaths.h"
#include "ysound.h"
#include "audiocd.h"
#include "ymixer.h"
#include "ymode.h"
#include "yconnection.h"
#include "yhost.h"
#include "playstack.h"
#include "ynet.h"

#include "yiff.h"
#include "options.h"


static u_int64_t htonl64(u_int64_t x);

static void YNetPrintError(
	FILE *stream,
	YConnectionNumber con_num,
	u_int32_t chunk_length,
	u_int16_t major_op_code,
	u_int16_t minor_op_code,
	const char *mesg
);

static int YNetSendToConnection(
	YConnectionNumber con_num, const char *buf, int len
);

void YShutdownSocket(int socket, int how, time_t timeout);
void YCloseSocketWait(int socket, time_t timeout);

int YNetSendAudioChangePreset(
	YConnectionNumber con_num,
	const char *audio_mode_name
);
int YNetSendAudioChangeValues(
	YConnectionNumber con_num,
	int sample_size,
	int channels,
	YDataLength sample_rate,
	int direction,
	int allow_fragmenting,
	int num_fragments,
	int fragment_size
);

int YNetParseAudioChange(YCNP_STD_INPUTS_PROTO);

int YNetSendServerStats(
	YConnectionNumber con_num,
	int protocol_version_major,
	int protocol_version_minor,
	Coefficient cycle_load,
	unsigned long start_time,
	const char *vendor_name,
	const char *server_name,
	const char *sound_name
);
int YNetParseServerStats(YCNP_STD_INPUTS_PROTO);

int YNetSendCycleChange(YConnectionNumber con_num, long cycle_us);
int YNetParseCycleChange(YCNP_STD_INPUTS_PROTO);

int YNetSendDisconnect(YConnectionNumber con_num, u_int16_t reason);
int YNetParseDisconnect(YCNP_STD_INPUTS_PROTO);

int YNetSendSetHost(
	YConnectionNumber con_num,
	u_int16_t minor_op_code,
	const YIPUnion *ip
);
int YNetParseSetHost(YCNP_STD_INPUTS_PROTO);

int YNetSendSetMixerDevice(
	YConnectionNumber con_num,
	int mixer_code,
	Coefficient value1, Coefficient value2,
	Coefficient value3, Coefficient value4
);
int YNetParseMixerDevice(YCNP_STD_INPUTS_PROTO);

int YNetSendSoundObjectPlay(
	YConnectionNumber con_num, YID yid,
	YDataPosition position, YDataLength length,
	int repeats, int total_repeats,
	YVolumeStruct *volume,
	int sample_rate
);
int YNetParseSoundObjectPlay(YCNP_STD_INPUTS_PROTO);

int YNetSendSoundObjectKill(YConnectionNumber con_num, YID yid);
int YNetParseSoundObjectKill(YCNP_STD_INPUTS_PROTO);

int YNetSendSoundObjectAttributes(
	YConnectionNumber con_num,
	const char *path,
	int format,
	int sample_size,
	int channels,
	int sample_rate,
	YDataLength length
);
int YNetParseSoundObjectAttributes(YCNP_STD_INPUTS_PROTO);

int YNetSendShutdown(YConnectionNumber con_num, u_int16_t reason);
int YNetParseShutdown(YCNP_STD_INPUTS_PROTO);

int YNetSendSync(YConnectionNumber con_num, long us);
int YNetParseSync(YCNP_STD_INPUTS_PROTO);

int YNetSendAudioStats(
	YConnectionNumber con_num,
	Audio *audio
);
int YNetParseAudioStats(YCNP_STD_INPUTS_PROTO);

int YNetSendListAudioModes(
	YConnectionNumber con_num,
	const char *audio_mode_name,
	int sample_rate, int channels,
	int sample_size, int fragment_size_bytes,
	char direction, Boolean allow_fragmenting, int num_fragments
);
int YNetParseListAudioModes(YCNP_STD_INPUTS_PROTO);

int YNetSendPlaySoundObjectValues(
	YConnectionNumber con_num, PlayStack *ps_ptr
);
int YNetParsePlaySoundObjectValues(YCNP_STD_INPUTS_PROTO);

int YNetSendClientMessage(
	YConnectionNumber con_num, int format, int type,
	const char *message, YDataLength length
);
int YNetParseClientMessage(YCNP_STD_INPUTS_PROTO);

int YNetSendYSHMSoundOpen(
	YConnectionNumber con_num, int yshm_id
);
int YNetSendYSHMSoundClose(
	YConnectionNumber con_num, int yshm_id
);
int YNetParseYSHMSoundOpen(YCNP_STD_INPUTS_PROTO);
void YNetDoNotifyAllYSHMSoundClose(void);

int YNetSendAudioCDPlayTrack(
	YConnectionNumber con_num,
	int track_number
);
int YNetSendAudioCDStop(
	YConnectionNumber con_num
);
int YNetSendAudioCDEject(
	YConnectionNumber con_num
);
int YNetParseAudioCD(YCNP_STD_INPUTS_PROTO);

int YNetSendListAudioCDTracks(
	YConnectionNumber con_num,
	int track_number,
	unsigned long track_start_time,
	unsigned long track_length,
	const char *track_name
);
int YNetParseListAudioCDTracks(YCNP_STD_INPUTS_PROTO);

static int YNetParse(
	YConnectionNumber con_num,
	const u_int8_t *buf,
	u_int32_t chunk_length,
	u_int16_t major_op_code,
	u_int16_t minor_op_code
);
int YNetRecv(YConnectionNumber con_num);;


#define ATOI(s)		(((s) != NULL) ? atoi(s) : 0)
#define ATOL(s)		(((s) != NULL) ? atol(s) : 0)
#define ATOF(s)		(((s) != NULL) ? atof(s) : 0.0f)
#define STRDUP(s)	(((s) != NULL) ? strdup(s) : NULL)

#define MAX(a,b)	(((a) > (b)) ? (a) : (b))
#define MIN(a,b)	(((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)	(MIN(MAX((a),(l)),(h)))
#define STRLEN(s)	(((s) != NULL) ? strlen(s) : 0)
#define STRISEMPTY(s)	(((s) != NULL) ? (*(s) == '\0') : 1)


extern int YAntiShift(int in);   /* In main.c */


/*
 *	Marks currently handled connection to be closed.
 */
static Boolean close_this_connection;


static u_int64_t htonl64(u_int64_t x)
{
	return(htonl(x));
#if 0
	u_int8_t *px = (u_int8_t *)&x, *py;
	py[7] = px[0];
	py[6] = px[1];
	py[5] = px[2];
	py[4] = px[3];
	py[3] = px[4];
	py[2] = px[5];
	py[1] = px[6];
	py[0] = px[7];
	return(*(u_int64_t *)py);
#endif
}

/*
 *	Procedure to print network error message.
 */
static void YNetPrintError(
	FILE *stream,
	YConnectionNumber con_num,
	u_int32_t chunk_length,
	u_int16_t major_op_code,
	u_int16_t minor_op_code,
	const char *mesg
)
{
	if(stream == NULL)
	    return;

	fprintf(
	    stream,
	    "Y server protocol error from connection: %i\n",
	    con_num
	);
	fprintf(      
	    stream,  
	    "    Major OP Code: %i\n",
	    major_op_code
	);
	fprintf(
	    stream,
	    "    Minor OP Code: %i\n",
	    minor_op_code 
	);
	fprintf(
	    stream,
	    "    Segment Size: %i bytes\n",
	    chunk_length
	);
	if(mesg != NULL)
	    fprintf(
		stream,
		"    Remarks: %s\n",   
		mesg
	    );
}

/*
 *	Send buffer buf of length len to connection, inputs assued valid
 *	and connection assumed connected.
 *
 *	The connection's socket is checked to see if it's available for
 *	sending data. Returns -1 if it was not able to send the buffer
 *	or the number of bytes sent.
 */
static int YNetSendToConnection(
	YConnectionNumber con_num, const char *buf, int len
)
{
	struct timeval timeout;
	fd_set writefds;
	int bytes_sent;
	YConnection *con = yconnection[con_num];	/* Assumed valid */
	int s = con->socket;

	timeout.tv_sec = 0;
	timeout.tv_usec = 0;
	FD_ZERO(&writefds);
	FD_SET(s, &writefds);
	select(s + 1, NULL, &writefds, NULL, &timeout);

	if(!FD_ISSET(s, &writefds))
	    return(-1);

	bytes_sent = send(s, buf, len, 0);
	return((bytes_sent >= 0) ? bytes_sent : -1);
}


/*
 *      Shuts down the socket.
 */
void YShutdownSocket(int socket, int how, time_t timeout)
{
        if(socket < 0)
            return;

        shutdown(socket, how);
}

/*
 *	Calls recv() until it returns 0 and then closes the socket.
 */
void YCloseSocketWait(int socket, time_t timeout)
{
        char buf[1024];

        if(socket < 0)
            return;

        while(recv(
            socket,
            buf, sizeof(buf),
            MSG_NOSIGNAL
        ) > 0);

        close(socket);
}


/*
 *	Send Audio mode change.
 */
int YNetSendAudioChangePreset(
	YConnectionNumber con_num,
	const char *audio_mode_name
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <...modename...>
	 */
	const YDataLength this_seg_len = 8 + YAudioNameMax;
	YDataLength actual_seg_len;
	int name_len;
	char buf[this_seg_len];


	if(audio_mode_name == NULL)
	    return(-1);

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);  
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioChange);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioChangePreset);

	name_len = MIN(strlen(audio_mode_name), YAudioNameMax);
	strncpy(
	    &buf[8],
	    audio_mode_name,
	    name_len
	);
	/* Do not null terminate string */

	/* Since this is a variable length segment we need to
	 * recalculate the segment length after the formatting
	 * above and update the segment header's chunk size.
	 */
	actual_seg_len = 8 + name_len;
	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);

	return(YNetSendToConnection(con_num, buf, actual_seg_len));

}
/*
 *	Sends audio values change.
 */
int YNetSendAudioChangeValues(
	YConnectionNumber con_num,
	int sample_size,
	int channels,
	YDataLength sample_rate,
	int direction,
	int allow_fragmenting,
	int num_fragments,
	int fragment_size
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u16_samplesize> <u16_channels> <u32_samplerate>
	 * <u8_direction>
	 * <u8_allowfrags> <u32_numfrags> <u32_fragsize>
	 */
	const YDataLength this_seg_len = 8 + 8 + 1 + 9;
	char buf[this_seg_len];


	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioChange);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioChangeValues);

	*(u_int16_t *)(&buf[8]) = htons((u_int16_t)sample_size);
	*(u_int16_t *)(&buf[10]) = htons((u_int16_t)channels);
	*(u_int32_t *)(&buf[12]) = htonl((u_int32_t)sample_rate);

	*(u_int8_t *)(&buf[16]) = (u_int8_t)direction;

	*(u_int8_t *)(&buf[17]) = (u_int8_t)allow_fragmenting;
	*(u_int32_t *)(&buf[18]) = htonl((u_int32_t)num_fragments);
	*(u_int32_t *)(&buf[22]) = htonl((u_int32_t)fragment_size);

	return(YNetSendToConnection(con_num, buf, this_seg_len));
}
/*
 *	Handles Audio mode change.
 */
int YNetParseAudioChange(YCNP_STD_INPUTS_PROTO)
{
	int i;
	YConnection *con;
	YMode *ymode_ptr;
	Audio *audio_ptr;

	/* Change mode values or change to preset mode? */
	if(minor_op_code == YAudioChangeValues)
	{
	    /* Parse set Audio values */

	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u16_samplesize> <u16_channels> <u32_samplerate>
	     * <u8_direction>
	     * <u8_allowfrags> <u32_numfrags> <u32_fragsize>
	     */
	    const YDataLength this_seg_len = 8 + 8 + 1 + 9;
	    int sample_size;
	    int channels;
	    YDataLength sample_rate;
	    int direction;
	    int allow_fragmenting;
	    int num_fragments;
	    int fragment_size_bytes;


	    if(chunk_length < this_seg_len)
		return(-1);

	    /* Get Audio values */
	    sample_size = CLIP(
		ntohs(*(u_int16_t *)(&buf[8])), 8, 16
	    );
	    channels = CLIP(
		ntohs(*(u_int16_t *)(&buf[10])), 1, 2
	    );
	    sample_rate = MAX(
		ntohl(*(u_int32_t *)(&buf[12])), 0
	    );

	    direction = (int)buf[16];

	    allow_fragmenting = (int)buf[17];
	    num_fragments = ntohl(*(u_int32_t *)(&buf[18]));
	    fragment_size_bytes = ntohl(*(u_int32_t *)(&buf[22]));


	    /* Special case: If the sample rate is 0, this implies
	     * that the DSP device should be closed (shelled out)
	     * to let other applications open it.
	     */
	    if(sample_rate == 0)
	    {
		/* Shell out the recorder */
		YSoundShellOut(recorder);

		/* Send back shell out response */
		YNetSendAudioChangeValues(
		    con_num,
		    sample_size,
		    channels,
		    0,		/* Sample rate should be 0 */
		    direction,
		    allow_fragmenting,
		    num_fragments,
		    fragment_size_bytes
		);

		/* Return now, do not delete playstacks or modify Audio */
		return(0);
	    }

	    /* Delete all playstacks and notify their respective owners
	     * about the deletion.
	     */
	    for(i = 0; i < total_playstacks; i++)
		YiffDestroyPlaystack(i);

	    /* Notify all connections about YSHM sound closing */
	    YNetDoNotifyAllYSHMSoundClose();

	    /* Shutdown and delete the recorder */
	    YSoundShutdown(recorder);
	    free(recorder);
	    recorder = NULL;


	    /* Set new Audio values to option Audio values */
	    audio_ptr = &option.audio;
	    audio_ptr->sample_size = sample_size;
	    audio_ptr->channels = channels;
	    audio_ptr->sample_rate = sample_rate;

#ifdef OSS_BUFFRAG
	    audio_ptr->allow_fragments = ((allow_fragmenting) ?
		True : False
	    );
	    audio_ptr->num_fragments = num_fragments;
	    audio_ptr->fragment_size = YAntiShift(fragment_size_bytes - 1);
#endif  /* OSS_BUFFRAG */

	    audio_ptr->direction = direction;


	    /* Allocate recorder structure */
	    recorder = (Recorder *)calloc(1, sizeof(Recorder));
	    if(recorder == NULL)
		return(-1);
	    /* Initialize recorder */
	    if(YSoundInit(recorder, &option.audio))
	    {
		/* Failed to initialize */

		/* Shutdown and delete the recorder */
		YSoundShutdown(recorder);
		free(recorder);
		recorder = NULL;

		/* Send back notification of initialization failure */
		YNetSendAudioChangeValues(
		    con_num,
		    0, 0, 0,
		    0, 0, 0, 0
		);

		return(-1);
	    }

	    /*  Need to sync audio device right after
	     *  initialization.
	     */
	    YSoundSync(recorder, 0);


	    /* Notify all connections about Audio values change */
	    for(i = 0; i < total_yconnections; i++)
	    {
		con = yconnection[i];
		if((con != NULL) ? (con->socket < 0) : True)
		    continue;

		YNetSendAudioChangeValues(
		    i,
		    sample_size,
		    channels,
		    sample_rate,
		    direction,
		    allow_fragmenting,
		    num_fragments,
		    fragment_size_bytes
		);
	    }
	}
	else
	{
	    /* Parse change preset Audio mode */

	    /*   <u32_cs> <u16_majop> <u16_minop>
	     *   <...modename...>
	     */
	    const YDataLength base_seg_len = 8;
	    int name_len;
	    int mode_num;
	    int mode_name_different = 0;
	    char mode_name[YAudioNameMax];
	    Audio *audio_ptr;


	    if(chunk_length < base_seg_len)
		return(-1);

	    name_len = (YDataLength)chunk_length - (YDataLength)base_seg_len;
	    if(name_len >= YAudioNameMax)
		name_len = YAudioNameMax - 1;

	    if(name_len > 0)
	    {
		strncpy(
		    mode_name,  
		    &buf[8],
		    name_len
		);
		mode_name[name_len] = '\0';

		/* Check if the given Audio mode name is different from
		 * the one specified in the Audio structure.
		 */
		if(recorder->audio.audio_mode_name != NULL)
		{
		    if(strcasecmp(
			recorder->audio.audio_mode_name,
			mode_name
		    ))
		        mode_name_different = 1;
		}
		else
		{
		    /* Last Audio mode name not recorded on Audio
		     * structure, so assume its different.
		     */
		    mode_name_different = 1;
		}
		/* Mode name should always be considered different if
		 * Audio is currently shelled out.
		 */
		if(recorder->audio.shelled_out)
		    mode_name_different = 1;


		/* Match preset Audio mode, skip if current Audio
		 * mode matches.
		 */
		mode_num = YModeMatch(mode_name);
		if(YModeIsAllocated(mode_num) &&
		   mode_name_different
		)
		{
		    ymode_ptr = ymode[mode_num];

		    /* Delete all playstacks and notify their respective
		     * owners about the deletion.
		     */
		    for(i = 0; i < total_playstacks; i++)
			YiffDestroyPlaystack(i);

		    /* Notify all connections about YSHM sound closing */
		    YNetDoNotifyAllYSHMSoundClose();

		    /* Shutdown and delete the recorder */
		    YSoundShutdown(recorder);
		    free(recorder);
		    recorder = NULL;


		    /* Set option Audio values to new mode's values */
		    audio_ptr = &option.audio;

		    /* Audio modes are always considered user set */
		    audio_ptr->cycle_set = CYCLE_SET_USER;

		    audio_ptr->cycle.ms = ymode_ptr->cycle.ms;
		    audio_ptr->cycle.us = ymode_ptr->cycle.us;

		    /* Leave audio_ptr->compensated_cycle alone */

		    audio_ptr->write_ahead.ms = ymode_ptr->write_ahead.ms;
		    audio_ptr->write_ahead.us = ymode_ptr->write_ahead.us;

		    audio_ptr->sample_size = ymode_ptr->sample_size;
		    audio_ptr->channels = ymode_ptr->channels;
		    audio_ptr->sample_rate = ymode_ptr->sample_rate;

#ifdef OSS_BUFFRAG
		    audio_ptr->allow_fragments = ymode_ptr->allow_fragments;
		    audio_ptr->num_fragments = ymode_ptr->num_fragments;
		    audio_ptr->fragment_size = ymode_ptr->fragment_size;
#endif  /* OSS_BUFFRAG */

		    audio_ptr->flip_stereo = ymode_ptr->flip_stereo;
		    audio_ptr->direction = ymode_ptr->direction;


		    /* Allocate recorder structure */
		    recorder = (Recorder *)calloc(1, sizeof(Recorder));
		    if(recorder == NULL)
			return(-1);
		    /* Initialize recorder */
		    if(YSoundInit(recorder, audio_ptr))
		    {
			/* Failed to initialize */

			/* Shutdown and delete the recorder */
			YSoundShutdown(recorder);
			free(recorder);
			recorder = NULL;

			/* Send back notification of initialization
			 * failure.
			 */
			YNetSendAudioChangePreset(con_num, "");

			return(-1);
		    }

		    /*  Need to sync audio device right after
		     *  initialization.
		     */
		    YSoundSync(recorder, 0);

		    /* Record name of preset Audio mode used to
		     * set this Audio.
		     */
		    free(recorder->audio.audio_mode_name);
		    recorder->audio.audio_mode_name = STRDUP(
			ymode_ptr->name
		    );

		    /* Notify all connections of Audio mode change */
		    for(i = 0; i < total_yconnections; i++)
		    {
			con = yconnection[i];
			if((con != NULL) ? (con->socket < 0) : True)
			    continue;

			YNetSendAudioChangePreset(i, mode_name);
		    }
		}
		/* Audio mode the same? */
		else if(!mode_name_different)
		{
		    /* Send back fake success response */
		    YNetSendAudioChangePreset(con_num, mode_name);
		}
		else
		{
		    /* No such preset Audio mode */

		    /* Notify connection of Audio mode change
		     * failure (send back empty Audio mode name).
		     */
		    YNetSendAudioChangePreset(con_num, "");
		}
	    }
	    else
	    {
		/* No mode name specified */

		/* Notify connection of Audio mode change
		 * failure (send back empty Audio mode name).
		 */
		YNetSendAudioChangePreset(con_num, "");
	    }
	}


	return(0);
}

/*
 *	Sends server stats.
 */
int YNetSendServerStats(
	YConnectionNumber con_num,
	int protocol_version_major,
	int protocol_version_minor,
	Coefficient cycle_load,
	unsigned long start_time,
	const char *vendor_name,
	const char *server_name,
	const char *sound_name
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_protocol_version_major> <u32_protocol_version_minor>
	 * <u16_load>
	 * <u64_start_time>
	 * <...vendor_name, server_name, sound_name...>
	 */
	const YDataLength this_seg_len = 8 + 8 + 2 + 8 +
	    YVendorNameMax + 1 + YServerNameMax + 1 + YSoundNameMax + 1;
	YDataLength actual_seg_len;
	int len, names_len;
	const char *s;
	char *ts, buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YServerStats);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YServerStatsSet);

	*(u_int32_t *)(&buf[8]) = htonl((u_int32_t)protocol_version_major);
	*(u_int32_t *)(&buf[12]) = htonl((u_int32_t)protocol_version_minor);

	*(u_int16_t *)(&buf[16]) = htons((u_int16_t)(
	    cycle_load * (Coefficient)((u_int16_t)-1)
	));
	*(u_int64_t *)(&buf[18]) = htonl64((u_int64_t)start_time);

	ts = (char *)&buf[26];
	names_len = 0;
	s = (vendor_name != NULL) ? vendor_name : "";
	len = MIN(strlen(s), YVendorNameMax);
	if(len > 0)
	    strncpy(&ts[names_len], s, len);
	ts[names_len + len] = '\0';

	names_len += len + 1;
	s = (server_name != NULL) ? server_name : "";
	len = MIN(strlen(s), YServerNameMax);
	if(len > 0)
	    strncpy(&ts[names_len], s, len);
	ts[names_len + len] = '\0';

	names_len += len + 1;
	s = (sound_name != NULL) ? sound_name : "";
	len = MIN(strlen(s), YSoundNameMax);
	if(len > 0)
	    strncpy(&ts[names_len], s, len);
	ts[names_len + len] = '\0';

	names_len += len + 1;

	/* Since this is a variable length segment we need to
	 * recalculate the segment length after the formatting
	 * above and update the segment header's chunk size.
	 */
	actual_seg_len = 8 + 8 + 2 + 8 + names_len;
	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);

	return(YNetSendToConnection(con_num, buf, actual_seg_len));
}
/*
 *	Parses server stats.
 */
int YNetParseServerStats(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YServerStatsGet)
	{
	    /* Parse server stats get */

	    /* <u32_cs> <u16_majop> <u16_minop>
	     */
	    const YDataLength this_seg_len = 8;
	
	    if(chunk_length < this_seg_len)
		return(-1);

	    YNetSendServerStats(
		con_num,
		YPROTOCOL_VERSION_MAJOR,
		YPROTOCOL_VERSION_MINOR,
		ystats.cycle_load,
		ystats.start_time,
		PROG_VENDOR_NAME,
		"",
		recorder->audio.sound_name
	    );
	}
	else if(minor_op_code == YServerStatsSet)
	{
	    /* Parse server stats set */
 
	    /* <u32_cs> <u16_majop> <u16_minop>
	     */
	    const YDataLength base_seg_len = 8;

	    if(chunk_length < base_seg_len)
		return(-1);
	}

	return(0);
}

/*
 *	Sends cycle change.
 */
int YNetSendCycleChange(YConnectionNumber con_num, long cycle_us)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_cycleus>
	 */
	const YDataLength this_seg_len = 8 + 4;
	char buf[this_seg_len];


	/* Most UNIXes requires cycles > than 100 us */
	if(cycle_us < 100)
	    cycle_us = 100;

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YCycleChange);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)0);

	*(u_int32_t *)(&buf[8]) = htonl((u_int32_t)cycle_us);

	return(YNetSendToConnection(con_num, buf, this_seg_len));
}
/*
 *	Handles cycle change.
 */
int YNetParseCycleChange(YCNP_STD_INPUTS_PROTO)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_cycleus>
	 */
	const YDataLength this_seg_len = 8 + 4;
	long cycle_us;
	int i;
	YConnection *con;


	if(chunk_length < this_seg_len)
	    return(-1);

	cycle_us = ntohl(*(u_int32_t *)(&buf[8]));
	/* Most UNIXes requires this > than 100 us */
	if(cycle_us < 100)
	    cycle_us = 100;

	/* Set new cycle */
	if(recorder == NULL)
	{
	    /* Recorder not available, send error response */
	    YNetSendCycleChange(con_num, 0);
	}
	else
	{
	    /* Set new cycle */
	    if(recorder->audio.cycle_set == CYCLE_SET_USER)
	    {
		recorder->audio.cycle.ms = cycle_us / 1000;
		recorder->audio.cycle.us = cycle_us % 1000;

		recorder->audio.compensated_cycle.ms =
		    recorder->audio.cycle.ms;
		recorder->audio.compensated_cycle.us =
		    recorder->audio.cycle.us;

		recorder->audio.write_ahead.ms = (long)(cycle_us * 1.5) /
		    1000;
		recorder->audio.write_ahead.us = (long)(cycle_us * 1.5) %
		    1000;

		option.audio.write_ahead.ms =
		    recorder->audio.write_ahead.ms;
		option.audio.write_ahead.us =
		    recorder->audio.write_ahead.us;


		/* Notify all connections of cycle change */
		for(i = 0; i < total_yconnections; i++)
		{
		    con = yconnection[i];
		    if((con != NULL) ? (con->socket < 0) : True)
			continue;

		    YNetSendCycleChange(i, cycle_us);
		}
	    }
	    else
	    {
		/* Program or hardware set cycle */
		cycle_us = (recorder->audio.cycle.ms * 1000) +
		    recorder->audio.cycle.us;

		YNetSendCycleChange(con_num, cycle_us);
	    }
	}


	return(0);
}


/*
 *	Sends disconnect.
 */
int YNetSendDisconnect(YConnectionNumber con_num, u_int16_t reason)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u16_reason>
	 */
	const YDataLength this_seg_len = 8 + 2;
	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YDisconnect);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)0);

	*(u_int16_t *)(&buf[8]) = htons((u_int16_t)reason);

	return(YNetSendToConnection(con_num, buf, this_seg_len));            
}
/*
 *	Handle disconnect.
 */
int YNetParseDisconnect(YCNP_STD_INPUTS_PROTO)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u16_reason>
	 */
	const YDataLength this_seg_len = 8 + 2;
	u_int16_t reason;


	if(chunk_length < this_seg_len)
	    return(-1);

	reason = ntohs(*(u_int16_t *)(&buf[8]));

	/* Mark currently handled connection number to be closed */
	close_this_connection = True;

	return(0);
}

/*
 *	Sends set host.
 */
int YNetSendSetHost(
	YConnectionNumber con_num,
	u_int16_t minor_op_code,
	const YIPUnion *ip
)
{
	/* <u32_cs> <u16_majop> <u16_minop> 
	 * <u8_ipc1> <u8_ipc2> <u8_ipc3> <u8_ipc4>
	 */
	const YDataLength this_seg_len = 8 + 4;

	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSetHost);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)minor_op_code);

	*(u_int8_t *)(&buf[8]) = ip->charaddr[0];
	*(u_int8_t *)(&buf[9]) = ip->charaddr[1];
	*(u_int8_t *)(&buf[10]) = ip->charaddr[2];
	*(u_int8_t *)(&buf[11]) = ip->charaddr[3];

	return(YNetSendToConnection(con_num, buf, this_seg_len));
}
/*
 *	Handles set host.
 */
int YNetParseSetHost(YCNP_STD_INPUTS_PROTO)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u8_ipc1> <u8_ipc2> <u8_ipc3> <u8_ipc4>
	 */
	const YDataLength this_seg_len = 8 + 4;
	YIPUnion ip;
	int status;

	if(chunk_length < this_seg_len)
	    return(-1);

	ip.charaddr[0] = (u_int8_t)buf[8];
	ip.charaddr[1] = (u_int8_t)buf[9];
	ip.charaddr[2] = (u_int8_t)buf[10];
	ip.charaddr[3] = (u_int8_t)buf[11];

	switch(minor_op_code)
	{
	  case YSetHostAdd:
	    status = YHostAllocate(&ip);
	    if(status < 0)
		fprintf(stderr,
		    "Cannot add YHost %i.%i.%i.%i.\n",
		    ip.charaddr[0],
		    ip.charaddr[1],
		    ip.charaddr[2],          
		    ip.charaddr[3]
		);
	    break;

	  case YSetHostRemove:
	    status = YHostDeleteByHost(&ip);
	    if(status < 0)
		fprintf(stderr,
		    "Cannot remove YHost %i.%i.%i.%i.\n",
		    ip.charaddr[0],
		    ip.charaddr[1],
		    ip.charaddr[2],
		    ip.charaddr[3] 
		);
	    break;
	}

	/* Notify connection of host add or remove */
	YNetSendSetHost(
	    con_num,
	    minor_op_code,
	    &ip
	);

	return(0);
}

/*
 *      Sends a mixer device set.
 */
int YNetSendSetMixerDevice(
	YConnectionNumber con_num,
	int mixer_code,
	Coefficient value1, Coefficient value2,
	Coefficient value3, Coefficient value4
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u16_mixercode>
	 * <u32_value1> <u32_value2>
	 * <u32_value3> <u32_value4>
	 */
	const YDataLength this_seg_len = 8 + 2 + 8 + 8;

	char buf[this_seg_len];
	u_int32_t ival[4];


	ival[0] = (u_int32_t)rint(value1 * (Coefficient)((u_int32_t)-1));
	ival[1] = (u_int32_t)rint(value2 * (Coefficient)((u_int32_t)-1));
	ival[2] = (u_int32_t)rint(value3 * (Coefficient)((u_int32_t)-1));
	ival[3] = (u_int32_t)rint(value4 * (Coefficient)((u_int32_t)-1));

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YMixerChannel);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YMixerChannelSet);

	*(u_int16_t *)(&buf[8]) = htons((u_int16_t)mixer_code);

	*(u_int32_t *)(&buf[10]) = htonl((u_int32_t)ival[0]);
	*(u_int32_t *)(&buf[14]) = htonl((u_int32_t)ival[1]);
	*(u_int32_t *)(&buf[18]) = htonl((u_int32_t)ival[2]);
	*(u_int32_t *)(&buf[22]) = htonl((u_int32_t)ival[3]);

	return(YNetSendToConnection(con_num, buf, this_seg_len));
}
/*
 *	Handles set mixer device.
 */
int YNetParseMixerDevice(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YMixerChannelSet)
	{
	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u16_mixercode>
	     * <u32_value1> <u32_value2>
	     * <u32_value3> <u32_value4>
	     */
	    const YDataLength this_seg_len = 8 + 2 + 8 + 8;
	    int i, status, mixer_code;
	    Coefficient value[YMixerValues];
	    YConnection *con;


	    if(chunk_length < this_seg_len)
		return(-1);

	    mixer_code = ntohs(*(u_int16_t *)(&buf[8]));

	    if(YMixerValues > 0)
		value[0] =
		    (Coefficient)ntohl(*(u_int32_t *)(&buf[10])) /
		    (Coefficient)((u_int32_t)-1);
	    if(YMixerValues > 1)
		value[1] =
		    (Coefficient)ntohl(*(u_int32_t *)(&buf[14])) /
		    (Coefficient)((u_int32_t)-1);
	    if(YMixerValues > 2)
		value[2] =
		    (Coefficient)ntohl(*(u_int32_t *)(&buf[18])) /
		    (Coefficient)((u_int32_t)-1);
	    if(YMixerValues > 3)
		value[3] =
		    (Coefficient)ntohl(*(u_int32_t *)(&buf[22])) /
		    (Coefficient)((u_int32_t)-1);

	    /* Set mixer device */
	    status = -1;
	    if(recorder != NULL)
	    {
		status = YMixerSet(recorder, mixer_code, value);
	    }
	    if(status < 0)
	    {
		/* Error setting mixer values, notify error */
		YNetSendSetMixerDevice(
		    con_num, mixer_code,
		    0.0, 0.0, 0.0, 0.0
		);
	    }
	    else
	    {
		/* Notify all connections about mixer channel change */
		for(i = 0; i < total_yconnections; i++)
		{
		    con = yconnection[i];
		    if((con != NULL) ? (con->socket < 0) : True)
			continue;

		    YNetSendSetMixerDevice(
		        i, mixer_code,
			(YMixerValues > 0) ? value[0] : 0.0,
			(YMixerValues > 1) ? value[1] : 0.0,
			(YMixerValues > 2) ? value[2] : 0.0,
			(YMixerValues > 3) ? value[3] : 0.0
		    );
		}
	    }
	}
	else if(minor_op_code == YMixerChannelGet)
	{
	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u16_mixercode>
	     */
	    const YDataLength this_seg_len = 8 + 2;
	    int status, mixer_code;
	    Coefficient value[YMixerValues];


	    if(chunk_length < this_seg_len)
		return(-1);

	    mixer_code = ntohs(*(u_int16_t *)(&buf[8]));

	    /* Get mixer channel device values */
	    status = -1;
	    if(recorder != NULL)
	    {
		status = YMixerGet(
		    recorder, mixer_code, value
		);
	    }
	    if(status < 0)
	    {
		/* Error getting values, respond with 0 channel values */
		YNetSendSetMixerDevice(
		    con_num, mixer_code,
		    0.0, 0.0, 0.0, 0.0
		);
	    }
	    else
	    {
		YNetSendSetMixerDevice(
		    con_num, mixer_code,
		    (YMixerValues > 0) ? value[0] : 0.0,
		    (YMixerValues > 1) ? value[1] : 0.0,
		    (YMixerValues > 2) ? value[2] : 0.0,
		    (YMixerValues > 3) ? value[3] : 0.0
		);
	    }
	}

	return(0);
}


/*
 *      Sends sound play.
 */
int YNetSendSoundObjectPlay(
	YConnectionNumber con_num, YID yid,
	YDataPosition position, YDataLength length,
	int repeats, int total_repeats,
	YVolumeStruct *volume,
	int sample_rate
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_flags> <u32_yid>
	 * <u32_position> <u32_length>
	 * <u32_repeats> <u32_total_repeats>
	 * <u16_volume_left> <u16_volume_right>
	 * <u16_volume_back_left> <u16_volume_back_right>
	 * <u32_sample_rate>
	 */
	const YDataLength this_seg_len = 8 + 8 + 8 + 8 + 4 + 4 + 4;
	char buf[this_seg_len];

	/* Set flags to match client side (see Y2/Ylib.h) */
	unsigned long flags =   (1 << 1) | (1 << 2) | (1 << 3) |
				(1 << 4) | (1 << 5) | (1 << 6) |
				(1 << 7);

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSoundObjectPlay);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)0);

	*(u_int32_t *)(&buf[8]) = htonl((u_int32_t)flags);
	*(u_int32_t *)(&buf[12]) = htonl((u_int32_t)yid);

	*(u_int32_t *)(&buf[16]) = htonl((u_int32_t)position);
	*(u_int32_t *)(&buf[20]) = htonl((u_int32_t)length);

	*(u_int32_t *)(&buf[24]) = htonl((u_int32_t)repeats);
	*(u_int32_t *)(&buf[28]) = htonl((u_int32_t)total_repeats);

	*(u_int16_t *)(&buf[32]) = htons((u_int16_t)(
	    (volume != NULL) ? volume->left : 0
	));
	*(u_int16_t *)(&buf[34]) = htons((u_int16_t)(
	    (volume != NULL) ? volume->right : 0
	));
	*(u_int16_t *)(&buf[36]) = htons((u_int16_t)(
	    (volume != NULL) ? volume->back_left : 0
	));
	*(u_int16_t *)(&buf[38]) = htons((u_int16_t)(
	    (volume != NULL) ? volume->back_right : 0
	));

	*(u_int32_t *)(&buf[40]) = htonl((u_int32_t)sample_rate);

	return(YNetSendToConnection(con_num, buf, this_seg_len));
}
/*
 *      Handles sound play.
 */
int YNetParseSoundObjectPlay(YCNP_STD_INPUTS_PROTO)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_yid> <u32_pos>
	 * <u16_volume_left> <u16_volume_right>
	 * <u16_volume_back_left> <u16_volume_back_right>
	 * <u32_sample_rate> <u32_repeats>
	 * <...path...>
	 */
/*	const YDataLength this_seg_len = 8 + 8 + 4 + 4 + 8 + YPathMax; */
	const YDataLength this_seg_len_base = 8 + 8 + 4 + 4 + 8;

	YID yid;
	YDataPosition pos;
	u_int16_t vol[4];
	int sample_rate;
	int repeats;

	int path_len;
	char path[YPathMax];
	YVolumeStruct volume;
	char *strptr;


	if(chunk_length < this_seg_len_base)
	    return(-1);

	yid = ntohl(*(u_int32_t *)(&buf[8]));
	pos = ntohl(*(u_int32_t *)(&buf[12]));
	vol[0] = ntohs(*(u_int16_t *)(&buf[16]));
	vol[1] = ntohs(*(u_int16_t *)(&buf[18]));
	vol[2] = ntohs(*(u_int16_t *)(&buf[20]));
	vol[3] = ntohs(*(u_int16_t *)(&buf[22]));

	sample_rate = ntohl(*(u_int32_t *)(&buf[24]));
	repeats = ntohl(*(u_int32_t *)(&buf[28]));

	path_len = (int)chunk_length - this_seg_len_base;
	if(path_len >= YPathMax)
	    path_len = YPathMax - 1;

	if(path_len > 0)
	{
	    strncpy(
		path,
		&buf[32],
		path_len
	    );
	    path[path_len] = '\0';
	}
	else
	{
	    /* If path is not specified, then do not continue */
	    YNetSendSoundObjectPlay(
		con_num, YIDNULL,
		0, 0, 0, 0,
		&volume,
		0
	    );
	    return(0);
	}


	/* Try to match correct complete path */
	strptr = SoundPathCompletePath(path);
	if(strptr == NULL)
	{
	    YNetSendSoundObjectPlay(
		con_num, YIDNULL,
		0, 0, 0, 0,
		&volume,
		0
	    );
	    return(0);
	}

	/* Do not start playing if recorder is not initialized */
	if(recorder == NULL)
	{
	    YNetSendSoundObjectPlay(
		con_num, YIDNULL,
		0, 0, 0, 0,
		&volume,
		0
	    );
	    return(0);
	}

	/* Create playstack */
	volume.left = vol[0];
	volume.right = vol[1];
	volume.back_left = vol[2];
	volume.back_right = vol[3];
	YiffCreatePlaystack( 
	    strptr,		/* Path */
	    con_num,		/* Owner */
	    yid,		/* YID */
	    pos,		/* Position */
	    &volume,
	    sample_rate,
	    repeats		/* 0 means repeat forever */
	);

	/* YiffCreatePlaystack() will send response */


	return(0);
}


/*
 *	Sends sound kill.
 */
int YNetSendSoundObjectKill(YConnectionNumber con_num, YID yid)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_yid>
	 */
	const YDataLength this_seg_len = 8 + 4;
	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSoundObjectKill);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)0);

	*(u_int32_t *)(&buf[8]) = htonl((u_int32_t)yid);

	return(YNetSendToConnection(con_num, buf, this_seg_len));
}
/*
 *	Handles sound kill.
 */
int YNetParseSoundObjectKill(YCNP_STD_INPUTS_PROTO)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_yid>
	 */
	const YDataLength this_seg_len = 8 + 4;
	YID yid;
	int i;
	PlayStack *ps;


	if(chunk_length < this_seg_len)
	    return(-1);

	yid = ntohl(*(u_int32_t *)(&buf[8])); 

	/* Kill playstack */
	for(i = 0; i < total_playstacks; i++)
	{
	    ps = playstack[i];
	    if(ps == NULL)
		continue;

	    if(ps->owner != con_num)
		continue;

	    if((ps->yid == YIDNULL) || (ps->yid != yid))
		continue;

	    /* Delete playstack and notify owner connection about it */
	    YiffDestroyPlaystack(i);
	}

	return(0);
}

/*
 *	Sends sound object attributes.
 */
int YNetSendSoundObjectAttributes(
	YConnectionNumber con_num,
	const char *path,
	int format,
	int sample_size,
	int channels,   
	int sample_rate,
	YDataLength length
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u16_format>
	 * <u16_samplesize> <u16_channels> <u32_samplerate>
	 * <u32_length>
	 * <...path...>
	 */
	const YDataLength this_seg_len = 8 + 2 + 8 + 4 + YPathMax;
	char buf[this_seg_len];
	YDataLength path_len, actual_seg_len;

	if(path == NULL)
	    return(-1);

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSoundObjectAttributes);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSoundObjectAttributesSet);

	*(u_int16_t *)(&buf[8]) = htons((u_int16_t)format);
	*(u_int16_t *)(&buf[10]) = htons((u_int16_t)sample_size);
	*(u_int16_t *)(&buf[12]) = htons((u_int16_t)channels);
	*(u_int32_t *)(&buf[14]) = htonl((u_int32_t)sample_rate);
	*(u_int32_t *)(&buf[18]) = htonl((u_int32_t)length);

	path_len = MIN(strlen(path), YPathMax);
	if(path_len > 0)
	    strncpy(
		&buf[22],
		path,
		path_len
	    );
	/* Do not null terminate string */

	/* Since this is a variable length segment we need to
	 * recalculate the segment length after the formatting
	 * above and update the segment header's chunk size.
	 */
	actual_seg_len = 8 + 2 + 8 + 4 + path_len;
	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);

	return(YNetSendToConnection(con_num, buf, actual_seg_len));
}
/*
 *	Handles sound object attributes.
 */
int YNetParseSoundObjectAttributes(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YSoundObjectAttributesGet)
	{
	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <...path...>
	     */
	    const YDataLength base_seg_len = 8;
/*	    const YDataLength this_seg_len = 8 + YPathMax; */
	    YDataLength path_len;
	    char path[YPathMax];
	    char *strptr;
	    AFWDataStruct af_data;


	    if(chunk_length < base_seg_len)
		return(-1);

	    path_len = (YDataLength)chunk_length -
		(YDataLength)base_seg_len;
	    if(path_len >= YPathMax)
		path_len = YPathMax - 1;

	    if(path_len > 0)
	    {
	        strncpy(
		    path,
		    (char *)(&buf[8]),
		    path_len
	        );
		path[path_len] = '\0';
	    }
	    else
	    {
		*path = '\0';
	    }


	    /* Try to match correct complete path */
	    strptr = SoundPathCompletePath(path);
	    if(strptr == NULL)
	    {
		YNetSendSoundObjectAttributes(
		    con_num,
		    "",
		    SndObjTypeNone,
		    0, 0, 0, 0
		);
		return(0);
	    }


	    /* Open audio file.*/
	    if(AFWOpen(strptr, &af_data))
	    {
		/* Error opening audio file, so reset audio file values
		 * to indicate error.
		 */
		*path = '\0';
		af_data.sndobj_format = SndObjTypeNone;
		af_data.sample_size = 0;
		af_data.channels = 0;
		af_data.sample_rate = 0;
		af_data.entire_length = 0;
	    }

	    /* Send audio file data */
	    YNetSendSoundObjectAttributes(
		con_num,
		path,		/* Send back original path */
		af_data.sndobj_format,
		af_data.sample_size,
		af_data.channels,
		af_data.sample_rate,
		af_data.entire_length
	    );

	    /* Close audio file */
	    AFWClose(
		&af_data,
		(recorder != NULL) ? &recorder->audio : NULL
	    );
	}
	else if(minor_op_code == YSoundObjectAttributesSet)
	{

	}

	return(0);
}

/*
 *	Sends shutdown.
 */
int YNetSendShutdown(YConnectionNumber con_num, u_int16_t reason)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u16_reason>
	 */
	const YDataLength this_seg_len = 8 + 2;
	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YShutdown);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)0);

	*(u_int16_t *)(&buf[8]) = htons((u_int16_t)reason);

	return(YNetSendToConnection(con_num, buf, this_seg_len));
}
/*
 *	Handles shutdown.
 */
int YNetParseShutdown(YCNP_STD_INPUTS_PROTO)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u16_reason>
	 */
	const YDataLength this_seg_len = 8 + 2;
	u_int16_t reason;


	if(chunk_length < this_seg_len)
	    return(-1);

	reason = ntohs(*(u_int16_t *)(&buf[8]));


/* Check if connection has permission to shut server down? */

	/*   Set runlevel to 1 to shutdown gently. Comfermation
	 *   will be sent to all connections just before shutdown.
	 */
	runlevel = 1;


	return(0);
}

/*
 *	Sends sync.
 */
int YNetSendSync(YConnectionNumber con_num, long us)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_cycleaheadus>
	 */
	const YDataLength this_seg_len = 8 + 4;
	char buf[this_seg_len];
	u_int32_t ca;

	if(us < 0)
	    us = 0;
	ca = us;

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSync);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)0);

	*(u_int32_t *)(&buf[8]) = htonl((u_int32_t)ca);

	return(YNetSendToConnection(con_num, buf, this_seg_len));
}
/*
 *	Handles sync.
 */
int YNetParseSync(YCNP_STD_INPUTS_PROTO)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_cycleaheadus>
	 */
	const YDataLength this_seg_len = 8 + 4;
	long cahead_us;


	if(chunk_length < this_seg_len)
	    return(-1);

	cahead_us = ntohl(*(u_int32_t *)(&buf[8]));

	if(recorder != NULL)
	{
	    /* Recaliberate cycle if program calculates it */
	    if(recorder->audio.cycle_set == CYCLE_SET_PROGRAM)
		YSoundCaliberateCycle(recorder);

	    /* Set new write ahead */
	    if(cahead_us > 0)
	    {
		recorder->audio.write_ahead.ms = cahead_us / 1000;
		recorder->audio.write_ahead.us = cahead_us % 1000;
	    }

	    /* Sync sound device */
	    YSoundSync(recorder, 0);
	}

	/* Send sync comfermation */
	YNetSendSync(con_num, cahead_us);

	return(0);
}

/*
 *	Sends set audio stats.
 */
int YNetSendAudioStats(
	YConnectionNumber con_num,
	Audio *audio
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u8_cycleset>
	 * <u32_cycleus> <u32_compensatedcycleus>
	 * <u32_writeaheadus> <u32_cumulativelatencyus>
	 * <u16_samplesize> <u16_channels> <u32_samplerate>
	 * <u32_bytespersec>
	 * <u8_allowfragments> <u32_numfragments> <u32_fragmentsize>
	 * <u8_flipstereo>
	 * <u8_direction>
	 * <...audiomodename...>
	 */
	YDataLength actual_seg_len = 8 + 1 + 8 + 8 + 8 + 4 + 9 + 1 + 1;
	const YDataLength this_seg_len = 8 + 1 + 8 + 8 + 8 + 4 + 9 + 1 + 1 +
					 YAudioNameMax;
	char buf[this_seg_len];

	if(audio == NULL)
	{
	    /* Send blank values (implying recorder not available) */
	    memset(buf, 0x00, this_seg_len * sizeof(char));

	    *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	    *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioStats);
	    *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioStatsSet);
	}
	else
	{
	    int name_len;
	    const char *audio_mode_name = (audio->audio_mode_name != NULL) ?
		audio->audio_mode_name : "";

	    *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	    *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioStats);
	    *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioStatsSet);

	    *(u_int8_t *)(&buf[8]) = (u_int8_t)audio->cycle_set;

	    *(u_int32_t *)(&buf[9]) = htonl((u_int32_t)(
		(audio->cycle.ms * 1000) + audio->cycle.us
	    ));
	    *(u_int32_t *)(&buf[13]) = htonl((u_int32_t)(
		(audio->compensated_cycle.ms * 1000) +
		audio->compensated_cycle.us
	    ));

	    *(u_int32_t *)(&buf[17]) = htonl((u_int32_t)(
		(audio->write_ahead.ms * 1000) + audio->write_ahead.us
	    ));
	    *(u_int32_t *)(&buf[21]) = htonl((u_int32_t)(
		(audio->cumulative_latency.ms * 1000) +
		audio->cumulative_latency.us
	    ));

	    *(u_int16_t *)(&buf[25]) = htons((u_int16_t)audio->sample_size);
	    *(u_int16_t *)(&buf[27]) = htons((u_int16_t)audio->channels);
	    *(u_int32_t *)(&buf[29]) = htonl((u_int32_t)audio->sample_rate);   

	    *(u_int32_t *)(&buf[33]) = htonl((u_int32_t)audio->bytes_per_second);

#ifdef OSS_BUFFRAG
	    *(u_int8_t *)(&buf[37]) = (u_int8_t)((audio->allow_fragments) ? 1 : 0);
	    *(u_int32_t *)(&buf[38]) = htonl((u_int32_t)audio->num_fragments);
	    *(u_int32_t *)(&buf[42]) = htonl((u_int32_t)(
		1 << audio->fragment_size
	    ));
#endif	/* OSS_BUFFRAG */

	    *(u_int8_t *)(&buf[46]) = (u_int8_t)((audio->flip_stereo) ? 1 : 0);
	    *(u_int8_t *)(&buf[47]) = (u_int8_t)audio->direction;

	    name_len = MIN(strlen(audio_mode_name), YAudioNameMax);
	    strncpy(&buf[48], audio_mode_name, name_len);
	    /* Do not null terminate string */

	    /* Since this is a variable length segment we need to
	     * recalculate the segment length after the formatting
	     * above and update the segment header's chunk size.
	     */
	    actual_seg_len = 8 + 1 + 8 + 8 + 8 + 4 + 9 + 1 + 1 +
		name_len;
	    *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);
	}

	return(YNetSendToConnection(con_num, buf, actual_seg_len));
}
/*
 *	Parses audio stats.
 */
int YNetParseAudioStats(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YAudioStatsGet)
	{
	    /* <u32_cs> <u16_majop> <u16_minop>
	     */
	    const YDataLength this_seg_len = 8;

	    if(chunk_length < this_seg_len)
		return(-1);

	    if(recorder == NULL)
		YNetSendAudioStats(con_num, NULL);
	    else
	        YNetSendAudioStats(con_num, &recorder->audio);
	}
	else if(minor_op_code == YAudioStatsSet)
	{

	}


	return(0);
}

/*
 *	Send list audio modes, mode values.
 */
int YNetSendListAudioModes(
	YConnectionNumber con_num,
	const char *audio_mode_name,
	int sample_rate, int channels,
	int sample_size, int fragment_size_bytes,
	char direction, Boolean allow_fragmenting, int num_fragments
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_samplerate> <u32_channels>
	 * <u32_samplesize> <u32_fragmentsizebytes>
	 * <u8_direction> <u8_allowfragmenting> <u32_numfragments>
	 * <...audiomodename...>
	 */
	const YDataLength this_seg_len = 8 + 8 + 8 + 6 + YAudioNameMax;
	char buf[this_seg_len];
	YDataLength name_len, actual_seg_len;


	if(audio_mode_name == NULL)
	    return(-1);

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YListAudioModes);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YListAudioModesSet);

	*(u_int32_t *)(&buf[8]) = htonl((u_int32_t)sample_rate);
	*(u_int32_t *)(&buf[12]) = htonl((u_int32_t)channels);
	*(u_int32_t *)(&buf[16]) = htonl((u_int32_t)sample_size);
	*(u_int32_t *)(&buf[20]) = htonl((u_int32_t)fragment_size_bytes);

	*(u_int8_t *)(&buf[24]) = (u_int8_t)direction;
	*(u_int8_t *)(&buf[25]) = (u_int8_t)((allow_fragmenting) ? 1 : 0);
	*(u_int32_t *)(&buf[26]) = htonl((u_int32_t)num_fragments);

	name_len = MIN(strlen(audio_mode_name), YAudioNameMax);
	strncpy(
	    &buf[30],
	    audio_mode_name,
	    name_len
	);
	/* Do not null terminate string */

	/* Since this is a variable length segment we need to
	 * recalculate the segment length after the formatting
	 * above and update the segment header's chunk size.
	 */
	actual_seg_len = 8 + 8 + 8 + 6 + name_len;
	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);

	return(YNetSendToConnection(con_num, buf, actual_seg_len));
}
/*
 *	Parses list audio modes.
 */
int YNetParseListAudioModes(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YListAudioModesGet)
	{
	    /* Parse list audio modes get */

	    /* <u32_cs> <u16_majop> <u16_minop>
	     */
	    const YDataLength this_seg_len = 8;
	    int i;
	    YMode *m;

		
	    if(chunk_length < this_seg_len)
		return(-1);

	    /* Send list of preset audio modes */
	    for(i = 0; i < total_ymodes; i++)
	    {
		m = ymode[i];
		if(m == NULL)
		    continue;

		YNetSendListAudioModes(
		    con_num,   
		    m->name,
		    m->sample_rate,
		    m->channels,
		    m->sample_size,
#ifdef OSS_BUFFRAG
		    (1 << m->fragment_size),
#else
		    1024,
#endif
		    m->direction,
#ifdef OSS_BUFFRAG
		    m->allow_fragments,
		    m->num_fragments
#else
		    False,
		    1
#endif
		);
	    }


	    /* End with an audio mode with no name (indicating end) */
	    YNetSendListAudioModes(
		con_num,
		"",
		0, 0,
		0, 0,
		0, False, 0
	    );
	}
	else if(minor_op_code == YListAudioModesSet)
	{

	}         
	
	return(0);
}

/*
 *	Sends play sound object values.
 */
int YNetSendPlaySoundObjectValues(
	YConnectionNumber con_num, PlayStack *ps_ptr
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_flags> <u32_yid>
	 * <u32_position> <u32_length>
	 * <u32_repeats> <u32_total_repeats>
	 * <u16_volume_left> <u16_volume_right>
	 * <u16_volume_back_left> <u16_volume_back_right>
	 * <u32_sample_rate>
	 */
	const YDataLength this_seg_len = 8 + 8 + 8 + 8 + 4 + 4 + 4;
	char buf[this_seg_len];

	/* Set flags to match client side (see Y2/Ylib.h) */
	unsigned long flags =	(1 << 1) | (1 << 2) | (1 << 3) |
				(1 << 4) | (1 << 5) | (1 << 6) |
				(1 << 7); 

	/* If no playstack is given then send error */
	if(ps_ptr == NULL)
	{
	    *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	    *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSoundObjectPlayValues);
	    *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSoundObjectPlayValuesSet);
	    *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)flags);
	    *(u_int32_t *)(&buf[12]) = htonl((u_int32_t)YIDNULL);

	    return(YNetSendToConnection(con_num, buf, this_seg_len));
	}

#define COEFFICIENT_TO_BUF16(x) (u_int16_t)( \
 (Coefficient)(x) * (Coefficient)((u_int16_t)-1) \
)
	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSoundObjectPlayValues);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSoundObjectPlayValuesSet);

	*(u_int32_t *)(&buf[8]) = htonl((u_int32_t)flags);
	*(u_int32_t *)(&buf[12]) = htonl((u_int32_t)ps_ptr->yid);

	*(u_int32_t *)(&buf[16]) = htonl((u_int32_t)ps_ptr->position);
	*(u_int32_t *)(&buf[20]) = htonl((u_int32_t)ps_ptr->data_length);

	*(u_int32_t *)(&buf[24]) = htonl((u_int32_t)ps_ptr->repeats);
	*(u_int32_t *)(&buf[28]) = htonl((u_int32_t)ps_ptr->total_repeats);

	*(u_int16_t *)(&buf[32]) = htons(COEFFICIENT_TO_BUF16(
	    ps_ptr->volume_left
	));
	*(u_int16_t *)(&buf[34]) = htons(COEFFICIENT_TO_BUF16(
	    ps_ptr->volume_right
	));
	*(u_int16_t *)(&buf[36]) = htons(COEFFICIENT_TO_BUF16(
	    ps_ptr->volume_back_left
	));
	*(u_int16_t *)(&buf[38]) = htons(COEFFICIENT_TO_BUF16(
	    ps_ptr->volume_back_right
	));

	*(u_int32_t *)(&buf[40]) = htonl((u_int32_t)ps_ptr->applied_sample_rate);
#undef COEFFICIENT_TO_BUF16

	return(YNetSendToConnection(con_num, buf, this_seg_len));
}
/*
 *	Parses play sound object values.
 */
int YNetParsePlaySoundObjectValues(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YSoundObjectPlayValuesGet)
	{
	    /* Parse play sound object values get */

	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u32_yid>
	     */
	    const YDataLength this_seg_len = 8 + 4;
	    Boolean response_sent = False;
	    YID yid;
	    int i;
	    PlayStack *ps;

	    if(chunk_length < this_seg_len)
		return(-1);

	    yid = (YID)ntohl(*(u_int32_t *)(&buf[8]));

	    /* Check if yid exists */
	    for(i = 0; i < total_playstacks; i++)
	    {
		ps = playstack[i];
		if(ps == NULL)
		    continue;

		if((ps->owner != con_num) ||
		   (ps->yid == YIDNULL)
		)
		    continue;

		if(ps->yid == yid)
		{
		    /* Send playing sound object values */
		    YNetSendPlaySoundObjectValues(con_num, ps);
		    response_sent = True;
		    break;
		}
	    }
	    if(!response_sent)
		YNetSendPlaySoundObjectValues(con_num, NULL);
	}
	else if(minor_op_code == YSoundObjectPlayValuesSet)
	{
	    /* Parse play sound object values set */

	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u32_flags> <u32_yid>
	     * <u32_pos> <u32_totalrepeats>
	     * <u16_volume_left> <u16_volume_right>
	     * <u16_volume_back_left> <u16_volume_back_right>
	     * <u32_sample_rate>
	     */
	    const YDataLength this_seg_len = 8 + 8 + 8 + 4 + 4 + 4;
	    int i;
	    u_int32_t flags;
	    YID yid;
	    u_int32_t position;
	    u_int32_t total_repeats;
	    u_int16_t vol[4];
	    u_int32_t sample_rate;
	    PlayStack *ps;

	    if(chunk_length < this_seg_len)
		return(-1);

	    flags = ntohl(*(u_int32_t *)(&buf[8]));
	    yid = ntohl(*(u_int32_t *)(&buf[12]));

	    position = ntohl(*(u_int32_t *)(&buf[16]));
	    total_repeats = ntohl(*(u_int32_t *)(&buf[20]));

	    vol[0] = ntohs(*(u_int16_t *)(&buf[24]));
	    vol[1] = ntohs(*(u_int16_t *)(&buf[26]));
	    vol[2] = ntohs(*(u_int16_t *)(&buf[28]));
	    vol[3] = ntohs(*(u_int16_t *)(&buf[30]));

	    sample_rate = ntohl(*(u_int32_t *)(&buf[32]));

	    /* Check if yid exists */
	    for(i = 0; i < total_playstacks; i++)
	    {
		ps = playstack[i];
		if(ps == NULL)
		    continue;

		if((ps->owner != con_num) ||
		   (ps->yid == YIDNULL)
		)
		    continue;

		if(ps->yid == yid)
		{
		    /* Set new values to play stack */

		    /* Change position? */
		    if(flags & (1 << 2))
			ps->position = (YDataPosition)position;

		    /* Change total repeats? */
		    if(flags & (1 << 5))
			ps->total_repeats = (int)total_repeats;

		    /* Change volume? */
		    if(flags & (1 << 6))
		    {
			ps->volume_left = (double)vol[0] /
			    (double)((u_int16_t)-1);
			ps->volume_right = (double)vol[1] /
			    (double)((u_int16_t)-1);
			ps->volume_back_left = (double)vol[2] /
			    (double)((u_int16_t)-1);
			ps->volume_back_right = (double)vol[3] /
			    (double)((u_int16_t)-1);
		    }

		    /* Change sample size? */
		    if(flags & (1 << 7))
			ps->applied_sample_rate = (int)sample_rate;

		    /* Send response of values set */
		    YNetSendPlaySoundObjectValues(con_num, ps);
		    break;
		}
	    }
	}

	return(0);
}

/*
 *	Sends client message.
 */
int YNetSendClientMessage(
	YConnectionNumber con_num, int format, int type,
	const char *message, YDataLength length
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_format> <u32_type>
	 * <...message...>
	 */
	const YDataLength this_seg_len = 8 + 8 + YClientMessageMessageMax;
	YDataLength actual_seg_len = 8 + 8;
	char buf[this_seg_len];


	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YClientMessage);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YClientMessageReceive);

	*(u_int32_t *)(&buf[8]) = htonl((u_int32_t)format);
	*(u_int32_t *)(&buf[12]) = htonl((u_int32_t)type);

	if((length > 0) && (message != NULL))
	{
	    memcpy(&buf[16], message, length);
	    actual_seg_len += length;
	}
	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);

	return(YNetSendToConnection(con_num, buf, actual_seg_len));
}
/*
 *	Parses client message.
 */
int YNetParseClientMessage(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YClientMessageSend)
	{
	    /* Parse client message send */

	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u32_format> <u32_type>
	     * <u8_notify_self>
	     */
	    const YDataLength base_seg_len = 8 + 8 + 1;
	    YConnection *con;
	    int i, format, type;
	    Boolean notify_self;
	    char message[YClientMessageMessageMax];
	    YDataLength message_length;

	    if(chunk_length < base_seg_len)
		return(-1);

	    format = ntohl(*(u_int32_t *)(&buf[8]));
	    type = ntohl(*(u_int32_t *)(&buf[12]));
	    notify_self = (*(u_int8_t *)(&buf[16])) ? True : False;

	    *message = '\0';
	    message_length = MIN(
		chunk_length - base_seg_len,
		YClientMessageMessageMax
	    );;
	    if(message_length > 0)
		memcpy(message, &buf[17], message_length);

	    /* Notify connections about client message */
	    for(i = 0; i < total_yconnections; i++)
	    {
		con = yconnection[i];
		if((con != NULL) ? (con->socket < 0) : True)
		    continue;

		if((i == con_num) && !notify_self)
		    continue;

		YNetSendClientMessage(
		    i, format, type, message, message_length
		);
	    }
	}

	return(0);
}

/*
 *	Sends YSHM sound open.
 */
int YNetSendYSHMSoundOpen(
	YConnectionNumber con_num, int yshm_id
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_shm_id>
	 */
	const YDataLength this_seg_len = 8 + 4;
	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSHMSound);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSHMSoundOpen);

	*(u_int32_t *)(&buf[8]) = htonl((u_int32_t)yshm_id);

	return(YNetSendToConnection(con_num, buf, this_seg_len));

	return(0);
}

/*
 *      Sends YSHM sound close.
 */
int YNetSendYSHMSoundClose(
	YConnectionNumber con_num, int yshm_id
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_shm_id>
	 */
	const YDataLength this_seg_len = 8 + 4;
	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSHMSound);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSHMSoundClose);

	*(u_int32_t *)(&buf[8]) = htonl((u_int32_t)yshm_id);

	return(YNetSendToConnection(con_num, buf, this_seg_len));

	return(0);
}

/*
 *	Parses YSHM sound open.
 */
int YNetParseYSHMSoundOpen(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YSHMSoundOpen)
	{
	    /* <u32_cs> <u16_majop> <u16_minop>
	     */
	    const YDataLength this_seg_len = 8;
	    int shm_id = -1;

	    if(chunk_length < this_seg_len)
		return(-1);

#ifdef YSHM_SUPPORT
	    if(recorder != NULL)
	    {
		Sound *sound_ptr = &recorder->sound;
		if(sound_ptr->buffer != NULL)
		    shm_id = sound_ptr->buffer_shm_id;
	    }
#endif	/* YSHM_SUPPORT */

	    YNetSendYSHMSoundOpen(con_num, shm_id);
	}
	else if(minor_op_code == YSHMSoundClose)
	{
	    /* Client should never tell the server to do this */
	}

	return(0);
}

/*
 *	Notify all connections about YSHM sound buffer closing.
 */
void YNetDoNotifyAllYSHMSoundClose(void)
{
	YConnectionNumber i;
	YConnection *con;
	int shm_id = -1;

#ifdef YSHM_SUPPORT
	if(recorder != NULL)
	{
	    Sound *sound_ptr = &recorder->sound;
	    if(sound_ptr->buffer != NULL)
		shm_id = sound_ptr->buffer_shm_id;
	}
#endif  /* YSHM_SUPPORT */

	for(i = 0; i < total_yconnections; i++)
	{
	    con = yconnection[i];
	    if((con != NULL) ? (con->socket < 0) : True)
		continue;

	    YNetSendYSHMSoundClose(i, shm_id);
	}
}

/*
 *	Sends audio CD play track.
 */
int YNetSendAudioCDPlayTrack(
	YConnectionNumber con_num,
	int track_number
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_track_number>
	 */
	const YDataLength this_seg_len = 8 + 4;
	char buf[this_seg_len];


	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioCD);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioCDPlayTrack);

	*(u_int32_t *)(&buf[8]) = htonl((u_int32_t)track_number);

	return(YNetSendToConnection(con_num, buf, this_seg_len));
}

/*
 *	Sends audio CD stop.
 */
int YNetSendAudioCDStop(
	YConnectionNumber con_num
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 */
	const YDataLength this_seg_len = 8;
	char buf[this_seg_len];


	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioCD);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioCDStop);

	return(YNetSendToConnection(con_num, buf, this_seg_len));
}

/*
 *	Sends audio CD eject.
 */
int YNetSendAudioCDEject(
	YConnectionNumber con_num
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 */
	const YDataLength this_seg_len = 8;
	char buf[this_seg_len];


	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioCD);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioCDEject);

	return(YNetSendToConnection(con_num, buf, this_seg_len));
}

/*
 *      Parses audio CD (play track, stop, and eject).
 */
int YNetParseAudioCD(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YAudioCDStatsGet)
	{

	}
	else if(minor_op_code == YAudioCDStatsSet)
	{

	}
	else if(minor_op_code == YAudioCDPlayTrack)
	{
	    /* Parse audio CD play track */

	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u32_track_number>
	     */
	    const YDataLength this_seg_len = 8 + 4;
	    int track_number;

	    if(chunk_length < this_seg_len)
		return(-1);

	    track_number = ntohl(*(u_int32_t *)(&buf[8]));

	    if(recorder != NULL)
	    {
		int i;
		YConnection *con;
		Audio *audio_ptr = &recorder->audio;

		/* Play audio CD track */
		AudioCDPlayTrack(audio_ptr->audiocd_context, track_number);

		/* Notify all connections about play audio CD track */
		for(i = 0; i < total_yconnections; i++)
		{
		    con = yconnection[i];
		    if((con != NULL) ? (con->socket < 0) : True)
			continue;

		    YNetSendAudioCDPlayTrack(i, track_number);
		}
	    }
	}
	else if(minor_op_code == YAudioCDStop)
	{
	    /* Parse stop audio CD */

	    /* <u32_cs> <u16_majop> <u16_minop>
	     */
	    const YDataLength this_seg_len = 8;

	    if(chunk_length < this_seg_len)
		return(-1);

	    if(recorder != NULL)
	    {
		int i;
		YConnection *con;
		Audio *audio_ptr = &recorder->audio;

		/* Stop audio CD */
		AudioCDStop(audio_ptr->audiocd_context);

		/* Notify all connections about stop audio CD */
		for(i = 0; i < total_yconnections; i++)
		{
		    con = yconnection[i];
		    if((con != NULL) ? (con->socket < 0) : True)
			continue;

		    YNetSendAudioCDStop(i);
		}
	    }
	}
	else if(minor_op_code == YAudioCDEject)
	{
	    /* Parse eject audio CD */

	    /* <u32_cs> <u16_majop> <u16_minop>
	     */
	    const YDataLength this_seg_len = 8;

	    if(chunk_length < this_seg_len)
		return(-1);

	    if(recorder != NULL)
	    {
		int i;
		YConnection *con;
		Audio *audio_ptr = &recorder->audio;

		/* Eject audio CD */
		AudioCDEject(audio_ptr->audiocd_context);

		/* Notify all connections about eject audio CD */
		for(i = 0; i < total_yconnections; i++)
		{
		    con = yconnection[i];
		    if((con != NULL) ? (con->socket < 0) : True)
			continue;

		    YNetSendAudioCDEject(i);
		}
	    }
	}

	return(0);
}

/*
 *	Sends audio CD track.
 */
int YNetSendListAudioCDTracks(
	YConnectionNumber con_num,
	int track_number,
	unsigned long track_start_time,
	unsigned long track_length,
	const char *track_name
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_track_number>
	 * <u64_track_start_time>
	 * <u64_track_length>
	 * <...track_name...>
	 */
	const YDataLength this_seg_len = 8 + 4 + 8 + 8 + YAudioCDTrackNameMax;
	YDataLength actual_seg_len;
	int len;
	char buf[this_seg_len];


	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioCDTracksList);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioCDTracksListSet);

	*(u_int32_t *)(&buf[8]) = htonl((u_int32_t)track_number);
	*(u_int32_t *)(&buf[12]) = htonl((u_int64_t)track_start_time);
	*(u_int32_t *)(&buf[20]) = htonl((u_int64_t)track_length);

	len = (track_name != NULL) ?
	    MIN(strlen(track_name), YAudioCDTrackNameMax - 1) : 0;
	if(len > 0)
	    strncpy(&buf[28], track_name, len);
	/* Do not null terminate string */

	/* Since this is a variable length segment we need to
	 * recalculate the segment length after the formatting
	 * above and update the segment header's chunk size.
	 */
	actual_seg_len = 8 + 4 + 8 + 8 + len;
	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);

	return(YNetSendToConnection(con_num, buf, actual_seg_len));
}

/*
 *	Parses audio CD tracks list.
 */
int YNetParseListAudioCDTracks(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YAudioCDTracksListGet)
	{
	    /* Parse list audio CD tracks get */

	    /* <u32_cs> <u16_majop> <u16_minop>
	     */
	    const YDataLength this_seg_len = 8;


	    if(chunk_length < this_seg_len)
		return(-1);

	    if(recorder != NULL)
	    {
		Audio *audio_ptr = &recorder->audio;
		int i, total;
		AudioCDTrack **track, *t;

		/* Get list of audio CD tracks */
		track = AudioCDListTracks(
		    audio_ptr->audiocd_context, &total
		);
		for(i = 0; i < total; i++)
		{
		    t = track[i];
		    if(t == NULL)
			continue;

		    /* Send this audio CD track */
		    YNetSendListAudioCDTracks(
			con_num, t->number, t->start_time, t->length, t->name
		    );
		}
		AudioCDDeleteTracksList(track, total);
	    }
	    /* End the list by sending track 0 to indicate end */
	    YNetSendListAudioCDTracks(con_num, 0, 0, 0, NULL);
	}
	else if(minor_op_code == YAudioCDTracksListSet)
	{

	}         
	
	return(0);

}


/*
 *      Parses the data in buf received by the connection con_num.
 *
 *	This function is called by YNetRecv().
 */
static int YNetParse(
	YConnectionNumber con_num,
	const u_int8_t *buf,
	u_int32_t chunk_length,
	u_int16_t major_op_code,
	u_int16_t minor_op_code
)
{
	int status;
	YConnection *con;      


	/* Connection assumed valid */
	con = yconnection[con_num];


	switch(major_op_code)
	{
	  case YAudioChange:
	    status = YNetParseAudioChange(YCNP_STD_INPUTS);
	    break;

	  case YCycleChange:
	    status = YNetParseCycleChange(YCNP_STD_INPUTS);
	    break;
	  
	  case YDisconnect:
	    status = YNetParseDisconnect(YCNP_STD_INPUTS);
	    break;

	  case YSetHost:
	    status = YNetParseSetHost(YCNP_STD_INPUTS);
	    break;

	  case YListHosts:
/* Work on this later */
	    break;
 
	  case YMixerChannel:
	    status = YNetParseMixerDevice(YCNP_STD_INPUTS);
	    break;
	
	  case YListMixers:
/* Work on this later */
	    break; 
 
	  case YSoundObjectPlay:
	    status = YNetParseSoundObjectPlay(YCNP_STD_INPUTS);
	    break;
 
	  case YSoundObjectKill:
	    status = YNetParseSoundObjectKill(YCNP_STD_INPUTS);
	    break;

	  case YSoundObjectAttributes:
	    status = YNetParseSoundObjectAttributes(YCNP_STD_INPUTS);
	    break;
 
	  case YShutdown:
	    status = YNetParseShutdown(YCNP_STD_INPUTS);
	    break;
 
	  case YSync:
	    status = YNetParseSync(YCNP_STD_INPUTS);
	    break;

	  case YAudioStats:
	    status = YNetParseAudioStats(YCNP_STD_INPUTS);
	    break;

	  case YServerStats:
	    status = YNetParseServerStats(YCNP_STD_INPUTS);
	    break;

	  case YListAudioModes:
	    status = YNetParseListAudioModes(YCNP_STD_INPUTS);
	    break;

	  case YSoundObjectPlayValues:
	    status = YNetParsePlaySoundObjectValues(YCNP_STD_INPUTS);
	    break;

	  case YClientMessage:
	    status = YNetParseClientMessage(YCNP_STD_INPUTS);
	    break;

	  case YSHMSound:
	    status = YNetParseYSHMSoundOpen(YCNP_STD_INPUTS);
	    break;

	  case YAudioCD:
	    status = YNetParseAudioCD(YCNP_STD_INPUTS);
	    break;

	  case YAudioCDTracksList:
	    status = YNetParseListAudioCDTracks(YCNP_STD_INPUTS);
	    break;


	  default:
	    YNetPrintError(
		stderr,
		con_num,
		chunk_length,
		major_op_code,
		minor_op_code,	
		"Unsupported Major Op Code"
	    );
	    break;
	}

	return(0);
}

/*
 *	Handles incoming data from connection con_num.
 */
int YNetRecv(YConnectionNumber con_num)
{
	YConnection *con;
	int i, n, s, status, loops;
	struct timeval timeout;
	fd_set readfds;

	u_int8_t *buf_ptr;  
	int bytes_read, segments_handled;

	u_int32_t chunk_length;
	u_int16_t major_op_code, minor_op_code;



	/* Reset close connection marker */
	close_this_connection = False;


	/* Connection is assumed valid and connected */
	con = yconnection[con_num];

	/* Get connection's socket number */
	s = con->socket;


	if(con->buf == NULL)   
	    return(0);

	if(con->buf_cont < 0)
	    con->buf_cont = 0;


	if((con->buf_len - con->buf_cont) <= 0)
	{
	    /* Contents buffer overflowed and thus we are unable to
	     * parse the current buffer any more. So reset the
	     * contents buffer and and disconnect connection since
	     * there is no way to seek the next chunk position.
	     */
	    con->buf_cont = 0;

	    /* Disconnect and delete connection */
	    if(con->socket > -1)
	    {
		YShutdownSocket(con->socket, SHUT_RDWR, 30l);
		YCloseSocketWait(con->socket, 30l);
		con->socket = -1;
	    }
	    YiffCloseConnection(con_num);

	    fprintf(
		stderr,
"YNetRecv(): Connection %i: Contents overflowed buffer length %ld.\n",
		con_num,
		con->buf_len
	    );

	    return(0);
	}


	/* Do not block, check if there is any data to be read */
	timeout.tv_sec = 0;
	timeout.tv_usec = 0;
	FD_ZERO(&readfds);
	FD_SET(s, &readfds);
	status = select(s + 1, &readfds, NULL, NULL, &timeout);
	if(status == -1)
	    perror("select");

	if(!FD_ISSET(s, &readfds))
	    return(0);


	buf_ptr = con->buf + con->buf_cont;
	bytes_read = recv(
	    s,
	    buf_ptr,
	    con->buf_len - con->buf_cont,
	    MSG_NOSIGNAL
	);
	if(bytes_read == 0)
	{
	    /* Disconnect and delete connection */
            YShutdownSocket(s, SHUT_RDWR, 30l);
	    close(s);
	    con->socket = s = -1;
	    YiffCloseConnection(con_num);

	    return(0);
	}
	else if(bytes_read < 0)
	{
	    /* Handle error */
	    const int error_code = errno;
	    switch(error_code)
	    {
	      case EWOULDBLOCK:
#if (EAGAIN != EWOULDBLOCK)
	      case EAGAIN:
#endif
		break;

	      case EINTR:
		break;

	      default:
		/* Disconnect and delete connection */
                YShutdownSocket(s, SHUT_RDWR, 30l);
                YCloseSocketWait(s, 30l);
		con->socket = -1;
		YiffCloseConnection(con_num);
		break;   
	    }
	    return(0);
	} 

	/* Increment buffer contents */
	con->buf_cont += bytes_read;
	if(con->buf_cont > con->buf_len)
	    con->buf_cont = con->buf_len;

	segments_handled = 0;
	for(loops = 0; loops < 30; loops++)
	{
	    /* Check if we got atleast the first 8 bytes, needed
	     * for chunk size, major op code, and minor op code.
	     */
	    if(con->buf_cont < 8)
		break;

	    /* All chunks start off with the same format, that being:
	     *   <4 bytes, chunk length> <2 bytes, major op code>
	     *   <2 bytes, minor op code>
	     */
	    chunk_length = (u_int32_t)MAX(
		(long)ntohl(*((u_int32_t *)(&con->buf[0]))), (long)0
	    );
	    major_op_code = ntohs(*((u_int16_t *)(&con->buf[4])));
	    minor_op_code = ntohs(*((u_int16_t *)(&con->buf[6])));


	    /* Specified chunk length must be 8 or greater */
	    if(chunk_length < 8)
	    {
		YNetPrintError(
		    stderr,
		    con_num,
		    chunk_length,
		    major_op_code,
		    minor_op_code,
 "Recieved a segment with header specified chunk length less than 8 bytes"
		); 

		/* Disconnect and delete connection */
                YShutdownSocket(con->socket, SHUT_RDWR, 30l);
                YCloseSocketWait(con->socket, 30l);
		con->socket = -1;
		YiffCloseConnection(con_num);

		return(0);
	    }


	    /* Did we get the entire chunk? Is the indicated chunk
	     * length greater than the contents buffer length?
	     */
	    if((YDataLength)chunk_length > (YDataLength)con->buf_cont)
		break;
#if 0
{
 printf("Server: %i %i %i: Recieved incomplete.\n",
  chunk_length, major_op_code, minor_op_code
 );
		break;
}
else
 printf("Server: %i %i | %i %i: Recieved.\n",
  chunk_length, con->buf_cont, major_op_code, minor_op_code
 );
#endif

	    /* Parse this segement and put data into event structure */
	    YNetParse(
		con_num,
		con->buf,
		chunk_length,
		major_op_code,
		minor_op_code
	    );
	    segments_handled++;

	    /* Stop parsing and close this connection? This would
	     * be said if the connection needed to be closed for some
	     * reason determined in a higher call to YNetParse()
	     * above
	     */
	    if(close_this_connection)
	    {
		/* Disconnect and delete connection */
                YShutdownSocket(con->socket, SHUT_RDWR, 30l);
                YCloseSocketWait(con->socket, 30l);
		con->socket = -1;
		YiffCloseConnection(con_num);

		break;
	    }

	    /* Shift the buffer */
	    for(i = 0, n = chunk_length;
		n < con->buf_cont;
		i++, n++
	    )
		con->buf[i] = con->buf[n];

	    /* Decrease buffer contents */
	    con->buf_cont = (YDataLength)con->buf_cont -
		(YDataLength)chunk_length;
	    if(con->buf_cont < 0)
		con->buf_cont = 0;
	}

	/*   Return number of segments handled which implies events
	 *   handled.
	 */
	return(segments_handled);
}
