Logo Search packages:      
Sourcecode: yiff version File versions  Download package

ynet.c

/*
      The functions contained here should only be called by functions
      from functions in original library source file yclientio.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
extern int h_errno;             /* For netdb.h */
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <ctype.h>
#include <math.h>
            
#define _USE_BSD
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <sys/stat.h> 
       
#include <netdb.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>          /* For inet_ntoa() */
#include <fcntl.h>

#include "../include/Y2/Y.h"
#include "../include/Y2/Ylib.h"
#include "../include/Y2/Yclientnet.h"

static u_int64_t htonl64(u_int64_t x);

static void YNetPrintError(
      FILE *stream,
      YConnection *con,
      u_int16_t chunk_length,
      u_int16_t major_op_code,
      u_int16_t minor_op_code,
      const char *mesg
);
void YSetEventToBeDisconnect(YEvent *event);
int YSendData(int s, const char *buf, int len);

int YNetSendAudioChangePreset(
      YConnection *con,
      const char *audio_mode_name
);
int YNetSendAudioChangeValues(
      YConnection *con,
      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 YNetSendCycleChange(YConnection *con, long cycle_us);
int YNetParseCycleChange(YCNP_STD_INPUTS_PROTO);

int YNetSendDisconnect(YConnection *con, int reason);
int YNetParseDisconnect(YCNP_STD_INPUTS_PROTO);

int YNetSendSetHost(
      YConnection *con,
      u_int16_t minor_op_code,
      YIPUnion *ip
);
int YNetParseSetHost(YCNP_STD_INPUTS_PROTO);

int YNetSendSetMixerChannel(
      YConnection *con,
      int mixer_code,
      Coefficient value1, Coefficient value2,
      Coefficient value3, Coefficient value4
);
int YNetSendGetMixerChannel(YConnection *con, int mixer_code);
int YNetParseMixerChannel(YCNP_STD_INPUTS_PROTO);

int YNetSendSoundPlay(
      YConnection *con,
      YID yid,
      const char *path,
      YDataPosition pos,
      YVolumeStruct *volume,
      int sample_rate,
      int repeats
);
int YNetParseSoundPlay(YCNP_STD_INPUTS_PROTO);

int YNetSendSoundKill(YConnection *con, YID yid);
int YNetParseSoundKill(YCNP_STD_INPUTS_PROTO);

int YNetSendGetSoundObjectAttributes(
      YConnection *con,
      const char *path
);
int YNetParseSoundObjectAttributes(YCNP_STD_INPUTS_PROTO);

int YNetSendShutdown(YConnection *con, int reason);
int YNetParseShutdown(YCNP_STD_INPUTS_PROTO);

int YNetSendSync(YConnection *con, long cycle_ahead_us);
int YNetParseSync(YCNP_STD_INPUTS_PROTO);

int YNetSendGetAudioStats(YConnection *con);
int YNetParseAudioStats(YCNP_STD_INPUTS_PROTO);

int YNetSendGetServerStats(YConnection *con);
int YNetParseServerStats(YCNP_STD_INPUTS_PROTO);

int YNetSendListAudioModes(YConnection *con);
int YNetParseListAudioModes(YCNP_STD_INPUTS_PROTO);

int YNetSendSetSoundObjectPlayValues(
      YConnection *con, YEventSoundPlay *value
);
int YNetSendGetSoundObjectPlayValues(YConnection *con, YID yid);
int YNetParseSoundObjectPlayValues(YCNP_STD_INPUTS_PROTO);

int YNetSendClientMessage(
      YConnection *con,
      Boolean notify_self,
      int format,
      int type,
      const char *message,
      int length
);
int YNetParseClientMessage(YCNP_STD_INPUTS_PROTO);

int YNetSendYSHMSoundOpen(YConnection *con);
int YNetParseYSHMSoundOpen(YCNP_STD_INPUTS_PROTO);

int YNetSendAudioCDPlayTrack(YConnection *con, int track_number);
int YNetSendAudioCDStop(YConnection *con);
int YNetSendAudioCDEject(YConnection *con);
int YNetParseAudioCD(YCNP_STD_INPUTS_PROTO);

int YNetSendListAudioCDTracks(YConnection *con);
int YNetParseListAudioCDTracks(YCNP_STD_INPUTS_PROTO);


static int YNetParse(
      YConnection *con,
      YEvent *event,
      const u_int8_t *buf,
      u_int32_t chunk_length,
      u_int16_t major_op_code,
      u_int16_t minor_op_code
);
int YNetRecv(YConnection *con);


#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 ABSOLUTE(x)     (((x) < 0) ? ((x) * -1) : (x))


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
}

/*
 *    Prints YIFF network error to stream.
 */
static void YNetPrintError(
      FILE *stream, YConnection *con,
      u_int16_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 client protocol error on connection: 0x%.8x\n",
          (u_int32_t)con
      );
      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
          );
}

/*
 *    Turns event into a disconnect event.
 */
void YSetEventToBeDisconnect(YEvent *event)
{
      if(event == NULL)
          return;

      /* Explicitly change event type to YDisconnect. */
      event->type = YDisconnect;

      /* Set disconnect reason code. */
      event->disconnect.reason = 0;
}


/*
 *    Sends binary data in buf to connection s.
 */
int YSendData(int s, const char *buf, int len)
{
      struct timeval timeout;
      fd_set writefds;

      int bytes_written;


      if(s < 0)
          return(-1);

      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_written = send(s, buf, len, 0);
      if(bytes_written < 0)
          return(-1);
      else
          return(bytes_written);
}


/*
 *    Sends audio mode change preset.
 */
int YNetSendAudioChangePreset(
      YConnection *con,
      const char *audio_mode_name
)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <...audiomodename...>
       */
      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(YSendData(con->fd, buf, actual_seg_len));
}
/*      
 *      Sends audio mode change values.
 */     
int YNetSendAudioChangeValues(
      YConnection *con,
      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(YSendData(con->fd, buf, this_seg_len)); 
}
/*
 *    Parses audio mode change.
 */
int YNetParseAudioChange(YCNP_STD_INPUTS_PROTO)
{
      if(minor_op_code == YAudioChangeValues)
      {
          /* Parse Audio mode 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;
          YEventAudioChange *audio = &event->audio;

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

          audio->preset = False;

          *audio->mode_name = '\0';

          audio->sample_size = ntohs(*(u_int16_t *)(&buf[8]));
          audio->channels = ntohs(*(u_int16_t *)(&buf[10]));
          audio->sample_rate = ntohl(*(u_int32_t *)(&buf[12]));

          audio->direction = buf[16];

          audio->allow_fragmenting = buf[17];
          audio->num_fragments = ntohl(*(u_int32_t *)(&buf[18]));
          audio->fragment_size_bytes = ntohl(*(u_int32_t *)(&buf[22]));
      }
      else if(minor_op_code == YAudioChangePreset)
      {
          /* Parse change preset Audio mode. */

          /* <u32_cs> <u16_majop> <u16_minop>
           * <...audiomodename...>
           */
          const YDataLength base_seg_len = 8;
          int mode_name_len;
          YEventAudioChange *audio = &event->audio;

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

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

          if(mode_name_len > 0)
          {
            strncpy(
                audio->mode_name,
                (char *)&buf[8],
                mode_name_len
            );
            audio->mode_name[mode_name_len] = '\0';
          }
          else
          {
            audio->mode_name[0] = '\0';
          }

          /* Clear rest of event members. */
          audio->preset = True;

          audio->sample_size = 0;
          audio->channels = 0;
          audio->sample_rate = 0;

          audio->direction = 0;

          audio->allow_fragmenting = 0;
          audio->num_fragments = 0;
          audio->fragment_size_bytes = 0;
      }

      return(0);
}

/*
 *      Sends cycle change.
 */
int YNetSendCycleChange(YConnection *con, long cycle_us)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_cycleus>
       */
      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)YCycleChange);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)0);
      *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)cycle_us);

      return(YSendData(con->fd, buf, this_seg_len));
}
/*
 *    Parses cycle change.
 */
int YNetParseCycleChange(YCNP_STD_INPUTS_PROTO)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_cycleus>
       */
      const YDataLength this_seg_len = 8 + 4;
      YEventCycleChange *cycle = &event->cycle;

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

      cycle->cycle_us = ntohl(*(u_int32_t *)(&buf[8]));

      return(0);
}


/*
 *    Sends disconnect.
 */
int YNetSendDisconnect(YConnection *con, int 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(YSendData(con->fd, buf, this_seg_len));
}
/*
 *    Parses disconnect.
 */
int YNetParseDisconnect(YCNP_STD_INPUTS_PROTO)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u16_reason>
       */
      const YDataLength this_seg_len = 8 + 2;
      YEventDisconnect *disconnect = &event->disconnect;

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

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

      return(0);
}

/*
 *    Sends a set host.
 */
int YNetSendSetHost(
      YConnection *con,
      u_int16_t minor_op_code,
      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]) = (u_int8_t)ip->charaddr[0];
      *(u_int8_t *)(&buf[9]) = (u_int8_t)ip->charaddr[1];
      *(u_int8_t *)(&buf[10]) = (u_int8_t)ip->charaddr[2];
      *(u_int8_t *)(&buf[11]) = (u_int8_t)ip->charaddr[3];

      return(YSendData(con->fd, 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;
      YEventHost *host = &event->host;

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

      host->op = ((minor_op_code == YSetHostAdd) ? 1 : 0);

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

      return(0);
}

/*
 *    Sends a mixer channel set.
 */
int YNetSendSetMixerChannel(
      YConnection *con,
      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(YSendData(con->fd, buf, this_seg_len));
}
/*
 *      Sends a mixer channel get.
 */
int YNetSendGetMixerChannel(YConnection *con, int mixer_code)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u16_mixercode>
       */
      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)YMixerChannel);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YMixerChannelGet);

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

      return(YSendData(con->fd, buf, this_seg_len));
}
/*
 *    Handles mixer device set.
 */
int YNetParseMixerChannel(YCNP_STD_INPUTS_PROTO)
{
      if(minor_op_code == YMixerChannelGet)
      {
          /* <u32_cs> <u16_majop> <u16_minop>
           * <u16_mixercode>
           */
          const YDataLength this_seg_len = 8 + 2;
  
          if(chunk_length < this_seg_len)
            return(-1);
      }
      else 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;
          YEventMixer *mixer = &event->mixer;

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

          mixer->code = ntohs(*(u_int16_t *)(&buf[8]));

          /* Get mixer values, check availibility with defination. */
          if(YMixerValues > 0)
            mixer->value[0] =
                (Coefficient)ntohl(*(u_int32_t *)(&buf[10])) /
                (Coefficient)((u_int32_t)-1);
          if(YMixerValues > 1)
            mixer->value[1] =
                (Coefficient)ntohl(*(u_int32_t *)(&buf[14])) /
                (Coefficient)((u_int32_t)-1);
          if(YMixerValues > 2)
            mixer->value[2] =
                (Coefficient)ntohl(*(u_int32_t *)(&buf[18])) /
                (Coefficient)((u_int32_t)-1);
          if(YMixerValues > 3)
            mixer->value[3] =
                (Coefficient)ntohl(*(u_int32_t *)(&buf[22])) /
                (Coefficient)((u_int32_t)-1);
      }

      return(0);
}

/*
 *      Sends a sound play.
 */
int YNetSendSoundPlay(
      YConnection *con,
      YID yid,
      const char *path,
      YDataPosition pos,
      YVolumeStruct *volume,
      int sample_rate,
      int repeats
)
{
      /* <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;
      char buf[this_seg_len];
      YDataLength path_len, actual_seg_len;

      if(repeats < 0)
          repeats = 0;

      *(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)yid);
      *(u_int32_t *)(&buf[12]) = htonl((u_int32_t)pos);

      *(u_int16_t *)(&buf[16]) = htons((u_int16_t)volume->left);
      *(u_int16_t *)(&buf[18]) = htons((u_int16_t)volume->right);

      *(u_int16_t *)(&buf[20]) = htons((u_int16_t)volume->back_left);
      *(u_int16_t *)(&buf[22]) = htons((u_int16_t)volume->back_right);

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

      path_len = MIN(strlen(path), YPathMax);
      strncpy(
          &buf[32],
          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 + 8 + 4 + 4 + 8 + path_len;
      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);

      return(YSendData(con->fd, buf, actual_seg_len));
}
/*
 *    Handles sound play.
 */
int YNetParseSoundPlay(YCNP_STD_INPUTS_PROTO)
{
      if(True)    /* No minor op codes. */
      {
          /* <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;
          YEventSoundPlay *play = &event->play;

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

#define BUF_TO_COEFFICIENT(x)   ( \
  (Coefficient)(x) / (Coefficient)((u_int16_t)-1) \
)
          play->flags = (unsigned long)ntohl(*(u_int32_t *)(&buf[8]));
          play->yid = (YID)ntohl(*(u_int32_t *)(&buf[12]));
          play->position = (YDataPosition)ntohl(*(u_int32_t *)(&buf[16]));
          play->length = (YDataLength)ntohl(*(u_int32_t *)(&buf[20]));
          play->repeats = ntohl(*(u_int32_t *)(&buf[24]));
          play->total_repeats = ntohl(*(u_int32_t *)(&buf[28]));
          play->left_volume = BUF_TO_COEFFICIENT(
            ntohs(*(u_int16_t *)(&buf[32]))
          );
          play->right_volume = BUF_TO_COEFFICIENT(
            ntohs(*(u_int16_t *)(&buf[34]))
          );
          play->back_left_volume = BUF_TO_COEFFICIENT(
            ntohs(*(u_int16_t *)(&buf[36]))
          );
          play->back_right_volume = BUF_TO_COEFFICIENT(
            ntohs(*(u_int16_t *)(&buf[38]))
          );
          play->sample_rate = ntohl(*(u_int32_t *)(&buf[40]));
#undef BUF_TO_COEFFICIENT
      }

      return(0);
}

/*
 *    Sends a sound kill.
 */
int YNetSendSoundKill(YConnection *con, 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(YSendData(con->fd, buf, this_seg_len));
}       
/*
 *    Parses sound kill.
 */
int YNetParseSoundKill(YCNP_STD_INPUTS_PROTO)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_yid>
       */
      const YDataLength this_seg_len = 8 + 4;
      YEventSoundKill *kill = &event->kill;

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

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

      return(0);
}

/*
 *    Sends get sound object attributes.
 */
int YNetSendGetSoundObjectAttributes(
      YConnection *con, const char *path
)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <...path...>
       */
      const YDataLength this_seg_len = 8 + YPathMax;
      YDataLength actual_seg_len;
      YDataLength path_len;
      char buf[this_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)YSoundObjectAttributesGet);

      path_len = MIN(strlen(path), YPathMax);
      strncpy(
          &buf[8],
          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 + path_len;
      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);

      return(YSendData(con->fd, buf, actual_seg_len));
}
/*
 *    Handles sound object attributes.
 */
int YNetParseSoundObjectAttributes(YCNP_STD_INPUTS_PROTO)
{
      if(minor_op_code == YSoundObjectAttributesGet)
      {

      }
      else if(minor_op_code == YSoundObjectAttributesSet)
      {
          /* <u32_cs> <u16_majop> <u16_minop>
           * <u16_format>
           * <u16_samplesize> <u16_channels> <u32_samplerate>
           * <u32_length>
           * <...path...>
           */
          const YDataLength base_seg_len = 8 + 2 + 8 + 4;
          YDataLength path_len;
          YEventSoundObjectAttributes *soa = &event->sndobjattributes;

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

          soa->format = ntohs(*(u_int16_t *)(&buf[8]));
          soa->sample_size = ntohs(*(u_int16_t *)(&buf[10]));
          soa->channels = ntohs(*(u_int16_t *)(&buf[12]));
          soa->sample_rate = ntohl(*(u_int32_t *)(&buf[14]));
          soa->length = ntohl(*(u_int32_t *)(&buf[18]));

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

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

      return(0);
}

/*
 *      Sends shutdown.
 */
int YNetSendShutdown(YConnection *con, int 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(YSendData(con->fd, buf, this_seg_len));
}
/*
 *      Parses shutdown.
 */
int YNetParseShutdown(YCNP_STD_INPUTS_PROTO)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u16_reason>
       */
      const YDataLength this_seg_len = 8 + 2;
      YEventShutdown *shutdown = &event->shutdown;

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

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

      return(0);
}       


/*
 *      Sends sync.
 */
int YNetSendSync(YConnection *con, long cycle_ahead_us)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_cycleaheadus>
       */
      const YDataLength this_seg_len = 8 + 4;
      char buf[this_seg_len];

      /* Sanitize cycle_ahead_us. */
      if(cycle_ahead_us < 0)
          cycle_ahead_us = 0;

      *(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)cycle_ahead_us);

      return(YSendData(con->fd, buf, this_seg_len));
}
/*        
 *      Parses sync.
 */
int YNetParseSync(YCNP_STD_INPUTS_PROTO)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_cycleaheadus>
       */
      const YDataLength this_seg_len = 8 + 4;
      YEventSync *sync = &event->sync;

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

      sync->cycle_ahead_us = ntohl(*(u_int32_t *)(&buf[8]));

      return(0);
}

/*
 *    Sends get audio stats.
 */
int YNetSendGetAudioStats(YConnection *con)
{
      /* <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)YAudioStats);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioStatsGet);

      return(YSendData(con->fd, buf, this_seg_len));
}
/*
 *    Handles set audio stats.
 */
int YNetParseAudioStats(YCNP_STD_INPUTS_PROTO)
{
      if(minor_op_code == YAudioStatsSet)
      {
          /* <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>
           * <...audio_mode_name...>
           */
          const YDataLength base_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; */
          YDataLength name_len;
          YEventAudioStats *as = &event->audiostats;
          if(chunk_length < base_seg_len)
            return(-1);

          as->cycle_set = (int)buf[8];

          as->cycle_us = ntohl(*(u_int32_t *)(&buf[9]));
          as->compensated_cycle_us = ntohl(*(u_int32_t *)(&buf[13]));

          as->write_ahead_us = ntohl(*(u_int32_t *)(&buf[17]));
          as->cumulative_latency_us = ntohl(*(u_int32_t *)(&buf[21]));

          as->sample_size = ntohs(*(u_int16_t *)(&buf[25]));
          as->channels = ntohs(*(u_int16_t *)(&buf[27]));
          as->sample_rate = ntohl(*(u_int32_t *)(&buf[29]));
          as->bytes_per_sec = ntohl(*(u_int32_t *)(&buf[33]));

          as->allow_fragments = ((buf[37]) ? True : False);
          as->num_fragments = ntohl(*(u_int32_t *)(&buf[38]));
          as->fragment_size = ntohl(*(u_int32_t *)(&buf[42]));

          as->flip_stereo = ((buf[46]) ? True : False);
          as->direction = (int)buf[47];

          name_len = (YDataLength)chunk_length - (YDataLength)base_seg_len;
          if(name_len >= YAudioNameMax)
            name_len = YAudioNameMax - 1;
          if(name_len > 0)
          {
            strncpy(
                as->mode_name,
                (char *)(&buf[48]),
                name_len
            );
            as->mode_name[name_len] = '\0';
          }
          else
          {
            *as->mode_name = '\0';
          }

      }

      return(0);
}

/*
 *      Send get server stats.   
 */
int YNetSendGetServerStats(YConnection *con)
{
      /* <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)YServerStats);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YServerStatsGet);

      return(YSendData(con->fd, buf, this_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);
      }
      else if(minor_op_code == YServerStatsSet)
      {
          /* Parse server stats set. */      

          /* <u32_cs> <u16_majop> <u16_minop>
           * <u32_protocolvermaj> <u32_protocolvermin>
           * <u16_load>
           * <u64_start_time>
           * <...vendor_name, server_name, sound_name...>
           */
          const YDataLength base_seg_len = 8 + 8 + 2 + 8;
          YEventServerStats *sstat = &event->serverstats;

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

          sstat->protocol_version_major =
            ntohl(*(u_int32_t *)(&buf[8]));
          sstat->protocol_version_minor =
            ntohl(*(u_int32_t *)(&buf[12]));

          sstat->cycle_load =
            (double)ntohs(*(u_int16_t *)(&buf[16])) /
            (double)((u_int16_t)-1);

          sstat->start_time = (u_int64_t)htonl64(*(u_int64_t *)(&buf[18]));

          /* Begin parsing strings. */
          if(chunk_length > base_seg_len)
          {
            const char  *s = (const char *)&buf[base_seg_len],
                        *s_end = (const char *)&buf[chunk_length];

            /* Vendor name. */
            char c, *ts = sstat->vendor_name;
            int i = 0, tlen = YVendorNameMax;
            while((s < s_end) && (i < tlen))
            {
                ts[i] = c = *s;
                if(c == '\0')
                  break;
                s++; i++;
            }
            if(i < tlen)
                ts[i] = '\0';
            else
                ts[tlen - 1] = '\0';

            /* Server name. */
            ts = sstat->server_name;
            i = 0; tlen = YServerNameMax;
            s++;
            while((s < s_end) && (i < tlen))
            {
                ts[i] = c = *s;
                if(c == '\0')
                  break;
                s++; i++;
            }
            if(i < tlen)
                ts[i] = '\0';
            else
                ts[tlen - 1] = '\0';

            /* Sound name. */
            ts = sstat->sound_name;
            i = 0; tlen = YSoundNameMax;
            s++;
            while((s < s_end) && (i < tlen))
            {
                ts[i] = c = *s;
                if(c == '\0')
                  break;
                s++; i++;
            }
            if(i < tlen)
                ts[i] = '\0';
            else
                ts[tlen - 1] = '\0';
          }

          /* More items to be added in future. */

      }


      return(0);
}

/*
 *    Send list audio modes.
 */
int YNetSendListAudioModes(YConnection *con)   
{
      /* <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)YListAudioModes);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YListAudioModesGet);

      return(YSendData(con->fd, buf, this_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;

          if(chunk_length < this_seg_len)
            return(-1);
      }
      else if(minor_op_code == YListAudioModesSet)
      {
          /* Parse list audio modes set. */

          /* <u32_cs> <u16_majop> <u16_minop>
           * <u32_samplerate> <u32_channels>
           * <u32_samplesize> <u32_fragmentsizebytes>
           * <u8_direction> <u8_allowfragmenting> <u32_numfragments>
           * <...audiomodename...>
           */
          const YDataLength base_seg_len = 8 + 8 + 8 + 6;
          int name_len;
          YEventAudioChange *audio = &event->audio;

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

          /* We use the YEventAudioChange audio event for convience
           * in the fetching and organization of these values.
           */

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

          if(name_len > 0)
          {
            strncpy(
                audio->mode_name,
                (char *)&buf[30],
                name_len
            );
            audio->mode_name[name_len] = '\0';
          }
          else
          {
            *audio->mode_name = '\0';
          }

          audio->preset = True;

          audio->sample_rate = ntohl(*(u_int32_t *)(&buf[8]));
          audio->channels = ntohl(*(u_int32_t *)(&buf[12]));
          audio->sample_size = ntohl(*(u_int32_t *)(&buf[16]));
          audio->fragment_size_bytes = ntohl(*(u_int32_t *)(&buf[20]));

          audio->direction = (u_int8_t)buf[24];
          audio->allow_fragmenting = (u_int8_t)buf[25];
          audio->num_fragments = ntohl(*(u_int32_t *)(&buf[26]));
      }

      return(0);
}

/*
 *    Sends set sound object play values.
 */
int YNetSendSetSoundObjectPlayValues(
      YConnection *con, YEventSoundPlay *value
)
{
      /* <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;
      char 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)value->flags);
      *(u_int32_t *)(&buf[12]) = htonl((u_int32_t)value->yid);

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

      *(u_int16_t *)(&buf[24]) = htons(COEFFICIENT_TO_BUF16(
          value->left_volume
      ));
      *(u_int16_t *)(&buf[26]) = htons(COEFFICIENT_TO_BUF16(
          value->right_volume
      ));
      *(u_int16_t *)(&buf[28]) = htons(COEFFICIENT_TO_BUF16(
          value->back_left_volume
      ));
      *(u_int16_t *)(&buf[30]) = htons(COEFFICIENT_TO_BUF16(
          value->back_right_volume
      ));
      *(u_int32_t *)(&buf[32]) = htonl((u_int32_t)value->sample_rate);
#undef COEFFICIENT_TO_BUF16

      return(YSendData(con->fd, buf, this_seg_len));
}
/*
 *      Sends get sound object play values.
 */
int YNetSendGetSoundObjectPlayValues(YConnection *con, 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)YSoundObjectPlayValues);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSoundObjectPlayValuesGet);

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

      return(YSendData(con->fd, buf, this_seg_len));
}
/*
 *    Parses a sound object play values.
 */
int YNetParseSoundObjectPlayValues(YCNP_STD_INPUTS_PROTO)
{
      if(minor_op_code == YSoundObjectPlayValuesGet)
      {

      }
      else if(minor_op_code == YSoundObjectPlayValuesSet)
      {
          /* Parse sound object play values set. */

          /* <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;
          YEventSoundPlay *play = &event->play;

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

#define BUF_TO_COEFFICIENT(x) ( \
  (Coefficient)(x) / (Coefficient)((u_int16_t)-1) \
)
          play->flags = (unsigned long)ntohl(*(u_int32_t *)(&buf[8]));
          play->yid = (YID)ntohl(*(u_int32_t *)(&buf[12]));
          play->position = (YDataPosition)ntohl(*(u_int32_t *)(&buf[16]));
          play->length = (YDataLength)ntohl(*(u_int32_t *)(&buf[20]));
          play->repeats = ntohl(*(u_int32_t *)(&buf[24]));
          play->total_repeats = ntohl(*(u_int32_t *)(&buf[28]));
          play->left_volume = BUF_TO_COEFFICIENT(
            ntohs(*(u_int16_t *)(&buf[32]))
          );
          play->right_volume = BUF_TO_COEFFICIENT(
            ntohs(*(u_int16_t *)(&buf[34]))
          );
          play->back_left_volume = BUF_TO_COEFFICIENT(
            ntohs(*(u_int16_t *)(&buf[36]))
          );
          play->back_right_volume = BUF_TO_COEFFICIENT(
            ntohs(*(u_int16_t *)(&buf[38]))
          );
          play->sample_rate = ntohl(*(u_int32_t *)(&buf[40]));
#undef BUF_TO_COEFFICIENT
      }

      return(0);
}

/*
 *    Sends a client message.
 */
int YNetSendClientMessage(
      YConnection *con,
      Boolean notify_self,
      int format,
      int type,
      const char *message,
      int length
)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_format> <u32_type>
       * <u8_notify_self>
       * <...message...>
       */
      const YDataLength this_seg_len = 8 + 8 + 1 + YClientMessageMessageMax;
      YDataLength actual_seg_len = 8 + 8 + 1;
      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)YClientMessageSend);

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

      if((message != NULL) && (length > 0))
      {
          int len = MIN(length, YClientMessageMessageMax);
          memcpy(&buf[17], message, len);
          actual_seg_len += len;
      }

      /* 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.
       */
      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);

      return(YSendData(con->fd, buf, actual_seg_len));
}
/*
 *      Parses a client message.
 */
int YNetParseClientMessage(YCNP_STD_INPUTS_PROTO)
{
      if(minor_op_code == YClientMessageReceive)
      {
          /* <u32_cs> <u16_majop> <u16_minop>
           * <u32_format> <u32_type>
           * <...message...>
           */
          const YDataLength base_seg_len = 8 + 8;
          YEventClientMessage *clientmessage = &event->clientmessage; 
          YDataLength len;
          if(chunk_length < base_seg_len)
            return(-1);

          clientmessage->format = ntohl(*(u_int32_t *)(&buf[8]));
          clientmessage->type = ntohl(*(u_int32_t *)(&buf[12]));

          clientmessage->message[0] = '\0';
          clientmessage->message[YClientMessageMessageMax - 1] = '\0';
          clientmessage->length = len = MIN(
            chunk_length - base_seg_len,
            YClientMessageMessageMax
          );
          if(len > 0)
            memcpy(
                clientmessage->message,
                &buf[16],
                len
            );
      }

      return(0);
}

/*
 *    Sends a YSHM sound open.
 */
int YNetSendYSHMSoundOpen(YConnection *con)
{
      /* <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)YSHMSound);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSHMSoundOpen);

      return(YSendData(con->fd, buf, this_seg_len));
}
/*
 *      Parses a YSHM sound open.
 */
int YNetParseYSHMSoundOpen(YCNP_STD_INPUTS_PROTO)
{
      if(minor_op_code == YSHMSoundOpen)
      {
          /* <u32_cs> <u16_majop> <u16_minop>
           * <u32_shm_id>
           */
          const YDataLength this_seg_len = 8 + 4;
          YEventYSHMSound *yshmsound = &event->yshmsound;

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

          yshmsound->minor_op_code = minor_op_code;
          yshmsound->shm_id = ntohl(*(u_int32_t *)(&buf[8]));
      }
      else if(minor_op_code == YSHMSoundClose)
      {
          /* <u32_cs> <u16_majop> <u16_minop>
           * <u32_shm_id>
           */
          const YDataLength this_seg_len = 8 + 4;
          YEventYSHMSound *yshmsound = &event->yshmsound;

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

          yshmsound->minor_op_code = minor_op_code;
          yshmsound->shm_id = ntohl(*(u_int32_t *)(&buf[8]));
      }

      return(0);
}

/*
 *      Send audio CD play track.
 */
int YNetSendAudioCDPlayTrack(YConnection *con, 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(YSendData(con->fd, buf, this_seg_len));
}

/*
 *    Send audio CD stop.
 */
int YNetSendAudioCDStop(YConnection *con)
{
      /* <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(YSendData(con->fd, buf, this_seg_len));
}

/*
 *    Send audio CD eject.
 */
int YNetSendAudioCDEject(YConnection *con)
{
      /* <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(YSendData(con->fd, buf, this_seg_len));
}

/*
 *    Parse 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 base_seg_len = 8 + 4;
          YEventAudioCDStats *acds = &event->audiocdstats;

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

          acds->minor_op_code = minor_op_code;
          acds->track_number = ntohl(*(u_int32_t *)(&buf[8]));
          acds->track_start_time = 0;
          acds->track_length = 0;
      }
      else if(minor_op_code == YAudioCDStop)
      {
          /* Parse audio CD stop. */

          /* <u32_cs> <u16_majop> <u16_minop>
           */
          const YDataLength base_seg_len = 8;
          YEventAudioCDStats *acds = &event->audiocdstats;

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

          acds->minor_op_code = minor_op_code;
          acds->track_number = 0;
          acds->track_start_time = 0;
          acds->track_length = 0;
      }
      else if(minor_op_code == YAudioCDEject)
      {
          /* Parse audio CD eject. */

          /* <u32_cs> <u16_majop> <u16_minop>
           */
          const YDataLength base_seg_len = 8;
          YEventAudioCDStats *acds = &event->audiocdstats;

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

          acds->minor_op_code = minor_op_code;
          acds->track_number = 0;
          acds->track_start_time = 0;
          acds->track_length = 0;
      }

      return(0);
}

/*
 *    Send list audio CD tracks.
 */
int YNetSendListAudioCDTracks(YConnection *con)
{
      /* <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)YAudioCDTracksList);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioCDTracksListGet);

      return(YSendData(con->fd, buf, this_seg_len));
}
/*
 *      Parses list audio CD tracks.
 */
int YNetParseListAudioCDTracks(YCNP_STD_INPUTS_PROTO)
{
      if(minor_op_code == YAudioCDTracksListGet)
      {

      }
      else if(minor_op_code == YAudioCDTracksListSet)
      {
          /* Parse list audio CD track set. */

          /* <u32_cs> <u16_majop> <u16_minop>
           * <u32_track_number>
           * <u64_track_start_time>
           * <u64_track_length>
           * <...track_name...>
           */
          const YDataLength base_seg_len = 8 + 4 + 8 + 8;
          YEventAudioCDStats *acds = &event->audiocdstats;
          int len;

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

          acds->minor_op_code = minor_op_code;
          acds->track_number = ntohl(*(u_int32_t *)(&buf[8]));
          acds->track_start_time = ntohl(*(u_int64_t *)(&buf[12]));
          acds->track_length = ntohl(*(u_int64_t *)(&buf[20]));

          len = MIN(chunk_length - base_seg_len, YAudioCDTrackNameMax - 1);
          if(len > 0)
            strncpy(
                acds->track_name,
                (const char *)(&buf[28]),
                len
            );
          acds->track_name[len] = '\0';
      }

      return(0);
}

/*
 *    Parses the buffer buf recieved from the server, this function
 *    should be called from YNetRecv().
 */
static int YNetParse(  
      YConnection *con,
      YEvent *event,
      const u_int8_t *buf,
      u_int32_t chunk_length, 
      u_int16_t major_op_code,
      u_int16_t minor_op_code
)
{
      int status;

      event->type = major_op_code;

      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 = YNetParseMixerChannel(YCNP_STD_INPUTS);
          break;

        case YListMixers:
/* Work on this later. */
          break;

        case YSoundObjectPlay:
          status = YNetParseSoundPlay(YCNP_STD_INPUTS);
          break;

        case YSoundObjectKill:
          status = YNetParseSoundKill(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 = YNetParseSoundObjectPlayValues(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:
          /* Unknown op code, so set event type to 0. */
          event->type = 0;
          break;
      }


      return(0);
}

/*
 *    This function is called by YGetNextEvent().
 *
 *    Returns the number of segments handled or -1 on fatal error.
 *    Fatal error implies the connection is no longer usable.
 *
 *    This function may close the connection and set the con->fd
 *    to -1 if a YDisconnect or YShutdown event is recieved.
 */
int YNetRecv(YConnection *con)
{
      /* Maximum events to fetch per recieve, further events are
       * discarded.
       */
      const int max_event_fetch_loops = 10000;

      int i, n, loops, status;
      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;

      YEvent *event_ptr;


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

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

      /* Make sure that con->buf_len is greater than con->buf_cont. */
      if((con->buf_len - con->buf_cont) <= 0)
      {
          /* Buffer overflowed and unable to parse, so reset it
           * and disconnect connection since there is no way to
           * seek the next chunk position anymore.
           */
          con->buf_cont = 0;

          fprintf(
            stderr,
 "YNetRecv(): Connection 0x%.8x: Contents overflowed buffer length %ld.\n",
            (u_int32_t)con,
            con->buf_len
          );

          /* Force disconnect. */
          if(con->fd > -1)
          {
            close(con->fd);
            con->fd = -1;
          }

          return(-1);
      }

      /* Check if there are any data to recieve. */
      timeout.tv_sec = 0;
      timeout.tv_usec = 0;
      FD_ZERO(&readfds);
      FD_SET(con->fd, &readfds);
      status = select(con->fd + 1, &readfds, NULL, NULL, &timeout);
      if(status == -1)
          perror("select");

      if(!FD_ISSET(con->fd, &readfds))
          return(0);


      buf_ptr = &(con->buf[con->buf_cont]);
      bytes_read = recv(
          con->fd,
          buf_ptr,
          con->buf_len - con->buf_cont,
          0
      );
      if(bytes_read == 0)
      {
          /* select() indicated there was data to be read but actual
           * recieving of data returned no data. This implies the
           * connection was reset by the peer.
           */

          /* Force disconnect. */
          if(con->fd > -1)
          {
            close(con->fd);
            con->fd = -1;
          }

          return(-1);
      }
      else if(bytes_read <= 0)
      {
          /* Handle error. */
          switch(errno)
          {
            /* These errors are okay, return 0 indicating no data
             * read but also no error.
             */
            case EWOULDBLOCK:
            case EINTR:
            return(0);
            break;

            /* All other errors, force disconnect and return -1. */
            default:
            /* Force disconnect. */
            if(con->fd > -1)
            {
                close(con->fd);
                con->fd = -1;
            }
            return(-1);
            break;
          }
      }

      /* Increment byte count indicating contents in buffer. */
      con->buf_cont += bytes_read;
      /* Sanitize buf_cont to the number of bytes allocated in the
       * buffer. This will be checked on the next loop if they
       * are equal and indicate an overflow if that happens.
       */
      if(con->buf_cont > con->buf_len)
          con->buf_cont = con->buf_len;


      /* Begin handling each segment, handle no more than
       * max_event_fetch_loops from the recieved buffer.
       */
      segments_handled = 0;
      for(loops = 0; loops < max_event_fetch_loops; loops++)
      {
          /* Check if we did not get the first 8 bytes (needed for the
           * 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])));

          /* Recieved data indicated chunk length less than 8 bytes? */
          if(chunk_length < 8)
          {
            /* This should never happen, so consider this a fatal
             * error. Report it and force disconnect.
             */
            YNetPrintError(
                stderr,
                con,
                chunk_length,
                major_op_code,
                minor_op_code,
 "Recieved a segment with header specified chunk length less than 8 bytes"
            );
            if(con->fd > -1)
            {
                close(con->fd);
                con->fd = -1;
            }
            return(-1);
          }

          /* Did we not get the entire chunk? In other words does the
           * buffer contents value suggest that we do not have the
           * entire chunk?
           */
          if((YDataLength)chunk_length > (YDataLength)con->buf_cont)
            break;


          /* Increment total number of queued events. */
          if(con->total_queued_events < 0)
            con->total_queued_events = 0;
          con->total_queued_events++;
          if(con->total_queued_events > YQueuedEventsMax)
          {
            /* Queued events overflowed. */

            con->total_queued_events = YQueuedEventsMax;

            /* Need to force close connection for this. */
            if(con->fd > -1)
            {
                close(con->fd);
                con->fd = -1;
            }

            fprintf(stderr,
 "YNetRecv(): Connection 0x%.8x: Limit of %i queued events exceeded.\n",
                (u_int32_t)con,
                YQueuedEventsMax
            );

            return(-1);
          }
          /* Allocate a new queued event. */
          con->queued_event = (YEvent *)realloc(
            con->queued_event,
            con->total_queued_events * sizeof(YEvent)
          );
          if(con->queued_event == NULL)
          {
            /* Memory allocation error. */

            con->total_queued_events = 0;

            /* Need to force close connection for this. */
            if(con->fd > -1)
            {
                close(con->fd);
                con->fd = -1;
            }

            return(-1);
          }
          /* Get pointer to newly allocated queued event segment. */
          event_ptr = &(con->queued_event[
            con->total_queued_events - 1
          ]);


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

          /* 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;


          /* Post event handling; check special case if the event we
           * just parsed is either YDisconnect or YShutdown.
           */
          if((event_ptr->type == YDisconnect) ||
             (event_ptr->type == YShutdown)
          )
          {
            /* Need to force close connection when we recieve either
             * of these events.
             */
            if(con->fd > -1)
            {
                close(con->fd);
                con->fd = -1;
            }
          }
      }


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

Generated by  Doxygen 1.6.0   Back to index