aboutsummaryrefslogtreecommitdiffstats
path: root/WinQuake/net_ser.c
diff options
context:
space:
mode:
Diffstat (limited to 'WinQuake/net_ser.c')
-rw-r--r--WinQuake/net_ser.c951
1 files changed, 951 insertions, 0 deletions
diff --git a/WinQuake/net_ser.c b/WinQuake/net_ser.c
new file mode 100644
index 0000000..f582f54
--- /dev/null
+++ b/WinQuake/net_ser.c
@@ -0,0 +1,951 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// net_ser.c
+
+#include "quakedef.h"
+#include "net_ser.h"
+#include "dosisms.h"
+#include "crc.h"
+
+#include "net_comx.c"
+
+// serial protocol
+
+#define SERIAL_PROTOCOL_VERSION 3
+
+// The serial protocol is message oriented. The high level message format is
+// a one byte message type (MTYPE_xxx), data, and a 16-bit checksum. All
+// multi-byte fields are sent in network byte order. There are currently 4
+// MTYPEs defined. Their formats are as follows:
+//
+// MTYPE_RELIABLE sequence data_length data checksum eom
+// MTYPE_UNRELIABLE sequence data_length data checksum eom
+// MTYPE_ACK sequence checksum eom
+// MTYPE_CONTROL data_length data checksum eom
+//
+// sequence is an 8-bit unsigned value starting from 0
+// data_length is a 16-bit unsigned value; it is the length of the data only
+// the checksum is a 16-bit value. the CRC formula used is defined in crc.h.
+// the checksum covers the entire messages, excluding itself
+// eom is a special 2 byte sequence used to mark the End Of Message. This is
+// needed for error recovery.
+//
+// A lot of behavior is based on knowledge of the upper level Quake network
+// layer. For example, only one reliable message can be outstanding (pending
+// reception of an MTYPE_ACK) at a time.
+//
+// The low level routines used to communicate with the modem are not part of
+// this protocol.
+//
+// The CONTROL messages are only used for session establishment. They are
+// not reliable or sequenced.
+
+#define MTYPE_RELIABLE 0x01
+#define MTYPE_UNRELIABLE 0x02
+#define MTYPE_CONTROL 0x03
+#define MTYPE_ACK 0x04
+#define MTYPE_CLIENT 0x80
+
+#define ESCAPE_COMMAND 0xe0
+#define ESCAPE_EOM 0x19
+
+static qboolean listening = false;
+
+
+typedef struct SerialLine_s
+{
+ struct SerialLine_s *next;
+ qsocket_t *sock;
+ int lengthStated;
+ int lengthFound;
+ int tty;
+ qboolean connected;
+ qboolean connecting;
+ qboolean client;
+ double connect_time;
+ unsigned short crcStated;
+ unsigned short crcValue;
+ byte currState;
+ byte prevState;
+ byte mtype;
+ byte sequence;
+} SerialLine;
+
+#define STATE_READY 0
+#define STATE_SEQUENCE 1
+#define STATE_LENGTH1 2
+#define STATE_LENGTH2 3
+#define STATE_DATA 4
+#define STATE_CRC1 5
+#define STATE_CRC2 6
+#define STATE_EOM 7
+#define STATE_ESCAPE 8
+#define STATE_ABORT 9
+
+SerialLine serialLine[NUM_COM_PORTS];
+
+int myDriverLevel;
+
+static void Serial_SendACK (SerialLine *p, byte sequence);
+
+
+static void ResetSerialLineProtocol (SerialLine *p)
+{
+ p->connected = false;
+ p->connecting = false;
+ p->currState = STATE_READY;
+ p->prevState = STATE_READY;
+ p->lengthFound = 0;
+}
+
+
+static int ProcessInQueue(SerialLine *p)
+{
+ int b;
+
+ while (1)
+ {
+ b = TTY_ReadByte(p->tty);
+ if (b == ERR_TTY_NODATA)
+ break;
+
+ if (b == ERR_TTY_LINE_STATUS)
+ {
+ p->currState = STATE_ABORT;
+ continue;
+ }
+ if (b == ERR_TTY_MODEM_STATUS)
+ {
+ p->currState = STATE_ABORT;
+ return -1;
+ }
+
+ if (b == ESCAPE_COMMAND)
+ if (p->currState != STATE_ESCAPE)
+ {
+ p->prevState = p->currState;
+ p->currState = STATE_ESCAPE;
+ continue;
+ }
+
+ if (p->currState == STATE_ESCAPE)
+ {
+ if (b == ESCAPE_EOM)
+ {
+ if (p->prevState == STATE_ABORT)
+ {
+ p->currState = STATE_READY;
+ p->lengthFound = 0;
+ continue;
+ }
+
+ if (p->prevState != STATE_EOM)
+ {
+ p->currState = STATE_READY;
+ p->lengthFound = 0;
+ Con_DPrintf("Serial: premature EOM\n");
+ continue;
+ }
+
+ switch (p->mtype)
+ {
+ case MTYPE_RELIABLE:
+ Con_DPrintf("Serial: sending ack %u\n", p->sequence);
+ Serial_SendACK (p, p->sequence);
+ if (p->sequence == p->sock->receiveSequence)
+ {
+ p->sock->receiveSequence = (p->sequence + 1) & 0xff;
+ p->sock->receiveMessageLength += p->lengthFound;
+ }
+ else
+ Con_DPrintf("Serial: reliable out of order; got %u wanted %u\n", p->sequence, p->sock->receiveSequence);
+ break;
+
+ case MTYPE_UNRELIABLE:
+ p->sock->unreliableReceiveSequence = (p->sequence + 1) & 0xff;
+ p->sock->receiveMessageLength += p->lengthFound;
+ break;
+
+ case MTYPE_ACK:
+ Con_DPrintf("Serial: got ack %u\n", p->sequence);
+ if (p->sequence == p->sock->sendSequence)
+ {
+ p->sock->sendSequence = (p->sock->sendSequence + 1) & 0xff;
+ p->sock->canSend = true;
+ }
+ else
+ Con_DPrintf("Serial: ack out of order; got %u wanted %u\n",p->sequence, p->sock->sendSequence);
+ break;
+
+ case MTYPE_CONTROL:
+ p->sock->receiveMessageLength += p->lengthFound;
+ break;
+ }
+
+ p->currState = STATE_READY;
+ p->lengthFound = 0;
+ continue;
+ }
+
+
+ if (b != ESCAPE_COMMAND)
+ {
+ p->currState = STATE_ABORT;
+ Con_DPrintf("Serial: Bad escape sequence\n");
+ continue;
+ }
+
+ // b == ESCAPE_COMMAND
+ p->currState = p->prevState;
+ }
+
+ p->prevState = p->currState;
+
+//DEBUG
+ if (p->sock->receiveMessageLength + p->lengthFound > NET_MAXMESSAGE)
+ {
+ Con_DPrintf("Serial blew out receive buffer: %u\n", p->sock->receiveMessageLength + p->lengthFound);
+ p->currState = STATE_ABORT;
+ }
+ if (p->sock->receiveMessageLength + p->lengthFound == NET_MAXMESSAGE)
+ {
+ Con_DPrintf("Serial hit receive buffer limit: %u\n", p->sock->receiveMessageLength + p->lengthFound);
+ p->currState = STATE_ABORT;
+ }
+//end DEBUG
+
+ switch (p->currState)
+ {
+ case STATE_READY:
+ CRC_Init(&p->crcValue);
+ CRC_ProcessByte(&p->crcValue, b);
+ if (p->client)
+ {
+ if ((b & MTYPE_CLIENT) != 0)
+ {
+ p->currState = STATE_ABORT;
+ Con_DPrintf("Serial: client got own message\n");
+ break;
+ }
+ }
+ else
+ {
+ if ((b & MTYPE_CLIENT) == 0)
+ {
+ p->currState = STATE_ABORT;
+ Con_DPrintf("Serial: server got own message\n");
+ break;
+ }
+ b &= 0x7f;
+ }
+ p->mtype = b;
+ if (b != MTYPE_CONTROL)
+ p->currState = STATE_SEQUENCE;
+ else
+ p->currState = STATE_LENGTH1;
+ if (p->mtype < MTYPE_ACK)
+ {
+ p->sock->receiveMessage[p->sock->receiveMessageLength] = b;
+ p->lengthFound++;
+ }
+ break;
+
+ case STATE_SEQUENCE:
+ p->sequence = b;
+ CRC_ProcessByte(&p->crcValue, b);
+ if (p->mtype != MTYPE_ACK)
+ p->currState = STATE_LENGTH1;
+ else
+ p->currState = STATE_CRC1;
+ break;
+
+ case STATE_LENGTH1:
+ p->lengthStated = b * 256;
+ CRC_ProcessByte(&p->crcValue, b);
+ p->currState = STATE_LENGTH2;
+ break;
+
+ case STATE_LENGTH2:
+ p->lengthStated += b;
+ CRC_ProcessByte(&p->crcValue, b);
+ if (p->mtype == MTYPE_RELIABLE && p->lengthStated > MAX_MSGLEN)
+ {
+ p->currState = STATE_ABORT;
+ Con_DPrintf("Serial: bad reliable message length %u\n", p->lengthStated);
+ }
+ else if (p->mtype == MTYPE_UNRELIABLE && p->lengthStated > MAX_DATAGRAM)
+ {
+ p->currState = STATE_ABORT;
+ Con_DPrintf("Serial: bad unreliable message length %u\n", p->lengthStated);
+ }
+ else
+ {
+ p->currState = STATE_DATA;
+ if (p->mtype < MTYPE_ACK)
+ {
+ *(short *)&p->sock->receiveMessage [p->sock->receiveMessageLength + 1] = p->lengthStated;
+ p->lengthFound += 2;
+ }
+ }
+ break;
+
+ case STATE_DATA:
+ p->sock->receiveMessage[p->sock->receiveMessageLength + p->lengthFound] = b;
+ p->lengthFound++;
+ CRC_ProcessByte(&p->crcValue, b);
+ if (p->lengthFound == p->lengthStated + 3)
+ p->currState = STATE_CRC1;
+ break;
+
+ case STATE_CRC1:
+ p->crcStated = b * 256;
+ p->currState = STATE_CRC2;
+ break;
+
+ case STATE_CRC2:
+ p->crcStated += b;
+ if (p->crcStated == CRC_Value(p->crcValue))
+ {
+ p->currState = STATE_EOM;
+ }
+ else
+ {
+ p->currState = STATE_ABORT;
+ Con_DPrintf("Serial: Bad crc\n");
+ }
+ break;
+
+ case STATE_EOM:
+ p->currState = STATE_ABORT;
+ Con_DPrintf("Serial: Bad message format\n");
+ break;
+
+ case STATE_ABORT:
+ break;
+ }
+ }
+ return 0;
+}
+
+
+int Serial_Init (void)
+{
+ int n;
+
+// LATER do Win32 serial support
+#ifdef _WIN32
+ return -1;
+#endif
+
+ if (COM_CheckParm("-nolan"))
+ return -1;
+ if (COM_CheckParm ("-noserial"))
+ return -1;
+
+ myDriverLevel = net_driverlevel;
+
+ if (TTY_Init())
+ return -1;
+
+ for (n = 0; n < NUM_COM_PORTS; n++)
+ {
+ serialLine[n].tty = TTY_Open(n);
+ ResetSerialLineProtocol (&serialLine[n]);
+ }
+
+ Con_Printf("Serial driver initialized\n");
+ serialAvailable = true;
+
+ return 0;
+}
+
+
+void Serial_Shutdown (void)
+{
+ int n;
+
+ for (n = 0; n < NUM_COM_PORTS; n++)
+ {
+ if (serialLine[n].connected)
+ Serial_Close(serialLine[n].sock);
+ }
+
+ TTY_Shutdown();
+}
+
+
+void Serial_Listen (qboolean state)
+{
+ listening = state;
+}
+
+
+qboolean Serial_CanSendMessage (qsocket_t *sock)
+{
+ return sock->canSend;
+}
+
+
+qboolean Serial_CanSendUnreliableMessage (qsocket_t *sock)
+{
+ return TTY_OutputQueueIsEmpty(((SerialLine *)sock->driverdata)->tty);
+}
+
+
+int Serial_SendMessage (qsocket_t *sock, sizebuf_t *message)
+{
+ SerialLine *p;
+ int n;
+ unsigned short crc;
+ byte b;
+
+ p = (SerialLine *)sock->driverdata;
+ CRC_Init (&crc);
+
+ // message type
+ b = MTYPE_RELIABLE;
+ if (p->client)
+ b |= MTYPE_CLIENT;
+ TTY_WriteByte(p->tty, b);
+ CRC_ProcessByte (&crc, b);
+
+ // sequence
+ b = p->sock->sendSequence;
+ TTY_WriteByte(p->tty, b);
+ if (b == ESCAPE_COMMAND)
+ TTY_WriteByte(p->tty, b);
+ CRC_ProcessByte (&crc, b);
+
+ // data length
+ b = message->cursize >> 8;
+ TTY_WriteByte(p->tty, b);
+ if (b == ESCAPE_COMMAND)
+ TTY_WriteByte(p->tty, b);
+ CRC_ProcessByte (&crc, b);
+ b = message->cursize & 0xff;
+ TTY_WriteByte(p->tty, b);
+ if (b == ESCAPE_COMMAND)
+ TTY_WriteByte(p->tty, b);
+ CRC_ProcessByte (&crc, b);
+
+ // data
+ for (n = 0; n < message->cursize; n++)
+ {
+ b = message->data[n];
+ TTY_WriteByte(p->tty, b);
+ if (b == ESCAPE_COMMAND)
+ TTY_WriteByte(p->tty, b);
+ CRC_ProcessByte (&crc, b);
+ }
+
+ // checksum
+ b = CRC_Value (crc) >> 8;
+ TTY_WriteByte(p->tty, b);
+ if (b == ESCAPE_COMMAND)
+ TTY_WriteByte(p->tty, b);
+ b = CRC_Value (crc) & 0xff;
+ TTY_WriteByte(p->tty, b);
+ if (b == ESCAPE_COMMAND)
+ TTY_WriteByte(p->tty, b);
+
+ // end of message
+ TTY_WriteByte(p->tty, ESCAPE_COMMAND);
+ TTY_WriteByte(p->tty, ESCAPE_EOM);
+
+ TTY_Flush(p->tty);
+
+ // mark sock as busy and save the message for possible retransmit
+ sock->canSend = false;
+ Q_memcpy(sock->sendMessage, message->data, message->cursize);
+ sock->sendMessageLength = message->cursize;
+ sock->lastSendTime = net_time;
+
+ return 1;
+}
+
+
+static void ReSendMessage (qsocket_t *sock)
+{
+ sizebuf_t temp;
+
+ Con_DPrintf("Serial: re-sending reliable\n");
+ temp.data = sock->sendMessage;
+ temp.maxsize = sock->sendMessageLength;
+ temp.cursize = sock->sendMessageLength;
+ Serial_SendMessage (sock, &temp);
+}
+
+
+int Serial_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *message)
+{
+ SerialLine *p;
+ int n;
+ unsigned short crc;
+ byte b;
+
+ p = (SerialLine *)sock->driverdata;
+
+ if (!TTY_OutputQueueIsEmpty(p->tty))
+ {
+ TTY_Flush(p->tty);
+ return 1;
+ }
+
+ CRC_Init (&crc);
+
+ // message type
+ b = MTYPE_UNRELIABLE;
+ if (p->client)
+ b |= MTYPE_CLIENT;
+ TTY_WriteByte(p->tty, b);
+ CRC_ProcessByte (&crc, b);
+
+ // sequence
+ b = p->sock->unreliableSendSequence;
+ p->sock->unreliableSendSequence = (b + 1) & 0xff;
+ TTY_WriteByte(p->tty, b);
+ if (b == ESCAPE_COMMAND)
+ TTY_WriteByte(p->tty, b);
+ CRC_ProcessByte (&crc, b);
+
+ // data length
+ b = message->cursize >> 8;
+ TTY_WriteByte(p->tty, b);
+ if (b == ESCAPE_COMMAND)
+ TTY_WriteByte(p->tty, b);
+ CRC_ProcessByte (&crc, b);
+ b = message->cursize & 0xff;
+ TTY_WriteByte(p->tty, b);
+ if (b == ESCAPE_COMMAND)
+ TTY_WriteByte(p->tty, b);
+ CRC_ProcessByte (&crc, b);
+
+ // data
+ for (n = 0; n < message->cursize; n++)
+ {
+ b = message->data[n];
+ TTY_WriteByte(p->tty, b);
+ if (b == ESCAPE_COMMAND)
+ TTY_WriteByte(p->tty, b);
+ CRC_ProcessByte (&crc, b);
+ }
+
+ // checksum
+ b = CRC_Value (crc) >> 8;
+ TTY_WriteByte(p->tty, b);
+ if (b == ESCAPE_COMMAND)
+ TTY_WriteByte(p->tty, b);
+ b = CRC_Value (crc) & 0xff;
+ TTY_WriteByte(p->tty, b);
+ if (b == ESCAPE_COMMAND)
+ TTY_WriteByte(p->tty, b);
+
+ // end of message
+ TTY_WriteByte(p->tty, ESCAPE_COMMAND);
+ TTY_WriteByte(p->tty, ESCAPE_EOM);
+
+ TTY_Flush(p->tty);
+
+ return 1;
+}
+
+
+static void Serial_SendACK (SerialLine *p, byte sequence)
+{
+ unsigned short crc;
+ byte b;
+
+ CRC_Init (&crc);
+
+ // message type
+ b = MTYPE_ACK;
+ if (p->client)
+ b |= MTYPE_CLIENT;
+ TTY_WriteByte(p->tty, b);
+ CRC_ProcessByte (&crc, b);
+
+ // sequence
+ b = sequence;
+ TTY_WriteByte(p->tty, b);
+ if (b == ESCAPE_COMMAND)
+ TTY_WriteByte(p->tty, b);
+ CRC_ProcessByte (&crc, b);
+
+ // checksum
+ b = CRC_Value (crc) >> 8;
+ TTY_WriteByte(p->tty, b);
+ if (b == ESCAPE_COMMAND)
+ TTY_WriteByte(p->tty, b);
+ b = CRC_Value (crc) & 0xff;
+ TTY_WriteByte(p->tty, b);
+ if (b == ESCAPE_COMMAND)
+ TTY_WriteByte(p->tty, b);
+
+ // end of message
+ TTY_WriteByte(p->tty, ESCAPE_COMMAND);
+ TTY_WriteByte(p->tty, ESCAPE_EOM);
+
+ TTY_Flush(p->tty);
+}
+
+
+static void Serial_SendControlMessage (SerialLine *p, sizebuf_t *message)
+{
+ unsigned short crc;
+ int n;
+ byte b;
+
+ CRC_Init (&crc);
+
+ // message type
+ b = MTYPE_CONTROL;
+ if (p->client)
+ b |= MTYPE_CLIENT;
+ TTY_WriteByte(p->tty, b);
+ CRC_ProcessByte (&crc, b);
+
+ // data length
+ b = message->cursize >> 8;
+ TTY_WriteByte(p->tty, b);
+ if (b == ESCAPE_COMMAND)
+ TTY_WriteByte(p->tty, b);
+ CRC_ProcessByte (&crc, b);
+ b = message->cursize & 0xff;
+ TTY_WriteByte(p->tty, b);
+ if (b == ESCAPE_COMMAND)
+ TTY_WriteByte(p->tty, b);
+ CRC_ProcessByte (&crc, b);
+
+ // data
+ for (n = 0; n < message->cursize; n++)
+ {
+ b = message->data[n];
+ TTY_WriteByte(p->tty, b);
+ if (b == ESCAPE_COMMAND)
+ TTY_WriteByte(p->tty, b);
+ CRC_ProcessByte (&crc, b);
+ }
+
+ // checksum
+ b = CRC_Value (crc) >> 8;
+ TTY_WriteByte(p->tty, b);
+ if (b == ESCAPE_COMMAND)
+ TTY_WriteByte(p->tty, b);
+ b = CRC_Value (crc) & 0xff;
+ TTY_WriteByte(p->tty, b);
+ if (b == ESCAPE_COMMAND)
+ TTY_WriteByte(p->tty, b);
+
+ // end of message
+ TTY_WriteByte(p->tty, ESCAPE_COMMAND);
+ TTY_WriteByte(p->tty, ESCAPE_EOM);
+
+ TTY_Flush(p->tty);
+}
+
+
+static int _Serial_GetMessage (SerialLine *p)
+{
+ byte ret;
+ short length;
+
+ if (ProcessInQueue(p))
+ return -1;
+
+ if (p->sock->receiveMessageLength == 0)
+ return 0;
+
+ ret = p->sock->receiveMessage[0];
+ length = *(short *)&p->sock->receiveMessage[1];
+ if (ret == MTYPE_CONTROL)
+ ret = 1;
+
+ SZ_Clear (&net_message);
+ SZ_Write (&net_message, &p->sock->receiveMessage[3], length);
+
+ length += 3;
+ p->sock->receiveMessageLength -= length;
+
+ if (p->sock->receiveMessageLength + p->lengthFound)
+ Q_memcpy(p->sock->receiveMessage, &p->sock->receiveMessage[length], p->sock->receiveMessageLength + p->lengthFound);
+
+ return ret;
+}
+
+int Serial_GetMessage (qsocket_t *sock)
+{
+ SerialLine *p;
+ int ret;
+
+ p = (SerialLine *)sock->driverdata;
+
+ ret = _Serial_GetMessage (p);
+
+ if (ret == 1)
+ messagesReceived++;
+
+ if (!sock->canSend)
+ if ((net_time - sock->lastSendTime) > 1.0)
+ {
+ ReSendMessage (sock);
+ sock->lastSendTime = net_time;
+ }
+
+ return ret;
+}
+
+
+void Serial_Close (qsocket_t *sock)
+{
+ SerialLine *p = (SerialLine *)sock->driverdata;
+ TTY_Close(p->tty);
+ ResetSerialLineProtocol (p);
+}
+
+
+char *com_types[] = {"direct", "modem"};
+unsigned com_bauds[] = {9600, 14400, 19200, 28800, 57600};
+
+void Serial_SearchForHosts (qboolean xmit)
+{
+ int n;
+ SerialLine *p;
+
+ if (sv.active)
+ return;
+
+ if (hostCacheCount == HOSTCACHESIZE)
+ return;
+
+ // see if we've already answered
+ for (n = 0; n < hostCacheCount; n++)
+ if (Q_strcmp (hostcache[n].cname, "#") == 0)
+ return;
+
+ for (n = 0; n < NUM_COM_PORTS; n++)
+ if (TTY_IsEnabled(n))
+ break;
+ if (n == NUM_COM_PORTS)
+ return;
+ p = &serialLine[n];
+
+ if (TTY_IsModem(p->tty))
+ return;
+
+ sprintf(hostcache[hostCacheCount].name, "COM%u", n+1);
+ Q_strcpy(hostcache[hostCacheCount].map, "");
+ hostcache[hostCacheCount].users = 0;
+ hostcache[hostCacheCount].maxusers = 0;
+ hostcache[hostCacheCount].driver = net_driverlevel;
+ Q_strcpy(hostcache[hostCacheCount].cname, "#");
+ hostCacheCount++;
+
+ return;
+}
+
+
+static qsocket_t *_Serial_Connect (char *host, SerialLine *p)
+{
+ int ret;
+ double start_time;
+ double last_time;
+
+ p->client = true;
+ if (TTY_Connect(p->tty, host))
+ return NULL;
+
+ p->sock = NET_NewQSocket ();
+ p->sock->driver = myDriverLevel;
+ if (p->sock == NULL)
+ {
+ Con_Printf("No sockets available\n");
+ return NULL;
+ }
+ p->sock->driverdata = p;
+
+ // send the connection request
+ start_time = SetNetTime();
+ last_time = 0.0;
+
+ SZ_Clear(&net_message);
+ MSG_WriteByte(&net_message, CCREQ_CONNECT);
+ MSG_WriteString(&net_message, "QUAKE");
+ do
+ {
+ SetNetTime();
+ if ((net_time - last_time) >= 1.0)
+ {
+ Serial_SendControlMessage (p, &net_message);
+ last_time = net_time;
+ Con_Printf("trying...\n"); SCR_UpdateScreen ();
+ }
+ ret = _Serial_GetMessage (p);
+ }
+ while (ret == 0 && (net_time - start_time) < 5.0);
+
+ if (ret == 0)
+ {
+ Con_Printf("Unable to connect, no response\n");
+ goto ErrorReturn;
+ }
+
+ if (ret == -1)
+ {
+ Con_Printf("Connection request error\n");
+ goto ErrorReturn;
+ }
+
+ MSG_BeginReading ();
+ ret = MSG_ReadByte();
+ if (ret == CCREP_REJECT)
+ {
+ Con_Printf(MSG_ReadString());
+ goto ErrorReturn;
+ }
+ if (ret != CCREP_ACCEPT)
+ {
+ Con_Printf("Unknown connection response\n");
+ goto ErrorReturn;
+ }
+
+ p->connected = true;
+ p->sock->lastMessageTime = net_time;
+
+ Con_Printf ("Connection accepted\n");
+
+ return p->sock;
+
+ErrorReturn:
+ TTY_Disconnect(p->tty);
+ return NULL;
+}
+
+qsocket_t *Serial_Connect (char *host)
+{
+ int n;
+ qsocket_t *ret = NULL;
+
+ // see if this looks like a phone number
+ if (*host == '#')
+ host++;
+ for (n = 0; n < Q_strlen(host); n++)
+ if (host[n] == '.' || host[n] == ':')
+ return NULL;
+
+ for (n = 0; n < NUM_COM_PORTS; n++)
+ if (TTY_IsEnabled(n) && !serialLine[n].connected)
+ if ((ret = _Serial_Connect (host, &serialLine[n])))
+ break;
+ return ret;
+}
+
+
+static qsocket_t *_Serial_CheckNewConnections (SerialLine *p)
+{
+ int command;
+
+ p->client = false;
+ if (!TTY_CheckForConnection(p->tty))
+ return NULL;
+
+ if (TTY_IsModem(p->tty))
+ {
+ if (!p->connecting)
+ {
+ p->connecting = true;
+ p->connect_time = net_time;
+ }
+ else if ((net_time - p->connect_time) > 15.0)
+ {
+ p->connecting = false;
+ TTY_Disconnect(p->tty);
+ return NULL;
+ }
+ }
+
+ p->sock = NET_NewQSocket ();
+ p->sock->driver = myDriverLevel;
+ if (p->sock == NULL)
+ {
+ Con_Printf("No sockets available\n");
+ return NULL;
+ }
+ p->sock->driverdata = p;
+
+ SZ_Clear(&net_message);
+ if (_Serial_GetMessage(p) != 1)
+ {
+ NET_FreeQSocket(p->sock);
+ return NULL;
+ }
+
+ MSG_BeginReading ();
+ command = MSG_ReadByte();
+
+ if (command == CCREQ_SERVER_INFO)
+ {
+ if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0)
+ return NULL;
+
+ if (MSG_ReadByte() != SERIAL_PROTOCOL_VERSION)
+ return NULL;
+
+ SZ_Clear(&net_message);
+ MSG_WriteByte(&net_message, CCREP_SERVER_INFO);
+ MSG_WriteString(&net_message, hostname.string);
+ MSG_WriteString(&net_message, sv.name);
+ MSG_WriteByte(&net_message, net_activeconnections);
+ MSG_WriteByte(&net_message, svs.maxclients);
+ Serial_SendControlMessage (p, &net_message);
+ SZ_Clear(&net_message);
+ return NULL;
+ }
+
+ if (command != CCREQ_CONNECT)
+ return NULL;
+
+ if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0)
+ return NULL;
+
+ // send him back the info about the server connection he has been allocated
+ SZ_Clear(&net_message);
+ MSG_WriteByte(&net_message, CCREP_ACCEPT);
+ Serial_SendControlMessage (p, &net_message);
+ SZ_Clear(&net_message);
+
+ p->connected = true;
+ p->connecting = false;
+ p->sock->lastMessageTime = net_time;
+ sprintf(p->sock->address, "COM%u", (int)((p - serialLine) + 1));
+
+ return p->sock;
+}
+
+qsocket_t *Serial_CheckNewConnections (void)
+{
+ int n;
+ qsocket_t *ret = NULL;
+
+ for (n = 0; n < NUM_COM_PORTS; n++)
+ if (TTY_IsEnabled(n) && !serialLine[n].connected)
+ if ((ret = _Serial_CheckNewConnections (&serialLine[n])))
+ break;
+ return ret;
+}