/*
 * universal multiplayer library level 0 implementation file
 * transport layer: TCP PC-windows 95
 */
 
/*
 *      Author: Christophe Roguet after Benoit GERMAIN
 *
 *      Modification Date:      07/05/96 first draft 
 *
 *                              29/10/96 changed the connection process, removed the UDP socket
 */
 
#include "warnings.h"
#include "NetSock.h"
 
/*
 * we need this one to fill the interface structure
 * with pointers to the correct functions
 */
 
#include "Privl0TCP.h"
#include "PrivNetDef.h"
#include "L0GlDef.h"
#include "NetMemCo.h"
#include "NetEnd.h"
#include <NET\Netdebug.h>
 
/* size of socket send buffer */
#define C_iSendBufferSize 666
 
/* array of descriptors to access a physical channel. */
static tdstL0PCWin95TCPChannel gs_a_stL0PCWin95TCPChannels[C_ucL0MaxNumberOfTCPChannels];
 
/* the IP adress of the computer the application is running on, in network order */
static struct in_addr gs_stMyAddress;
 
/* the global socket descriptor used to listen on our TCP port */
static SOCKET gs_uiListenSD;
 
static char gs_bL0PCWin95TCPInitState;
 
static tdstWinSockFunc *gs_p_stTCPWinSocDesc;
static char gs_cIsWinsocDescInit = 0;
 
#define DLL_FD_ISSET(fd, set) ((gs_p_stTCPWinSocDesc->m_pfn_i_WSAFDIsSet)((SOCKET)(fd), (fd_set *)(set)))
 
/* static functions */
 
static tduwNetChannel uwL0PCWin95TCPOpenChannel(SOCKET);
static void vL0PCWin95TCPCloseChannel(tduwNetChannel);
static NetLib_tdeErrorStatus eL0PCWin95TCPConfigureSocket(SOCKET);
 
/*****************************************************************************/
 
/*****************************************************************************
 *
 * Description: uwL0PCWin95TCPGetChannel
 *
 * look up the channel associated to a particular remote address
 *
 *****************************************************************************
 *
 * Input:   inet address to look up
 *
 * Output:  identification of the channel
 *
 *****************************************************************************
 * Creation Date: April 26, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date: June 6, 1996                    Author: Christophe ROGUET
 * take the 'broadcast channel' flag into account, in order to be able to use
 * the library with a direct connection over internet
 *
 ****************************************************************************/
tduwNetChannel uwL0PCWin95TCPGetChannel(struct sockaddr_in *Key)
{
 tduwNetChannel uwCurrentChannel;
 for(uwCurrentChannel=0; uwCurrentChannel<C_ucL0MaxNumberOfTCPChannels; uwCurrentChannel++)
  if(
  gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsSlotInUse==1
  && !gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsBroadcast
  &&  gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].stRemoteAddr.sin_addr.s_addr==Key->sin_addr.s_addr
  )
   return uwCurrentChannel;
 
 return C_uwNetInvalidChannel;      /* Error invalid channel ?*/
}
 
 /*****************************************************************************
 *
 * Description: uwL0PCWin95TCPOpenChannel
 *
 * returns the identification number of an unused slot in the channel table
 *
 *****************************************************************************
 *
 * Input:   inet address to bind to the channel
 *
 * Output:  identification of the channel
 *
 *****************************************************************************
 * Creation Date: April 24, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
static tduwNetChannel uwL0PCWin95TCPOpenChannel(SOCKET uiSD)
{
 tduwNetChannel uwCurrentChannel;
 
 /* scan the channel table until we find an unused slot */
 for(
  uwCurrentChannel=0;
  uwCurrentChannel<C_ucL0MaxNumberOfTCPChannels;
  uwCurrentChannel++)
   if(gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsSlotInUse == 0)
   {
    /* set the use mark */
    gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsSlotInUse=1;
    gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsBroadcast=0;
    gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsReconnect=0;
    /* set the socket descriptor for this channel */
    gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].uiSD=uiSD;
    /* the channel isn't connected */
    gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsConnected=0;
 
    return uwCurrentChannel;
   }
 
 /* there are no free channels */
 return C_uwNetInvalidChannel;/* Error invalid channel */
}
 
/*****************************************************************************
 *
 * Description: vL0PCWin95TCPCloseChannel
 *
 * frees a slot in the channel table
 *
 *****************************************************************************
 *
 * Input:     identifier of the channel
 *
 * Output:        none
 *
 *****************************************************************************
 * Creation Date: April 24, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
static void vL0PCWin95TCPCloseChannel(tduwNetChannel uwChannel)
{
 tdstL0PCWin95TCPChannel *pstChannel;     /* Channel to read from */
 
 if(uwChannel>=C_ucL0MaxNumberOfTCPChannels)
  return /*NetLib_E_es_InvalidChannel*/; /* the channel does not exist in the table */
 
 pstChannel=&gs_a_stL0PCWin95TCPChannels[uwChannel];
 
 if(!pstChannel->ubf1IsSlotInUse)
  return /*NetLib_E_es_ChannelUninitialized*/;   /* the slot is not initialized for use */
  
 /* clear the use mark */
 pstChannel->ubf1IsSlotInUse=0;
 /* release the socket descriptor for this channel */
 gs_p_stTCPWinSocDesc->m_pfn_i_closesocket(pstChannel->uiSD);
 /* set the remote address to none */
 pstChannel->stRemoteAddr.sin_addr.s_addr=gs_p_stTCPWinSocDesc->m_pfn_ul_htonl(INADDR_NONE);
}
 
 /*****************************************************************************
 *
 * Description: uwL0PCWin95TCPStartChannelScan
 *
 * returns the identification number of the first channel for the protocol
 *
 *****************************************************************************
 *
 * Input:   none
 *
 * Output:  identification of the channel
 *
 *****************************************************************************
 * Creation Date: April 24, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
tduwNetChannel uwL0PCWin95TCPStartChannelScan(void)
{
 tduwNetChannel uwCurrentChannel;
 
 /* scan the array of channel definitions */
 uwCurrentChannel= C_ucL0MaxNumberOfTCPChannels-1;
 while
  (
  (uwCurrentChannel!=C_uwNetInvalidChannel)&&
  (!gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsSlotInUse)
  )
  uwCurrentChannel--;
 
 /* return its index value */
 return uwCurrentChannel;
}
 
/*****************************************************************************
 *
 * Description: uwL0PCWin95TCPNextChannel
 *
 * returns the identification number of the channel following the last
 *
 *****************************************************************************
 *
 * Input:   uwLastScannedChannel, index returned by the previous call
 *
 * Output:  identification of the channel following the specified one
 *
 *****************************************************************************
 * Creation Date: April 24, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
tduwNetChannel uwL0PCWin95TCPNextChannel(tduwNetChannel uwLastScannedChannel)
{
 tduwNetChannel uwCurrentChannel;
 /*
  * scan the array of channel definitions, starting with the channel following
  * the last channel returned
  */
 uwCurrentChannel = (unsigned short)(uwLastScannedChannel-1);
 while
  (
  (uwCurrentChannel!=C_uwNetInvalidChannel)&&
  (!gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsSlotInUse)
  )
  uwCurrentChannel--;
 
 /* else no slot is in use */
 return uwCurrentChannel;
}
 
/*****************************************************************************
 *
 * Description: uwL0PCWin95TCPStartBroadcastChannelScan
 *
 * returns the identification number of the first broadcast channel
 * for the protocol
 *
 *****************************************************************************
 *
 * Input:   none
 *
 * Output:  identification of the channel
 *
 *****************************************************************************
 * Creation Date: April 24, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
tduwNetChannel uwL0PCWin95TCPStartBroadcastChannelScan(void)
{
 tduwNetChannel uwCurrentChannel;
 
 /* scan the array of channel definitions */
 uwCurrentChannel= C_ucL0MaxNumberOfTCPChannels-1;
 while
  (
  (uwCurrentChannel!=C_uwNetInvalidChannel)&&
  ((!gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsSlotInUse)||
   (!gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsBroadcast))
  )
  uwCurrentChannel--;
 
 /* else no slot is in use */
 return uwCurrentChannel;
}
 
/*****************************************************************************
 *
 * Description: uwL0PCWin95TCPNextBroadcastChannel
 *
 * returns the identification number of the broadcast channel following
 * the specified one
 *
 *****************************************************************************
 *
 * Input:   uwLastScannedChannel, index returned by the previous call
 *
 * Output:  identification of the channel following the specified one
 *
 *****************************************************************************
 * Creation Date: April 24, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
tduwNetChannel uwL0PCWin95TCPNextBroadcastChannel(tduwNetChannel uwLastScannedChannel)
{
 tduwNetChannel uwCurrentChannel;
 
 uwCurrentChannel = (unsigned short)(uwLastScannedChannel-1);
 while
  (
  (uwCurrentChannel!=C_uwNetInvalidChannel)&&
  ((!gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsSlotInUse)||
   (!gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsBroadcast))
  )
  uwCurrentChannel--;
 
 /* else no slot is in use */
 return uwCurrentChannel;
}
 
/*****************************************************************************
 *
 * Description: uwL0PCWin95TCPGetNewReconnectChannel
 *
 * returns the identification number of a reconnect channel whose connection succeeded
 * returns C_uwNetInvalidChannel if there is no such channel
 *****************************************************************************
 *
 * Input:   none
 *
 * Output:  identification of the channel
 *
 *****************************************************************************
 * Creation Date: January 24, 1997         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
tduwNetChannel uwL0PCWin95TCPGetNewReconnectChannel(void)
{
 tduwNetChannel uwCurrentChannel;
 
 /* scan the array of channel definitions */
 uwCurrentChannel= C_ucL0MaxNumberOfTCPChannels-1;
 while
  (
  (uwCurrentChannel!=C_uwNetInvalidChannel)&&
  ((!gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsSlotInUse)||
   (!gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsReconnect) ||
   (!gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsConnected))
  )
  uwCurrentChannel--;
 
 if(uwCurrentChannel!=C_uwNetInvalidChannel)
   /* if we found a new channel, unmark it */
   gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsReconnect=0;
 
 return uwCurrentChannel;
}
 
/*****************************************************************************
 *
 * Description: eL0PCWin95TCPReadData
 *
 *
 *****************************************************************************
 *
 * Input:   p_uwChannel, pointer on a field to store the identification of
 *          a new channel in case of access to a newly created broacast channel
 *          ppData, address of the pointer to retrieve a full message if
 *          necessary.
 *
 * Output:  an error condition.
 *
 *****************************************************************************
 * Creation Date: April 26, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date: 19/06/96                        Author: Christophe ROGUET
 * stopped using ioctlsocket(FIONREAD), which doesn't work as expected and was
 * causing a bug when using the library over internet
 * instead, I now use recv(MSG_PEEK) and test the return value
 * Date: 25/10/96                        Author: Christophe ROGUET
 * test the recv() return value for 0 : detects graceful disconnections
 * Date: 29/10/96                        Author: Christophe ROGUET
 * no UDP socket/broadcast channel any more
 ****************************************************************************/
NetLib_tdeErrorStatus eL0PCWin95TCPReadData(tduwNetChannel *p_uwChannel, tdpPointer *ppData)
{
 tdstL0PCWin95TCPChannel *pstChannel;     /* Channel to read from */
 tdstNetMessage stMsgHeader;            /* header of the incoming message */
 unsigned long ulMsgSize;               /* length of the incoming message (header included) */
 int iErrorCode;
 long lRecvdBytes;
 
 /* verify the validity of the Channel */
 
 if(*p_uwChannel>=C_ucL0MaxNumberOfTCPChannels)
  return NetLib_E_es_InvalidChannel; /* the channel does not exist in the table */
 
 pstChannel=&gs_a_stL0PCWin95TCPChannels[*p_uwChannel];
 
 if(!pstChannel->ubf1IsSlotInUse)
  return NetLib_E_es_ChannelUninitialized;   /* the slot is not initialized for use */
 
 if(!pstChannel->ubf1IsConnected)
  /* is it worth a new error code ? */
  return NetLib_E_es_ChannelUninitialized;   /* the slot should not be accessed yet */
 
 /* try to get a message from the socket */
 /* if no message is being received, try to read a header from the socket and allocate a buffer for the message */
 if(pstChannel->pMsg==C_pNull)
 {
  if((lRecvdBytes=gs_p_stTCPWinSocDesc->m_pfn_i_recv(pstChannel->uiSD,(char *)&stMsgHeader, sizeof stMsgHeader, MSG_PEEK))
   ==SOCKET_ERROR)
  {
   iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError();
   if(iErrorCode==WSAEWOULDBLOCK)
    return NetLib_E_es_FIFOIsEmpty;    /* error: no message available */
   if(iErrorCode==WSAECONNRESET)
   {
#if defined(NET_USE_DEBUG)
    vDebugSI(Net_C_Debug_TCP,"connection reset",pstChannel->uiSD);
#endif /* NET_USE_DEBUG */
    /* the connection has been broken */
    vL0PCWin95TCPCloseChannel(*p_uwChannel);
    return NetLib_E_es_ChannelIsInvalid;
   }
   return NetLib_E_es_UnknownError;
  }
 
  if(lRecvdBytes==0)
  {
#if defined(NET_USE_DEBUG)
    vDebugSI(Net_C_Debug_TCP,"graceful disconnection",pstChannel->uiSD);
#endif /* NET_USE_DEBUG */
    /* the connection has been closed by the remote end */
    vL0PCWin95TCPCloseChannel(*p_uwChannel);
    return NetLib_E_es_ChannelIsInvalid;
  }
  
  if(lRecvdBytes!=sizeof stMsgHeader)
  {
 
#if defined(NET_USE_DEBUG)
    vDebugSI(Net_C_Debug_TCP,"recv MSG_PEEK partiel",pstChannel->uiSD);
#endif /* NET_USE_DEBUG */
   return NetLib_E_es_FIFOIsEmpty;    /* error: header partially available */
  }
 
    /* if big/little endian differs, swap the header before anything else*/
  if (stMsgHeader.uxHeadBigEndian!=NetLib_ucGetLittleBigEndian())
    NetLib_eSwapLittleBigEndianMsgHeader(&stMsgHeader);
 
  if
  (
  (M_uwCheckSumSlot((&stMsgHeader))!=M_uwProcessCheckSum((&stMsgHeader))) ||
  (stMsgHeader.eMessageType >= E_Net_mt_LastSysMsg)||
  (stMsgHeader.eMessageType <= E_Net_mt_InvalidMessage)
  )
  {
 
#if defined(NET_USE_DEBUG)
    vDebugSDump(Net_C_Debug_TCP,"type de message incorrect",&stMsgHeader, sizeof stMsgHeader);
#endif /* NET_USE_DEBUG */
   return NetLib_E_es_UnknownError;
  }
 
#if defined(NET_USE_DEBUG)
  vDebugSI(Net_C_Debug_TCP,"Read data",pstChannel->uiSD);
  vDebugSI(Net_C_Debug_TCP,"\t\tType de message",stMsgHeader.eMessageType);
  vDebugSI(Net_C_Debug_TCP,"\t\tTaille du message",stMsgHeader.uwMessageSizeInBytes);
  vDebugSI(Net_C_Debug_TCP,"\t\tExpediteur",stMsgHeader.uxSenderId);
  vDebugSI(Net_C_Debug_TCP,"\t\tDestinataire",stMsgHeader.uxRecipientId);
#endif /* NET_USE_DEBUG */
  /* compute the message length */
  ulMsgSize=stMsgHeader.uwMessageSizeInBytes + sizeof(tdstNetMessage);
  pstChannel->pMsg=pMalloc(ulMsgSize);
  if(pstChannel->pMsg == C_pNull)
  {
   /* the pointer couldn't be allocated :*/
   /* Purge the message : DOES THIS WORK FINE WITH A STREAM SOCKET ?????? */
   if(gs_p_stTCPWinSocDesc->m_pfn_i_recv(pstChannel->uiSD, NULL, ulMsgSize, 0)<0)
   {
 
#if defined(NET_USE_DEBUG)
  vDebugSI(Net_C_Debug_TCP,"Read data Error allocation impossible",gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError());
#endif /* NET_USE_DEBUG */
    return NetLib_E_es_UnknownError;
   }
   /* Return en invalid channel :*/
   return NetLib_E_es_NotEnoughMemory;
  }
 
  /* no bytes of this message have been read yet */
  pstChannel->ulBytesRead=0;
  pstChannel->ulBytesLeft=ulMsgSize;
 }
 else
 {
#if defined(NET_USE_DEBUG)
  vDebugSISI(Net_C_Debug_TCP,"\t\t recv() msg partiel bytes read",pstChannel->ulBytesRead,
           "bytes left",pstChannel->ulBytesLeft);
#endif /* NET_USE_DEBUG */
 }
 
 if((lRecvdBytes=gs_p_stTCPWinSocDesc->m_pfn_i_recv(pstChannel->uiSD, &pstChannel->pMsg[pstChannel->ulBytesRead], pstChannel->ulBytesLeft, 0))<0)
 {
 
#if defined(NET_USE_DEBUG)
  vDebugSI(Net_C_Debug_TCP,"Read data Error",gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError());
#endif /* NET_USE_DEBUG */
  iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError();
  if(iErrorCode==WSAEWOULDBLOCK)
   return NetLib_E_es_FIFOIsEmpty;
 
  /* free the message buffer or not ??? */
  return NetLib_E_es_UnknownError;
 }
 
#if defined(NET_USE_DEBUG)
if(pstChannel->ulBytesLeft!=0)
{
  vDebugSISI(Net_C_Debug_TCP,"\t\t recv() msg partiel bytes read",pstChannel->ulBytesRead,
             "bytes left",pstChannel->ulBytesLeft);
}
#endif /* NET_USE_DEBUG */
 
 pstChannel->ulBytesRead+=lRecvdBytes;
 pstChannel->ulBytesLeft-=lRecvdBytes;
 if(pstChannel->ulBytesLeft>0)
 { /* has the message been read in whole ??? */
 
#if defined(NET_USE_DEBUG)
  vDebugSISI(Net_C_Debug_TCP,"\t\t recv() msg partiel bytes read",pstChannel->ulBytesRead,
             "bytes left",pstChannel->ulBytesLeft);
#endif /* NET_USE_DEBUG */
 
  return NetLib_E_es_FIFOIsEmpty; /* error: no full message available yet */
 }
 /* return the message */
 *ppData=pstChannel->pMsg;
 pstChannel->pMsg=C_pNull;
 return NetLib_E_es_NoError; /* no error */
}
 
 
/*****************************************************************************
 *
 * Description: eL0PCWin95TCPSendData
 *
 *
 *****************************************************************************
 *
 * Input:   uwChannel, channel to send the message into.
 *          pData, pointer on the block to send
 *
 * Output:  an error condition.
 *
 *****************************************************************************
 * Creation Date: April 16, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date: October 29, 1996                Author: Christophe ROGUET
 * no more UDP socket/broadcast channel
 *****************************************************************************/
NetLib_tdeErrorStatus eL0PCWin95TCPSendData(tduwNetChannel uwChannel, tdpPointer pData)
{
 unsigned long ulBytestoSend;
 unsigned long ulBytesSent;
 int iErrorCode;
 
#if defined(NET_USE_DEBUG)
 vDebugSI(Net_C_Debug_TCP,"Send data",gs_a_stL0PCWin95TCPChannels[uwChannel].uiSD);
 vDebugSI(Net_C_Debug_TCP,"\t\tType de message",((tdstNetMessage*)pData)->eMessageType);
 vDebugSI(Net_C_Debug_TCP,"\t\tTaille du message",((tdstNetMessage*)pData)->uwMessageSizeInBytes);
 vDebugSI(Net_C_Debug_TCP,"\t\tExpediteur",((tdstNetMessage*)pData)->uxSenderId);
 vDebugSI(Net_C_Debug_TCP,"\t\tDestinataire",((tdstNetMessage*)pData)->uxRecipientId);
#endif /* NET_USE_DEBUG */
 /* verify the validity of the Channel */
 if(uwChannel>=C_ucL0MaxNumberOfTCPChannels)
  return NetLib_E_es_InvalidChannel; /* the channel does not exist in the table */
 
 if(!gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsSlotInUse)
  return NetLib_E_es_ChannelUninitialized;   /* the slot is not initialized for use */
 
 if(!gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsConnected)
  /* is it worth a new error code ? */
  return NetLib_E_es_ChannelUninitialized;   /* the slot should not be accessed yet */
 
 ulBytestoSend=((tdstNetMessage *) pData)->uwMessageSizeInBytes + sizeof(tdstNetMessage);
 
 /* connected channel: write the data to the connected socket */
 if((ulBytesSent=gs_p_stTCPWinSocDesc->m_pfn_i_send(gs_a_stL0PCWin95TCPChannels[uwChannel].uiSD, pData, ulBytestoSend, 0))==SOCKET_ERROR)
 {
  iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError();
  if(iErrorCode==WSAECONNRESET)
  { /* the connection has been closed by the remote side */
#if defined(NET_USE_DEBUG)
   vDebugS(Net_C_Debug_TCP,"Send data :connection reset");
#endif
   vL0PCWin95TCPCloseChannel(uwChannel);
   return NetLib_E_es_ChannelIsInvalid;
  }
  if(iErrorCode==WSAEWOULDBLOCK)
  { /* the system is blocking */
#if defined(NET_USE_DEBUG)
   vDebugS(Net_C_Debug_TCP,"Send data :would block");
#endif
   return NetLib_E_es_EmissionInProgress;
  }
#if defined(NET_USE_DEBUG)
   vDebugSI(Net_C_Debug_TCP,"Send data :socket failure, error",iErrorCode);
#endif
  return NetLib_E_es_SendSocketFailure;
 }
 
 if(ulBytesSent!=ulBytestoSend)
 {
 
#if defined(NET_USE_DEBUG)
  vDebugSISI(Net_C_Debug_TCP,"Send data :partly sent BytesSent",ulBytesSent,"BytestoSend", ulBytestoSend);
#endif
 
  return NetLib_E_es_MessagePartlySent;
 }
 
 /* if the routine succeeded in sending the message, then it's its duty to free the associated cell */
 vFree(pData);
 return NetLib_E_es_NoError;
}
 
/*****************************************************************************
 *
 * Description: eL0PCWin95TCPQueryChannelStatus
 *
 * returns the current status of the channel
 *
 *****************************************************************************
 *
 * Input:   uwChannel, channel to query
 *
 * Output:  an error condition.
 *
 *****************************************************************************
 * Creation Date: April 16, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date: October 29, 1996               Author: Christophe ROGUET
 * the IsReadable flag does not exist any more
 ****************************************************************************/
NetLib_tdeErrorStatus eL0PCWin95TCPQueryChannelStatus(tduwNetChannel uwChannel)
{
 /* verify the validity of the Channel */
 int iErrorCode;
 int iParameterSize;
 
 if(uwChannel>=C_ucL0MaxNumberOfTCPChannels)
  return NetLib_E_es_InvalidChannel; /* the channel does not exist in the table */
 
 if(!gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsSlotInUse)
  return NetLib_E_es_ChannelUninitialized;   /* the slot is not initialized for use */
 
 if(!gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsConnected)
  /* is it worth a new error code ? */
  return NetLib_E_es_ChannelUninitialized;   /* the slot should not be accessed yet */
 
 /* what else to report ???? */
 iParameterSize=sizeof iErrorCode;
 
 if
  (
   gs_p_stTCPWinSocDesc->m_pfn_i_getsockopt
   (
    gs_a_stL0PCWin95TCPChannels[uwChannel].uiSD,SOL_SOCKET,
    SO_ERROR,(char *)&iErrorCode, &iParameterSize
   )==SOCKET_ERROR
  )
 {
  iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError();
  return NetLib_E_es_UnknownError;
 }
 
#if defined(NET_USE_DEBUG)
 if(iErrorCode!=0)
 {
  vDebugSISISI(Net_C_Debug_TCP,"QueryChannelStatus(): on channel",uwChannel,
               "socket",gs_a_stL0PCWin95TCPChannels[uwChannel].uiSD,"error",iErrorCode);
 }
#endif
 switch(iErrorCode)
 {
  case 0:
   return NetLib_E_es_NoError;
 
  case WSAECONNRESET: /* the connection has been closed by the remote end */
   vL0PCWin95TCPCloseChannel(uwChannel);
   return NetLib_E_es_ChannelIsInvalid;
 
  default:
   return NetLib_E_es_UnknownError;
 }
}
 
void vL0PCWin95TCPGSConnectEngine();
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : void eL0PCWin95TCPNetEngine(void)
 The level 0 NetEngine function for TCP connections
////////////////////////////////////////////////////////////////////////////////
Input :
None
////////////////////////////////////////////////////////////////////////////////
Output :
NetLib_E_es_NoError
An NetLib_tdeErrorStatus if an error occured
////////////////////////////////////////////////////////////////////////////////
Creation date : May 7, 96
Author : Christophe Roguet
 
modification log:
Date: October 25, 1996                      Author: Christophe ROGUET
modified the exception detection
////////////////////////////////////////////////////////////////////////////////
*/
void vL0PCWin95TCPNetEngine(void)
{
 static fd_set stReadFD, stWriteFD, stExceptionFD;
 static struct timeval stTimeOut = { 0, 0 };
 tduwNetChannel uwChannel;                  /* channel number attributed to new connections */
 int iErrorCode;
 SOCKET uiSD;
 int iAddrLength;                          /* getpeername() needs a variable argument */
 int iOptLen;
 
 /* is there any connection operation waiting to be serviced ? */
 /* build an fd-set to test: */
 
 FD_ZERO(&stReadFD);
 FD_ZERO(&stWriteFD);
 FD_ZERO(&stExceptionFD);
 
 /*  ability to read on our listening socket */
 FD_SET(gs_uiListenSD, &stReadFD);
 
 /*      ability to write on sockets for which a connect() call has been issued
         and presence of an exception for the same sockets and the sockets already used
 */
 
 for(uwChannel=0; uwChannel<C_ucL0MaxNumberOfTCPChannels; uwChannel++)
  if(gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsSlotInUse) {
    FD_SET(gs_a_stL0PCWin95TCPChannels[uwChannel].uiSD, &stExceptionFD);
    if(!gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsConnected)
      FD_SET(gs_a_stL0PCWin95TCPChannels[uwChannel].uiSD, &stWriteFD);
  }
 
 /* call select() */
 if(gs_p_stTCPWinSocDesc->m_pfn_i_select(0, &stReadFD, &stWriteFD, &stExceptionFD, &stTimeOut)==SOCKET_ERROR)
 {
  gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError();
  return; /* an error code ??? */
 }
 
 /* service connection requests: */
 /* accept() on our listening socket until there are no more pending requests */
 if(DLL_FD_ISSET(gs_uiListenSD, &stReadFD))
  do
  {
   uiSD=gs_p_stTCPWinSocDesc->m_pfn_ui_accept(gs_uiListenSD,NULL,NULL);
   if(uiSD==INVALID_SOCKET)
   {
    iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError();
    /* no more pending connections ? */
    if(iErrorCode==WSAEWOULDBLOCK)
     break;
    gs_p_stTCPWinSocDesc->m_pfn_i_closesocket(uiSD);
    return; /* an error code ??? */
   }
#if defined(NET_USE_DEBUG)
  vDebugSI(Net_C_Debug_TCP,"accept()",uiSD);
#endif
   
   /* got a new connection from a remote player: allocate a new channel */
   uwChannel=uwL0PCWin95TCPOpenChannel(uiSD);
   if(uwChannel==C_uwNetInvalidChannel)
   {
    gs_p_stTCPWinSocDesc->m_pfn_i_closesocket(uiSD);
    return; /* return an error code ? */
   }
   /* set the channel's connection flag */
   gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsConnected=1;
   gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsBroadcast=1;
   /* set the channel's remote address */
    iAddrLength=sizeof(gs_a_stL0PCWin95TCPChannels[uwChannel].stRemoteAddr);
    if(gs_p_stTCPWinSocDesc->m_pfn_i_getpeername(uiSD,
     (struct sockaddr *)&gs_a_stL0PCWin95TCPChannels[uwChannel].stRemoteAddr,
     &iAddrLength)==SOCKET_ERROR) {
        iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError();
        /* if an error occured, simply close the channel .... */
        vL0PCWin95TCPCloseChannel(uwChannel);
    }
 
  } while(1);
 
 /* service connection completion: */
 /* mark the channels that are ready to be used */
 /* re- attempt to connect the channels whose connection has failed */
 for(uwChannel=0; uwChannel<C_ucL0MaxNumberOfTCPChannels; uwChannel++) {
   if(gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsSlotInUse) {
     if(!gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsConnected) {
       if(DLL_FD_ISSET(gs_a_stL0PCWin95TCPChannels[uwChannel].uiSD, &stWriteFD)) {
         /* connection completion */
         gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsConnected=1;
#if defined(NET_USE_DEBUG)
         vDebugSIS(Net_C_Debug_TCP,"channel",uwChannel,"connected !");
#endif
       }
     }
     if(DLL_FD_ISSET(gs_a_stL0PCWin95TCPChannels[uwChannel].uiSD, &stExceptionFD)) {
       /* connection failure or connection reset */
       /* retrieve the error code */
       iOptLen = sizeof(iErrorCode);
       if(gs_p_stTCPWinSocDesc->m_pfn_i_getsockopt(gs_a_stL0PCWin95TCPChannels[uwChannel].uiSD,
                              SOL_SOCKET, SO_ERROR,
                              (char *)&iErrorCode, &iOptLen) == SOCKET_ERROR) {
         iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError();
         continue;
       }
  #if defined(NET_USE_DEBUG)
       vDebugSISISI(Net_C_Debug_TCP,"exception on channel",uwChannel,
                 "socket",gs_a_stL0PCWin95TCPChannels[uwChannel].uiSD,"error",iErrorCode);
  #endif
     
      /* release the corresponding channel */
      /* vL0PCWin95TCPCloseChannel(uwChannel); */
 
      /* attempt a new connection */
      /* connect the socket to the remote host */
      if(gs_p_stTCPWinSocDesc->m_pfn_i_connect(gs_a_stL0PCWin95TCPChannels[uwChannel].uiSD,
            (struct sockaddr *)&gs_a_stL0PCWin95TCPChannels[uwChannel].stRemoteAddr,
            sizeof(gs_a_stL0PCWin95TCPChannels[uwChannel].stRemoteAddr))==SOCKET_ERROR)
      {
       iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError();
       if(iErrorCode!=WSAEWOULDBLOCK) {
            vL0PCWin95TCPCloseChannel(uwChannel);
       }
      }
 
     }
   }
 }
 
 vL0PCWin95TCPGSConnectEngine();
}
 
/*****************************************************************************
 *
 * Description: eL0PCWin95TCPInit
 *
 * loads the sockets dll
 *
 *****************************************************************************
 *
 * Input:
 *
 * Output:  an error code
 *
 *****************************************************************************
 * Creation Date: April 24, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 *
 ****************************************************************************/
 
NetLib_tdeErrorStatus eL0PCWin95TCPInit(void)
{
 char ac80HostName[80];          /* the name of the computer the application is running on */
 struct hostent *hp;                     /* the list of IP¨adresses associated to the host */
 
 if(!(gs_bL0PCWin95TCPInitState&0x01))
   return NetLib_E_es_ProtocolNotInitialized;
 
 /* find the host address, set the corresponding global variable */
 
 if(gs_p_stTCPWinSocDesc->m_pfn_i_gethostname(ac80HostName, sizeof ac80HostName)==SOCKET_ERROR)
  return NetLib_E_es_InitialisationSocketError;
 
 if((hp=gs_p_stTCPWinSocDesc->m_pfn_pst_gethostbyname(ac80HostName))==NULL)
  return NetLib_E_es_InitialisationSocketError;
 
 gs_stMyAddress=*(struct in_addr *)(hp->h_addr_list[0]);
 gs_bL0PCWin95TCPInitState = gs_bL0PCWin95TCPInitState |(char)0x02;
 return NetLib_E_es_NoError;
}
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : tdeErrorStatus eL0PCWin95TCPAddPort
Add a new port
////////////////////////////////////////////////////////////////////////////////
Input :
port number to use
////////////////////////////////////////////////////////////////////////////////
Output :
a tdeErrorStatus
////////////////////////////////////////////////////////////////////////////////
Creation date : May 6,96
Author : Albert Pais
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95TCPAddPort(unsigned short uwPortNumber)
{
 return eL0PCWin95DirectTCPAddPort(uwPortNumber, INADDR_NONE);
}
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : NetLib_tdeErrorStatus eL0PCWin95TCPAddPortSym
Add a new port
////////////////////////////////////////////////////////////////////////////////
Input :
port number to use
remote IP address to send 'broadcast' messages to
////////////////////////////////////////////////////////////////////////////////
Output :
a NetLib_tdeErrorStatus
////////////////////////////////////////////////////////////////////////////////
Creation date : May 6,96
Author : Albert Pais
 
modification log:
Date: June 6, 1996                      Author: Christophe ROGUET
added the connect address parameter
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95DirectTCPAddPortSym(unsigned short uwPortNumber, char *ConnectAddress)
{
    unsigned long NumAddr;
 
    /* WinSock doesn't have inet_aton(), we have to use inet_addr() */
 
    if((NumAddr=gs_p_stTCPWinSocDesc->m_pfn_ul_inet_addr(ConnectAddress))!=INADDR_NONE)
        return eL0PCWin95DirectTCPAddPort(uwPortNumber, gs_p_stTCPWinSocDesc->m_pfn_ul_ntohl(NumAddr));
 
    return NetLib_E_es_InitialisationSocketError;
}
/*
////////////////////////////////////////////////////////////////////////////////
Description : NetLib_tdeErrorStatus eL0PCWin95TCPReconnect
try to connect to an address to have a direct route to a player
////////////////////////////////////////////////////////////////////////////////
Input :
port number to use
remote IP address to send 'broadcast' messages to
////////////////////////////////////////////////////////////////////////////////
Output :
a NetLib_tdeErrorStatus
////////////////////////////////////////////////////////////////////////////////
Creation date : January 24, 1997
Author : Christophe Roguet
 
modification log:
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95TCPReconnect(unsigned short uwPortNumber, long lConnectAddress)
{
    tduwNetChannel uwCurrentChannel;
 
    /* if we already have a channel for this target address, return  */
    for(uwCurrentChannel=0; uwCurrentChannel<C_ucL0MaxNumberOfTCPChannels; uwCurrentChannel++)
        if(   gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsSlotInUse==1
           && gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].stRemoteAddr.sin_addr.s_addr==
                                    gs_p_stTCPWinSocDesc->m_pfn_ul_ntohl(lConnectAddress))
            return NetLib_E_es_NoError;
 
    /* initiate an asynchronous connection to the target address */
    return eL0PCWin95TCPChannelConnect(uwPortNumber, lConnectAddress, eReconnect, (tduwNetChannel *)C_pNull);
}
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : NetLib_tdeErrorStatus eL0PCWin95DirectTCPAddPort
Add a new port
////////////////////////////////////////////////////////////////////////////////
Input :
port number to use
IP address of remote host to connect to
////////////////////////////////////////////////////////////////////////////////
Output :
a NetLib_tdeErrorStatus
////////////////////////////////////////////////////////////////////////////////
Creation date :June 6, 1996                      Author: Christophe ROGUET
Modification log:
Date:                                Author:
October 29, 1996                     Christophe Roguet
no more UDP socket at channel 0; attempts to connect to the eventual remote host
January 24, 1997                     Christophe Roguet
connect part moved to a new function: eL0PCWin95TCPChannelConnect
February 14, 1997                     Christophe Roguet
close the socket descriptor when an error occurs
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95DirectTCPAddPort(unsigned short uwPortNumber, long lConnectAddress)
{
 struct sockaddr_in my_name;    /* the address to bind the socket to */
 NetLib_tdeErrorStatus eErrorCode;
 
 if(uwPortNumber<IPPORT_RESERVED)
  return NetLib_E_es_InvalidPortNumber;
 
 if(!(gs_bL0PCWin95TCPInitState&0x01))
  return NetLib_E_es_ProtocolNotInitialized;
 
 if(!(gs_bL0PCWin95TCPInitState&0x02))
   eL0PCWin95TCPInit();
 
 if(!(gs_bL0PCWin95TCPInitState&0x02))
  return NetLib_E_es_InitialisationSocketError;
 
 /* open the IP stream socket used for listening */
 
 gs_uiListenSD=gs_p_stTCPWinSocDesc->m_pfn_ui_socket(PF_INET, SOCK_STREAM, 0);
 if(gs_uiListenSD==INVALID_SOCKET)
  return NetLib_E_es_InitialisationSocketError;
 
 if(eL0PCWin95TCPConfigureSocket(gs_uiListenSD)!=NetLib_E_es_NoError)
    eErrorCode = NetLib_E_es_InitialisationSocketError;
 else {
     /* bind this socket to our specific port */
     my_name.sin_family = AF_INET;
     my_name.sin_port = gs_p_stTCPWinSocDesc->m_pfn_uw_htons(uwPortNumber);
     my_name.sin_addr = gs_stMyAddress;
 
     if(gs_p_stTCPWinSocDesc->m_pfn_i_bind(gs_uiListenSD, (struct sockaddr *)&my_name, sizeof my_name)<0)
         eErrorCode = NetLib_E_es_InitialisationSocketError;
     else {
         /* start listening on this socket */
 
         if(gs_p_stTCPWinSocDesc->m_pfn_i_listen(gs_uiListenSD, 5)==SOCKET_ERROR) /* 5 is the maximum backlog supported by Windobe */
          eErrorCode = NetLib_E_es_InitialisationSocketError;
         else {
             eErrorCode=NetLib_E_es_NoError;
 
             /* if the remote address is a valid address, initiate handshake with the remote end */
 
             if(lConnectAddress!=INADDR_NONE) {
                eErrorCode=eL0PCWin95TCPChannelConnect(uwPortNumber, lConnectAddress, eFirstConnect, (tduwNetChannel *)C_pNull);
             }
         }
     }
 }
 
 if(eErrorCode!=NetLib_E_es_NoError) {
    gs_p_stTCPWinSocDesc->m_pfn_i_closesocket(gs_uiListenSD);
    gs_uiListenSD=INVALID_SOCKET;
 } else  gs_bL0PCWin95TCPInitState = gs_bL0PCWin95TCPInitState |(char)0x04;
 
 return eErrorCode;
}
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : NetLib_tdeErrorStatus eL0PCWin95TCPChannelConnect
open a new channel and initiate an asynchronous connection to a target address
////////////////////////////////////////////////////////////////////////////////
Input :
port number to use
IP address of remote host to connect to
connection mode : either eFirstConnect or eReconnect
puwChannel : (output parameter) pointer to a variable that can hold the chosen channel number; can be C_pNull
////////////////////////////////////////////////////////////////////////////////
Output :
a NetLib_tdeErrorStatus
////////////////////////////////////////////////////////////////////////////////
Creation date :January 24, 1997                      Author: Christophe ROGUET
(code moved from TCPDIrectConnect)
Modification log:
////////////////////////////////////////////////////////////////////////////////
*/
 
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95TCPChannelConnect(unsigned short uwPortNumber, long ConnectAddress, int iMode, tduwNetChannel *puwChannel)
 
{
 int iErrorCode;
 tduwNetChannel uwChannel;      /* channel number for the connection attempt */
 SOCKET uiNewSD;                /* socket descriptor for the connection attempt */
 
  /* open a new socket */
  uiNewSD=gs_p_stTCPWinSocDesc->m_pfn_ui_socket(PF_INET, SOCK_STREAM, 0);
  if(uiNewSD==INVALID_SOCKET)
   return NetLib_E_es_InitialisationSocketError;
 
  /* set options on the socket */
  if(eL0PCWin95TCPConfigureSocket(uiNewSD)!=NetLib_E_es_NoError) {
    gs_p_stTCPWinSocDesc->m_pfn_i_closesocket(uiNewSD);
    return NetLib_E_es_InitialisationSocketError;
  }
  /* allocate a new channel */
  if((uwChannel=uwL0PCWin95TCPOpenChannel(uiNewSD))==C_uwNetInvalidChannel) {
    gs_p_stTCPWinSocDesc->m_pfn_i_closesocket(uiNewSD);
    return NetLib_E_es_InitialisationSocketError;
  }
 
  /* if the caller wants to know the new channel number, tell him */
  
  if(puwChannel!=(tduwNetChannel *)C_pNull)
    *puwChannel=uwChannel;
    
  /* mark the channel as 'broadcast' */
  
  gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsBroadcast=1;
 
  /* store the target address in the channel */
  
  gs_a_stL0PCWin95TCPChannels[uwChannel].stRemoteAddr.sin_family = AF_INET;
  gs_a_stL0PCWin95TCPChannels[uwChannel].stRemoteAddr.sin_port=gs_p_stTCPWinSocDesc->m_pfn_uw_htons(uwPortNumber);
  gs_a_stL0PCWin95TCPChannels[uwChannel].stRemoteAddr.sin_addr.s_addr=gs_p_stTCPWinSocDesc->m_pfn_ul_ntohl(ConnectAddress);
 
#if defined(NET_USE_DEBUG)
  vDebugS(Net_C_Debug_TCP,"connect()");
#endif
  /* connect the socket to the remote host */
  if(gs_p_stTCPWinSocDesc->m_pfn_i_connect(uiNewSD,
        (struct sockaddr *)&gs_a_stL0PCWin95TCPChannels[uwChannel].stRemoteAddr,
        sizeof(gs_a_stL0PCWin95TCPChannels[uwChannel].stRemoteAddr))==SOCKET_ERROR)
  {
   iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError();
   if(iErrorCode!=WSAEWOULDBLOCK) {
        vL0PCWin95TCPCloseChannel(uwChannel);
        return NetLib_E_es_InitialisationSocketError;
   }
  }
 
  /* differenciate between first connection (which will eventually result in a new broadcast channel) */
  /* and re-connection (which will eventually result in a new 'reconnection' channel) */
  
  switch(iMode) {
      case eFirstConnect:
          /* this is a channel usable to send only broadcast messages */
          gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsBroadcast = 1;
          gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsReconnect = 0;
          break;
          
      case eReconnect:
          gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsBroadcast = 0;
          gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsReconnect = 1;
          break;
  }
 
  /* the channel is working properly */
  gs_a_stL0PCWin95TCPChannels[uwChannel].eChannelStatus = E_ts_OK;
 
  return NetLib_E_es_NoError;    
}
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : vL0PCWin95TCPCloseProtocol(void)
TCP-Level 0 closing function
////////////////////////////////////////////////////////////////////////////////
Input :
none
////////////////////////////////////////////////////////////////////////////////
Output :
none
////////////////////////////////////////////////////////////////////////////////
Creation date : May 6,96
Author : Albert Pais
////////////////////////////////////////////////////////////////////////////////
*/
 
void vL0PCWin95TCPCloseProtocol(void)
{
 tduwNetChannel uwChannel;
 
 /* close the listening socket */
 gs_p_stTCPWinSocDesc->m_pfn_i_closesocket(gs_uiListenSD);
 /* close all channel sockets still open */
 for(uwChannel=0; uwChannel<C_ucL0MaxNumberOfTCPChannels; uwChannel++)
  if(gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsSlotInUse
     && gs_a_stL0PCWin95TCPChannels[uwChannel].ubf1IsConnected)
    gs_p_stTCPWinSocDesc->m_pfn_i_closesocket(gs_a_stL0PCWin95TCPChannels[uwChannel].uiSD);
 
 eNetRestoreWinSock();
 gs_cIsWinsocDescInit=0;
 
 vLevel1RemoveProtocol(E_Net_pr_Windows95TCPProtocol);
 
#if defined(NET_USE_DEBUG)
  vDebugS(Net_C_Debug_TCP,"\n\nBye-bye\n");
#endif
 
}
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : eL0PCWin95TCPIsProtocolSet
Check if the protocol is avaible for use or not
////////////////////////////////////////////////////////////////////////////////
Input :
none
////////////////////////////////////////////////////////////////////////////////
Output :
NetLib_E_es_ProtocolNotInitialized : the vLevel0SetupWin95PCTCPInterface has not
been called yet or it has failed, the application should call the level 2
eInitializeGlobalData function.
NetLib_E_es_InitialisationSocketError : the eLevel0InitWin95PCTCP function has not been
called yet or it has failed, the application should call the level 2
eInitializeGlobalData function
NetLib_E_es_NoPortSelected : the eL0PCWin95TCPAddPort has not been called yet. The
protocol has been correctly initialised, it can be used, but it won't give any
results since no port is avaible.  The application should call it.
////////////////////////////////////////////////////////////////////////////////
Creation date : June 4,96
Author : Albert Pais
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95TCPIsProtocolSet(void)
{
 if(!(gs_bL0PCWin95TCPInitState&0x01))
  return NetLib_E_es_ProtocolNotInitialized;
 if(!(gs_bL0PCWin95TCPInitState&0x02))
  return NetLib_E_es_InitialisationSocketError;
 if(!(gs_bL0PCWin95TCPInitState&0x04))
  return NetLib_E_es_NoPortSelected;
 return NetLib_E_es_NoError;
}
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : eL0PCWin95TCPIsPortAvailable
Check if the port is avaible for use or not
////////////////////////////////////////////////////////////////////////////////
Input :
A port number to test
////////////////////////////////////////////////////////////////////////////////
Output :
NetLib_E_es_True if the port is avaible
NetLib_E_es_PortAlreayUsed if the port is already in use
An error code otherwise
////////////////////////////////////////////////////////////////////////////////
Creation date : June 26,96
Author : Albert Pais
Modification log:
Date: October 29, 1996                    Author: Christophe ROGUET
no more UDP socket
Date: October 29, 1996                    Author: Christophe ROGUET
close the socket descriptor even if an error occurs before binding
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95TCPIsPortAvailable(unsigned short uwPortNumber)
{
 struct sockaddr_in my_name; /* the address to bind the socket to */
 SOCKET uiTempSock;
 NetLib_tdeErrorStatus tdeErrorCode;
 
 /* bind to the DLL if necessary */
 if(!gs_cIsWinsocDescInit) {
	 if((tdeErrorCode=eNetGetWinSockDesc(&gs_p_stTCPWinSocDesc))!=NetLib_E_es_NoError)
		 return NetLib_E_es_InitialisationSocketError;
	 gs_cIsWinsocDescInit=~0;
 }
 
 if(uwPortNumber<IPPORT_RESERVED) return NetLib_E_es_InvalidPortNumber;
 
 /* try to bind a stream TCP socket on the port */
 
 uiTempSock=gs_p_stTCPWinSocDesc->m_pfn_ui_socket(PF_INET, SOCK_STREAM, 0);
 
 if(uiTempSock==INVALID_SOCKET)
  return  NetLib_E_es_InitialisationSocketError;
  
 if(eL0PCWin95TCPConfigureSocket(uiTempSock)!=NetLib_E_es_NoError)
  tdeErrorCode = NetLib_E_es_InitialisationSocketError;
 else {
     my_name.sin_family = AF_INET;
     my_name.sin_port = gs_p_stTCPWinSocDesc->m_pfn_uw_htons(uwPortNumber);
     my_name.sin_addr = gs_stMyAddress;
 
     if(gs_p_stTCPWinSocDesc->m_pfn_i_bind(uiTempSock, (struct sockaddr *)&my_name, sizeof(my_name))<0)
      tdeErrorCode = NetLib_E_es_InitialisationSocketError;
 
     tdeErrorCode = NetLib_E_es_True;
 }
 
 gs_p_stTCPWinSocDesc->m_pfn_i_closesocket(uiTempSock);
 
 return tdeErrorCode;
}
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : eL0PCWin95TCPIsProtocolAvailable
Check if the protocol is avaible for use or not
////////////////////////////////////////////////////////////////////////////////
Input :
None
////////////////////////////////////////////////////////////////////////////////
Output :
NetLib_E_es_True if the port is avaible
An error code otherwise
////////////////////////////////////////////////////////////////////////////////
Creation date : June 26,96
Author : Albert Pais
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95TCPIsProtocolAvailable(void)
{
 unsigned short uwPortNumberTest;
 NetLib_tdeErrorStatus eErrorReturned;
 
 /* bind to the DLL if necessary */
 if(!gs_cIsWinsocDescInit) {
	 if((eErrorReturned=eNetGetWinSockDesc(&gs_p_stTCPWinSocDesc))!=NetLib_E_es_NoError)
		 return NetLib_E_es_InitialisationSocketError;
	 gs_cIsWinsocDescInit=~0;
 }
 
 uwPortNumberTest =  IPPORT_RESERVED+1000;
 while
  (
  ((eErrorReturned = eL0PCWin95TCPIsPortAvailable(uwPortNumberTest))==NetLib_E_es_PortAlreadyUsed)&&
  (uwPortNumberTest<IPPORT_RESERVED+1100)
  )
  uwPortNumberTest++;
 return eErrorReturned;
}
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : vL0PCWin95TCPClosePort
Close the socket bound with the specified port number
////////////////////////////////////////////////////////////////////////////////
Input :
The port number to close
////////////////////////////////////////////////////////////////////////////////
Output :
None
////////////////////////////////////////////////////////////////////////////////
Creation date : June 26,96
Author : Albert Pais
Modification log:
Date: October 29, 1996                    Author: Christophe ROGUET
no more UDP socket at channel 0; close all used channels as well
////////////////////////////////////////////////////////////////////////////////
*/
 
void _NET_CALLING_CONV_ vL0PCWin95TCPClosePort(unsigned short uwOldPort)
{
 struct sockaddr_in ListenSockName; /* the address that the listen socket is bound to */
 int iAddrLen;
 int iErrorCode;
 tduwNetChannel uwCurrentChannel;
 
 /* first verify that this port is used ... */
 
 if(!(gs_bL0PCWin95TCPInitState&0x04))
  return;
 
 iAddrLen=sizeof(ListenSockName);
 
 if(gs_p_stTCPWinSocDesc->m_pfn_i_getsockname(gs_uiListenSD, (struct sockaddr *)&ListenSockName, &iAddrLen)==SOCKET_ERROR) {
  iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError();
  return;
 }
 
 if(!(uwOldPort == gs_p_stTCPWinSocDesc->m_pfn_uw_ntohs(ListenSockName.sin_port)))
  return;
  
 /* close the socket used for listening */
 gs_p_stTCPWinSocDesc->m_pfn_i_closesocket(gs_uiListenSD);
 gs_uiListenSD = INVALID_SOCKET;
 
 /* close all used channels */
 
 for(uwCurrentChannel=0; uwCurrentChannel<C_ucL0MaxNumberOfTCPChannels; uwCurrentChannel++)
  if(gs_a_stL0PCWin95TCPChannels[uwCurrentChannel].ubf1IsSlotInUse==1)
   vL0PCWin95TCPCloseChannel(uwCurrentChannel);
 
 gs_bL0PCWin95TCPInitState = gs_bL0PCWin95TCPInitState& (char)~0x04;
 
}
/*
////////////////////////////////////////////////////////////////////////////////
Description : eL0PCWin95TCPIsPortOpened
Returns wether the port is opened or not
////////////////////////////////////////////////////////////////////////////////
Input :
A port
////////////////////////////////////////////////////////////////////////////////
Output :
an tdeErrorStatus
////////////////////////////////////////////////////////////////////////////////
Creation date : June 27,96
Author : Albert Pais
 Modification log:
 
 Date: October 29, 1996                    Author: Christophe ROGUET
 test on the listen socket since the UDP socket doesn't exist any more
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95TCPIsPortOpened(unsigned short uwPortNumber)
{
 struct sockaddr_in ListenSockName; /* the address that the listen socket is bound to */
 int iAddrLen;
 int iErrorCode;
 
 /* test whether the listen socket is valid and has the right port number */
 
 if(gs_uiListenSD==INVALID_SOCKET)
    return NetLib_E_es_False;
    
 if(!(gs_bL0PCWin95TCPInitState&0x04))
  return NetLib_E_es_NoPortSelected;
 
 iAddrLen=sizeof(ListenSockName);
 
 if(gs_p_stTCPWinSocDesc->m_pfn_i_getsockname(gs_uiListenSD, (struct sockaddr *)&ListenSockName, &iAddrLen)==SOCKET_ERROR) {
  iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError();
  return NetLib_E_es_UnknownError;
 }
 
 if(!(uwPortNumber == gs_p_stTCPWinSocDesc->m_pfn_uw_ntohs(ListenSockName.sin_port)))
  return NetLib_E_es_False;
 return NetLib_E_es_True;
}
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : eL0PCWin95TCPGetMyAddress
copy into a character string the numerical IP address which we are bound to
////////////////////////////////////////////////////////////////////////////////
Input :
array of characters to store an alphanumeric name into
size of the array
////////////////////////////////////////////////////////////////////////////////
Output :
NetLib_E_es_ProtocolNotInitialized : the vLevel0SetupWin95PCTCPInterface has not
been called yet or it has failed, the application should call the level 2
eInitializeGlobalData function.
NetLib_E_es_InitialisationSocketError : the eLevel0InitWin95PCTCP function has not been
called yet or it has failed, the application should call the level 2
eInitializeGlobalData function
////////////////////////////////////////////////////////////////////////////////
Creation date : July 4,96
Author : Christophe Roguet
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95TCPGetMyAddress(char *pcReturnStringAddress, unsigned short Size)
{
    char LocalStringAddress[80];
 
    if(!(gs_bL0PCWin95TCPInitState&0x01))
        return NetLib_E_es_ProtocolNotInitialized;
 
 if(!(gs_bL0PCWin95TCPInitState&0x02))
   eL0PCWin95TCPInit();
 
    if(!(gs_bL0PCWin95TCPInitState&0x02))
        return NetLib_E_es_InitialisationSocketError ;
 
    /* translate the numerical address into an alphabetic string */
 
    strncpy(LocalStringAddress, gs_p_stTCPWinSocDesc->m_pfn_pc_inet_ntoa(gs_stMyAddress), 80);
 
    /* if the address string is too long to fit in the caller's array, return an error value */
 
    if(strlen(LocalStringAddress)+1>Size)
    {
        memset(pcReturnStringAddress, '\0', Size);
        return NetLib_E_es_UnknownError;
    }
 
    strcpy(pcReturnStringAddress, LocalStringAddress);
 
    return NetLib_E_es_NoError;
}
 
NetLib_tdeErrorStatus eL0PCWin95TCPGetPortAndAddress(unsigned short *p_uwPort, unsigned long *p_ulAddress)
{
    struct sockaddr_in ListenSockName; /* the address that the listen socket is bound to */
    int iAddrLen;
    int iErrorCode;
    
    if(!(gs_bL0PCWin95TCPInitState&0x01))
        return NetLib_E_es_ProtocolNotInitialized;
 
    if(!(gs_bL0PCWin95TCPInitState&0x02))
        eL0PCWin95TCPInit();
 
    if(!(gs_bL0PCWin95TCPInitState&0x02))
        return NetLib_E_es_InitialisationSocketError ;
 
    /* copy the requested information into the return parameters */
 
    /* get the port number from the listen socket */
    
    if(gs_uiListenSD==INVALID_SOCKET) {
        *p_uwPort=0; /* if we are not bound, return 0 as port number */
    } else {
        iAddrLen=sizeof(ListenSockName);
 
        if(gs_p_stTCPWinSocDesc->m_pfn_i_getsockname(gs_uiListenSD, (struct sockaddr *)&ListenSockName, &iAddrLen)==SOCKET_ERROR) {
            iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError();
            return NetLib_E_es_UnknownError;
        }
 
        *p_uwPort=gs_p_stTCPWinSocDesc->m_pfn_uw_ntohs(ListenSockName.sin_port);
    }
    
    /* get the IP address from the global variable */
    *p_ulAddress=gs_p_stTCPWinSocDesc->m_pfn_ul_ntohl(gs_stMyAddress.s_addr);
 
    return NetLib_E_es_NoError;
}
 
/*
////////////////////////////////////////////////////////////////////////////////
Description : eL0PCWin95TCPConfigureSocket
set appropriate options on a socket
////////////////////////////////////////////////////////////////////////////////
Input :
uiSD the socket descriptor of the socket to configure
////////////////////////////////////////////////////////////////////////////////
Output :
NetLib_E_es_InitialisationSocketError if a system call returned an error
NetLib_E_es_NoError if the socket could be properly configured
////////////////////////////////////////////////////////////////////////////////
Creation date : October 29,96
Author : Christophe Roguet
////////////////////////////////////////////////////////////////////////////////
*/
NetLib_tdeErrorStatus eL0PCWin95TCPConfigureSocket(SOCKET uiSD)
 
{
 int iOpt;                      /* parameter used for socket option setting via setsockopt() */
 int iOptLen;                   /* length of option retrieved with getsockopt() */
 unsigned long ulOpt;           /* parameter used for socket option setting via ioctlsocket() */
 int iErrorCode;
 
 /* set non blocking mode on this socket */
 ulOpt=(unsigned long)~0L;
 if(gs_p_stTCPWinSocDesc->m_pfn_i_ioctlsocket(uiSD, FIONBIO, &ulOpt)==SOCKET_ERROR) {
  iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError();
  return NetLib_E_es_InitialisationSocketError;
 }
 
 /* disable the Nagle algorithm on this socket */
 /* (in order not to be blocked because a small message won't go out) */
 
 iOpt=~0;
 if(gs_p_stTCPWinSocDesc->m_pfn_i_setsockopt(uiSD, IPPROTO_TCP, TCP_NODELAY, (char *)&iOpt, sizeof(iOpt))<0)
 {
  iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError();
  return NetLib_E_es_InitialisationSocketError;
 }
 
 /* set the system buffer size for send operations */
 /* if it is too big, it will cause a lag because lots of messages will be stored in this buffer */
 
 iOpt=C_iSendBufferSize;
 iOptLen=sizeof(iOpt);
 
 if(gs_p_stTCPWinSocDesc->m_pfn_i_setsockopt(uiSD, SOL_SOCKET, SO_SNDBUF, (char *)&iOpt, iOptLen)<0)
 {
  iErrorCode=gs_p_stTCPWinSocDesc->m_pfn_i_WSAGetLastError();
  return NetLib_E_es_InitialisationSocketError;
 }
  return NetLib_E_es_NoError;
}
 
 
/*****************************************************************************/
/*          functions related to gameservice direct reconnection             */ 
/*****************************************************************************/
 
/* 5 s to perform all connections */
 
#define GS_CONNECTION_TIMEOUT 5000
 
typedef struct _stTargetCell {
    unsigned short uwPortNumber;          /* port number to reconnect to          */
    unsigned long ulTargetIPAddr;         /* IP address to reconnect to           */
    tduwNetChannel uwChannel;             /* channel associated to this target    */
    char cRouteKnown;                     /* flag: level 1 knows this route */
    struct _stTargetCell *pstNextTarget;  /* link to next cell in list            */
} tdstTargetCell;
 
unsigned short gs_pstL0PCWin95TCP_GS_ListenPort=0;                          /* socket number to listen on */
tdstTargetCell *gs_pstL0PCWin95TCPTargetsList=(tdstTargetCell *)C_pNull;    /* list of target addresses to reconnect to  */
unsigned char gs_ucL0PCWin95TCPConnecting=0;                                /* flag: are we currently in a connection process ? */
unsigned long gs_ulL0PCWin95TCPConnectionStartTime;                         /* time at which the connect process began */
 
static tdfn_vConnectionResult gs_pfnConnectionResult;    /* Call back for connection result. */ 
 
/*****************************************************************************/
/* start listening on a port */
 
void _NET_CALLING_CONV_ vL0PCWin95TCPGSSetCallBackResult(tdfn_vConnectionResult pfnConnectionResult)
{
  gs_pfnConnectionResult=pfnConnectionResult;
}
 
/*****************************************************************************/
/* start listening on a port */
 
void GS_FUNCTIONCALL vL0PCWin95TCPGSSetPLayer(unsigned short uwPlayerId, unsigned short uwPortNumber)
 
{
    /* the playerId argument is ignored ... */
#if defined(NET_USE_DEBUG)
  vDebugSISI(Net_C_Debug_TCP,"SetPlayer PlayerId",uwPlayerId,"Port",uwPortNumber);
#endif
 
    eL0PCWin95TCPAddPort(uwPortNumber);
    gs_pstL0PCWin95TCP_GS_ListenPort=uwPortNumber;
}
 
/*****************************************************************************/
/* add a new entry to the list of targets to reconnect to                    */
 
void GS_FUNCTIONCALL vL0PCWin95TCPGSSetChannel(unsigned short uwPortNumber, char *pcIPAddr)
 
{
    tdstTargetCell *pstTargetCell;     /* new list element for the new target */
    tdstTargetCell *pstCurrentCell;    /* pointer to run through the list     */
 
#if defined(NET_USE_DEBUG)
  vDebugSI(Net_C_Debug_TCP,"SetChannel Port",uwPortNumber);
  vDebugSS(Net_C_Debug_TCP,"Addr : ",pcIPAddr);
#endif
 
    /* do not begin to register a new list before the former has not been processed */
 
    if(gs_ucL0PCWin95TCPConnecting)
        return;
 
    /* allocate a new cell */
    
    pstTargetCell=(tdstTargetCell *)pMalloc(sizeof(tdstTargetCell));
 
    if(pstTargetCell==(tdstTargetCell *)C_pNull)
        return;                 /* not enough memory */
 
    /* fill the new cell */
 
    pstTargetCell->uwPortNumber=uwPortNumber;
    pstTargetCell->ulTargetIPAddr=
                    gs_p_stTCPWinSocDesc->m_pfn_ul_ntohl(gs_p_stTCPWinSocDesc->m_pfn_ul_inet_addr(pcIPAddr));
    pstTargetCell->uwChannel=C_uwNetInvalidChannel;
    pstTargetCell->cRouteKnown=0;
 
    /* insert the new cell at the tail of the list */
    
    if(gs_pstL0PCWin95TCPTargetsList==(tdstTargetCell *)C_pNull) {
        pstTargetCell->pstNextTarget=(tdstTargetCell *)C_pNull;
        gs_pstL0PCWin95TCPTargetsList=pstTargetCell;
    } else {
        pstTargetCell->pstNextTarget=(tdstTargetCell *)C_pNull;
 
        pstCurrentCell=gs_pstL0PCWin95TCPTargetsList;
 
        while(pstCurrentCell->pstNextTarget!=(tdstTargetCell *)C_pNull)
            pstCurrentCell=pstCurrentCell->pstNextTarget;
 
        pstCurrentCell->pstNextTarget=pstTargetCell;
    }
    
}
 
/*****************************************************************************/
/* start reconnecting to the targets */
 
void GS_FUNCTIONCALL vL0PCWin95TCPGSInitiateConnection(void)
 
{
    tdstTargetCell *pstCurrentCell;    /* pointer to run through the list     */
 
#if defined(NET_USE_DEBUG)
  vDebugS(Net_C_Debug_TCP,"InitiateConnection");
#endif
    /* record the start time */
    
    gs_ulL0PCWin95TCPConnectionStartTime=GetTickCount();
 
    /* for each entry in the list, allocate a channel and launch an asynchronous connection */
 
    pstCurrentCell=gs_pstL0PCWin95TCPTargetsList;
 
    while(pstCurrentCell!=(tdstTargetCell *)C_pNull) {
        eL0PCWin95TCPChannelConnect(pstCurrentCell->uwPortNumber, pstCurrentCell->ulTargetIPAddr,
                                    eReconnect, &pstCurrentCell->uwChannel);
        pstCurrentCell=pstCurrentCell->pstNextTarget;
    }
 
    /* set the connection flag */
 
    gs_ucL0PCWin95TCPConnecting=(unsigned char)~0;
}
 
/*****************************************************************************/
 
void _NET_CALLING_CONV_ vL0PCWin95TCPCloseListenPort()
 
{
#if defined(NET_USE_DEBUG)
  vDebugS(Net_C_Debug_TCP,"Close listen port");
#endif
    vL0PCWin95TCPClosePort(gs_pstL0PCWin95TCP_GS_ListenPort);
    gs_pstL0PCWin95TCP_GS_ListenPort=0;
}
 
/*****************************************************************************/
 
void vL0PCWin95TCPNewRouteKnown(tduwNetChannel uwChannel)
 
{
    tdstTargetCell *pstCurrentCell;    /* pointers to run through the list     */
 
#if defined(NET_USE_DEBUG)
  vDebugSI(Net_C_Debug_TCP,"route through channel known",uwChannel);
#endif
 
    pstCurrentCell=gs_pstL0PCWin95TCPTargetsList;
 
    while(pstCurrentCell!=(tdstTargetCell *)C_pNull) {
 
      if(pstCurrentCell->uwChannel==uwChannel) {
        pstCurrentCell->cRouteKnown=1;
        break;
      }
      pstCurrentCell=pstCurrentCell->pstNextTarget;
    }
}
 
/*****************************************************************************/
 
void vL0PCWin95TCPGSConnectEngine()
 
{
    char cCompleted;
    
    tdstTargetCell *pstCurrentCell, *pstNextCell;    /* pointers to run through the list     */
    
    if(gs_ucL0PCWin95TCPConnecting) {
     
        /* check for connection process timeout/completion */
 
        cCompleted=1;
        
        pstCurrentCell=gs_pstL0PCWin95TCPTargetsList;
 
        while(pstCurrentCell!=(tdstTargetCell *)C_pNull) {
 
            if(!gs_a_stL0PCWin95TCPChannels[pstCurrentCell->uwChannel].ubf1IsConnected) {
                cCompleted=0;
                break;
            }
 
      if(!pstCurrentCell->cRouteKnown) {
        cCompleted=0;
        break;
            }
 
            pstCurrentCell=pstCurrentCell->pstNextTarget;
        }
 
        if(cCompleted) { /* all connections succeeded */
            
            /* free the target list */
 
            pstCurrentCell=gs_pstL0PCWin95TCPTargetsList;
 
            while(pstCurrentCell!=(tdstTargetCell *)C_pNull) {
                pstNextCell=pstCurrentCell->pstNextTarget;
                vFree(pstCurrentCell);
                pstCurrentCell=pstNextCell;
            }
 
            gs_pstL0PCWin95TCPTargetsList=(tdstTargetCell *)C_pNull;
 
            /* alert the gameservice */
            
#if defined(NET_USE_DEBUG)
  vDebugS(Net_C_Debug_TCP,"Connection result 1");
#endif
            if (gs_pfnConnectionResult) gs_pfnConnectionResult(1);
            
            gs_ucL0PCWin95TCPConnecting=0;
            
        } else if(GetTickCount()-gs_ulL0PCWin95TCPConnectionStartTime>GS_CONNECTION_TIMEOUT) {
        
            /* time is over */
        
            /* close all channels and free the target list */
 
            pstCurrentCell=gs_pstL0PCWin95TCPTargetsList;
 
            while(pstCurrentCell!=(tdstTargetCell *)C_pNull) {
                pstNextCell=pstCurrentCell->pstNextTarget;
 
                vL0PCWin95TCPCloseChannel(pstCurrentCell->uwChannel);
                
                vFree(pstCurrentCell);
                pstCurrentCell=pstNextCell;
            }
 
            gs_pstL0PCWin95TCPTargetsList=(tdstTargetCell *)C_pNull;
 
            /* close the listen port */
 
            vL0PCWin95TCPCloseListenPort();
            
            /* alert the gameservice */
 
#if defined(NET_USE_DEBUG)
  vDebugS(Net_C_Debug_TCP,"Connection result 0");
#endif
            if (gs_pfnConnectionResult) gs_pfnConnectionResult(1);
            
            gs_ucL0PCWin95TCPConnecting=0;
         }
     }
}
 
/*****************************************************************************/
 
/*****************************************************************************
 *
 * Description: vL0PCWin95TCPOpenProtocol
 *
 * initialize the specified interface structure with the correct function
 * pointers to enable a protocol-independent data transmission process.
 *
 *****************************************************************************
 *
 * Input:   p_stProtocolInterface, pointer to the interface structure
 *
 * Output:  none
 *
 *****************************************************************************
 * Creation Date: April 24, 1996         Author: Christophe ROGUET
 *****************************************************************************
 *
 * Modification log:
 *
 * Date:                                Author:
 * October 29, 1996                     Christophe Roguet
 * the CloseChannel interface function is now null
 ****************************************************************************/
void _NET_CALLING_CONV_ vL0PCWin95TCPOpenProtocol(void)
{
 tduwNetChannel uwChannelToInit;
 tdstNetProtocolInterface *p_stProtocolInterface;
 tdstTCPReconnectInterface *p_stReconnectInterface;
 
 p_stReconnectInterface=pstNetGetTCPReconnectInterface();
 p_stReconnectInterface->pfn_eTCPReconnect=eL0PCWin95TCPReconnect;
 p_stReconnectInterface->pfn_eTCPGetPortAndAddress=eL0PCWin95TCPGetPortAndAddress;
 p_stReconnectInterface->pfn_vTCPNewRouteKnown=vL0PCWin95TCPNewRouteKnown;
 p_stReconnectInterface->pfn_uwTCPGetNewReconnectChannel=uwL0PCWin95TCPGetNewReconnectChannel;
 
 /* initialize our global socket descriptors */
#if defined(NET_USE_DEBUG)
  vDebugClear(Net_C_Debug_TCP);
  vDebugS(Net_C_Debug_TCP,"Netlib - Level 0 - TCP WIN95 : Log file");
#endif /* NET_USE_DEBUG */
 
 gs_bL0PCWin95TCPInitState = 0x00;
 
 /* bind to the DLL if necessary */
 
 if(!gs_cIsWinsocDescInit) {
	 if(eNetGetWinSockDesc(&gs_p_stTCPWinSocDesc)!=NetLib_E_es_NoError)
		 return ;
	 gs_cIsWinsocDescInit=~0;
 }
 
 p_stProtocolInterface=pstLevel1AddProtocol();
 
 /* logical identification of the protocol */
 p_stProtocolInterface->eProtocol = E_Net_pr_Windows95TCPProtocol;
 
 /* setup the function pointers */
 p_stProtocolInterface->fn_uwStartChannelScan = uwL0PCWin95TCPStartChannelScan;
 p_stProtocolInterface->fn_uwStartBroadcastChannelScan= uwL0PCWin95TCPStartBroadcastChannelScan;
 p_stProtocolInterface->fn_uwNextChannel= uwL0PCWin95TCPNextChannel;
 p_stProtocolInterface->fn_uwNextBroadcastChannel= uwL0PCWin95TCPNextBroadcastChannel;
 p_stProtocolInterface->fn_eReadData= eL0PCWin95TCPReadData;
 p_stProtocolInterface->fn_eSendData= eL0PCWin95TCPSendData;
 p_stProtocolInterface->fn_eQueryChannelStatus= eL0PCWin95TCPQueryChannelStatus;
 p_stProtocolInterface->fn_vLevel0NetEngine = vL0PCWin95TCPNetEngine;
 p_stProtocolInterface->fn_vCloseChannel = (tdfn_vCloseChannel)C_pNull;
 p_stProtocolInterface->fn_vLevel0CloseProtocol = vL0PCWin95TCPCloseProtocol;
 p_stProtocolInterface->eIsInternet = 1;
 
 /* initialize all slots as unused channels */
 
 for (uwChannelToInit = 0; uwChannelToInit < C_ucL0MaxNumberOfTCPChannels; uwChannelToInit ++)
 {
  /* the channel is not used yet  */
  gs_a_stL0PCWin95TCPChannels[uwChannelToInit].ubf1IsSlotInUse = 0;
 
  /* this is a channel usable to send private messages */
 
  gs_a_stL0PCWin95TCPChannels[uwChannelToInit].ubf1IsBroadcast = 0;
  gs_a_stL0PCWin95TCPChannels[uwChannelToInit].ubf1IsConnected = 0;
 
  /* initialize the destination address: */
 
  gs_a_stL0PCWin95TCPChannels[uwChannelToInit].stRemoteAddr.sin_family=AF_INET;
  gs_a_stL0PCWin95TCPChannels[uwChannelToInit].stRemoteAddr.sin_port=0;
  gs_a_stL0PCWin95TCPChannels[uwChannelToInit].stRemoteAddr.sin_addr.s_addr=gs_p_stTCPWinSocDesc->m_pfn_ul_htonl(INADDR_NONE);
 
  /* the channel is working properly */
  gs_a_stL0PCWin95TCPChannels[uwChannelToInit].eChannelStatus = E_ts_OK;
 }
 gs_bL0PCWin95TCPInitState = gs_bL0PCWin95TCPInitState |(char)0x01;
 gs_pfnConnectionResult=NULL;
}
