/********************************************************************************/
/*                                                                                                                                                              */
/*      NetL1.c                                                                                                                                         */
/*                                                                                                                                                              */
/*      This file defines the level 1 functions                                                                         */
/*      message routing and queueing                                                                                            */
/*                                                                                                                                                              */
/********************************************************************************/
 
#include "warnings.h"
#include "l0gldef.h"
 
#include "PrivNetDef.h"
#include "NetL1Pri.h"
#include "NetEnd.h"
#include "NetMemCo.h"
#include "NetList2.h"
#include <NET\NetDebug.h>
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
#define C_MaxNbrElement 200L
 
typedef struct
{
  tdeNetProtocol eProtocol;
  tduwNetChannel uwChannel;
}tdstProtChannel;
 
typedef struct
{
  tdstNetMessage *pMessage;
  tdstProtChannel *dstTabDest;
  unsigned short uwNbrDest;
}tdstNetL1Message;
 
/*
------------------------------------------------------------------------------------------
DEFINITION OF INTERNAL VARIABLES:
------------------------------------------------------------------------------------------
*/
/*
 * there is one slot per non local player. any invalid identification field
 * marks the slot as free for any incoming player.
 */
static tdstRouteCell *gs_d_stRouteTable;
 
static tdstNetList2 gs_stOutgoingMsgList;
static tdstNetList2 gs_stIncomingSysMsgList;
static tdstNetList2 gs_stIncomingEngineMsgList;
 
/* Maximum of remote players accepted in the session */
static unsigned short gs_uwMaxNumberOfRemotePlayers;
/* identification of the local player */
static NetLib_tduxPlayerId gs_ulLocalPlayerId;
/* identification of the session :*/
static NetLib_tduxSessionId gs_ulSessionId;
 
static short gs_wBroadcastIpx;
static NetLib_tduwMode gs_uwMode;
 
static unsigned short gs_uwMaxNbrOfMsgPerList;
static unsigned char *gs_dcRndList;
 
/* list of sessions which answered to our session request
 * message. */
static tdstSessionCell *gs_p_stSessionCell;
 
/* dynamic array of available protocol identifiers, and the
 * number of such protocols */
static tdstNetProtocolInterface *gs_d_stAvailableProtocolInterfaces;
static unsigned short gs_uwNumberOfProtocol;
 
/*
 * Reemission mode
*/
static unsigned char gs_ucReemissionMode;
 
  /* This type contain the list of player who should receive a broadcast message from a
     player "SourcePlayer". */
typedef struct
{
  NetLib_tduxPlayerId SourcePlayer;   /* Source player. */
  NetLib_tduxPlayerId NbrDestPlayer;  /* Number of players who should receive a broadcast message. */
  NetLib_tduxPlayerId *TabDestPlayer; /* Table of players who should receive a broadcast message. */
}BroadcastSource;
 
NetLib_tduxPlayerId NbrBroadcastSource; /* Number of source broadcast who must routing. */
BroadcastSource *TabBroadcastSource;    /* Table of source broadcast who must routing. */
 
static NetLib_tdeErrorStatus eNetLevel1InsertNewMsg(tdstNetIter *pstIter,tdstNetL1Message *pstL1Message);
static NetLib_tdeErrorStatus eLevel1FillMessageHeader(tdstNetMessage *p_stMessage);
 
/*****************************************************************************
 * Description: vLevel1RandomList
 * Return a random list of n first integer.
 *****************************************************************************
 * Input:   Nbr : Number of random integer (should be less than 256).
 *          List : Array of integer.
 * Output:  none
 ****************************************************************************/
static void vLevel1RandomList(unsigned char Nbr,unsigned char *List)
{
  int i,v;
  char c;
  for (i=0;i<Nbr;i++) List[i]=i;
  for (i=Nbr-1;i>0;i--)
  {
    v=rand()%(i+1);
    c=List[i];
    List[i]=List[v];
    List[v]=c;
  }
}
 
/*****************************************************************************
 * Description: vLevel1SearchBroadcastSource
 * Search a broadcast source.
 *****************************************************************************/
static BroadcastSource *vLevel1SearchBroadcastSource(NetLib_tduxPlayerId SourcePlayer)
{
  NetLib_tduxPlayerId i;
  for (i=0;i<NbrBroadcastSource;i++)
  {
    if (TabBroadcastSource[i].SourcePlayer==SourcePlayer) return TabBroadcastSource+i;
  }
  return NULL;
}
 
/*****************************************************************************
 * Description: vLevel1AddBroadcastSource
 * Add a new broadcast source.
 *****************************************************************************/
static BroadcastSource *vLevel1AddBroadcastSource(NetLib_tduxPlayerId SourcePlayer)
{
  BroadcastSource *NewTab;
  NewTab=(BroadcastSource *)pMalloc(sizeof(BroadcastSource)*(NbrBroadcastSource+1));
  if (NewTab)
  {
    memcpy(NewTab,TabBroadcastSource,sizeof(BroadcastSource)*NbrBroadcastSource);
    NewTab[NbrBroadcastSource].SourcePlayer=SourcePlayer;
    NewTab[NbrBroadcastSource].NbrDestPlayer=0;
    NewTab[NbrBroadcastSource].TabDestPlayer=NULL;
    vFree((char *)TabBroadcastSource);
    TabBroadcastSource=NewTab;
    NbrBroadcastSource++;
    return TabBroadcastSource+(NbrBroadcastSource-1);
  }
  else return NULL;
}
 
/*****************************************************************************
 * Description: vLevel1SupBroadcastSource
 * Supprim a broadcast source.
 *****************************************************************************/
void vLevel1SupBroadcastSource(NetLib_tduxPlayerId SourcePlayer)
{
  BroadcastSource *NewTab;
  if (vLevel1SearchBroadcastSource(SourcePlayer))
  {
    if (NbrBroadcastSource>1)
    {
      NewTab=(BroadcastSource *)pMalloc(sizeof(BroadcastSource)*(NbrBroadcastSource-1));
      if (NewTab)
      {
        NetLib_tduxPlayerId i,j;
        for (i=0,j=0;i<NbrBroadcastSource;i++)
        {
          if (TabBroadcastSource[i].SourcePlayer==SourcePlayer) vFree((char *)TabBroadcastSource[i].TabDestPlayer);
          else
          {
            NewTab[j]=TabBroadcastSource[i];
            j++;
          }
        }
        vFree((char *)TabBroadcastSource);
        TabBroadcastSource=NewTab;
        NbrBroadcastSource--;
      }
    }
    else
    {
      vFree((char *)TabBroadcastSource);
      TabBroadcastSource=NULL;
      NbrBroadcastSource=0;
    }
  }
}
 
/*****************************************************************************
 * Description: vLevel1AddBroadcastPath
 * Add a path into the broadcast table.
 *****************************************************************************/
void vLevel1AddBroadcastPath(NetLib_tduxPlayerId SourcePlayer,NetLib_tduxPlayerId DestPlayer)
{
  BroadcastSource *bs;
  NetLib_tduxPlayerId *NewTab;
  NetLib_tduxPlayerId i;
  bs=vLevel1SearchBroadcastSource(SourcePlayer);
  if (!bs) bs=vLevel1AddBroadcastSource(SourcePlayer);
  if (!bs) return;
  for (i=0;i<bs->NbrDestPlayer;i++)
  {
    if (bs->TabDestPlayer[i]==DestPlayer) return;
  }
  NewTab=(NetLib_tduxPlayerId *)pMalloc(sizeof(NetLib_tduxPlayerId)*(bs->NbrDestPlayer+1));
  if (NewTab)
  {
    memcpy(NewTab,bs->TabDestPlayer,sizeof(NetLib_tduxPlayerId)*bs->NbrDestPlayer);
    NewTab[bs->NbrDestPlayer]=DestPlayer;
    vFree((char *)bs->TabDestPlayer);
    bs->TabDestPlayer=NewTab;
    bs->NbrDestPlayer++;
  }
}
 
/*****************************************************************************
 * Description: vLevel1SupBroadcastPath
 * Supprim a path into the broadcast table.
 *****************************************************************************/
void vLevel1SupBroadcastPath(NetLib_tduxPlayerId SourcePlayer,NetLib_tduxPlayerId DestPlayer)
{
  BroadcastSource *bs;
  NetLib_tduxPlayerId *NewTab;
  NetLib_tduxPlayerId i;
  bs=vLevel1SearchBroadcastSource(SourcePlayer);
  if (!bs) return;
  for (i=0;i<bs->NbrDestPlayer;i++)
  {
    if (bs->TabDestPlayer[i]==DestPlayer)
    {
      if (bs->NbrDestPlayer==1) vLevel1SupBroadcastSource(SourcePlayer);
      else
      {
        NewTab=(NetLib_tduxPlayerId *)pMalloc(sizeof(NetLib_tduxPlayerId)*(bs->NbrDestPlayer-1));
        if (NewTab)
        {
          memcpy(NewTab,bs->TabDestPlayer,sizeof(NetLib_tduxPlayerId)*i);
          memcpy(NewTab+i,bs->TabDestPlayer+i+1,sizeof(NetLib_tduxPlayerId)*(bs->NbrDestPlayer-i-1));
          vFree((char *)bs->TabDestPlayer);
          bs->TabDestPlayer=NewTab;
          bs->NbrDestPlayer--;
        }
      }
      break;
    }
  }
}
 
/*****************************************************************************
 * Description: vLevel1SupBroadcastDest
 * Supprim a destination into the broadcast table.
 *****************************************************************************/
void vLevel1SupBroadcastDest(NetLib_tduxPlayerId DestPlayer)
{
  BroadcastSource *bs;
  NetLib_tduxPlayerId *NewTab;
  NetLib_tduxPlayerId i,SourcePlayer;
  for (SourcePlayer=0;SourcePlayer<NbrBroadcastSource;SourcePlayer++)
  {
    bs=TabBroadcastSource+SourcePlayer;
    for (i=0;i<bs->NbrDestPlayer;i++)
    {
      if (bs->TabDestPlayer[i]==DestPlayer)
      {
        if (bs->NbrDestPlayer==1) vLevel1SupBroadcastSource(SourcePlayer);
        else
        {
          NewTab=(NetLib_tduxPlayerId *)pMalloc(sizeof(NetLib_tduxPlayerId)*(bs->NbrDestPlayer-1));
          if (NewTab)
          {
            memcpy(NewTab,bs->TabDestPlayer,sizeof(NetLib_tduxPlayerId)*i);
            memcpy(NewTab+i,bs->TabDestPlayer+i+1,sizeof(NetLib_tduxPlayerId)*(bs->NbrDestPlayer-i-1));
            vFree((char *)bs->TabDestPlayer);
            bs->TabDestPlayer=NewTab;
            bs->NbrDestPlayer--;
          }
        }
        break;
      }
    }
  }
}
 
/*****************************************************************************
 * Description: vLevel1ExistBroadcastPath
 * Return non zero value if path exist.
 *****************************************************************************/
int vLevel1ExistBroadcastPath(NetLib_tduxPlayerId SourcePlayer,NetLib_tduxPlayerId DestPlayer)
{
  BroadcastSource *bs;
  NetLib_tduxPlayerId i;
  bs=vLevel1SearchBroadcastSource(SourcePlayer);
  if (!bs) return 0;
  for (i=0;i<bs->NbrDestPlayer;i++)
  {
    if (bs->TabDestPlayer[i]==DestPlayer) return 1;
  }
  return 0;
}
 
/*****************************************************************************
 * Description: vLevel1GetBroadcastSource
 * Return the table of destination players who should receive a broadcast message
 * from a source player.
 * If no dest, TabDestPlayer is set to NULL, and NbrDestPlayer is set to 0.
 *****************************************************************************/
static void vLevel1GetBroadcastSource(NetLib_tduxPlayerId SourcePlayer,NetLib_tduxPlayerId **TabDestPlayer,NetLib_tduxPlayerId *NbrDestPlayer)
{
  BroadcastSource *bs;
  bs=vLevel1SearchBroadcastSource(SourcePlayer);
  if (bs)
  {
    *TabDestPlayer=bs->TabDestPlayer;
    *NbrDestPlayer=bs->NbrDestPlayer;
  }
  else
  {
    *TabDestPlayer=NULL;
    *NbrDestPlayer=0;
  }
}
 
/*****************************************************************************
 * Description: vLevel1KillBroadcastTab
 * Dereserv the table of broadcast source.
 *****************************************************************************/
static void vLevel1KillBroadcastTab(void)
{
  NetLib_tduxPlayerId i;
  for (i=0;i<NbrBroadcastSource;i++)
  {
    vFree((char *)TabBroadcastSource[i].TabDestPlayer);
  }
  vFree((char *)TabBroadcastSource);
  TabBroadcastSource=NULL;
  NbrBroadcastSource=0;
}
 
/*****************************************************************************
 * Description : eLevel1SetBroadcastState
 * Set the broadcast state.
 *****************************************************************************/
void vLevel1SetBroadcastState(short BroadcastIpx)
{
  gs_wBroadcastIpx=BroadcastIpx;
}
 
/*****************************************************************************
 * Description: eLevel1UpdateRouteOfPlayer
 *****************************************************************************
 * Input: p_stIOSystemCell, pointer to the received message.
 * Output: none
 ****************************************************************************/
void eLevel1UpdateRouteOfPlayer(tdstNetMessage *pstMes,tdeNetProtocol eProt,tduwNetChannel uwChannel)
{
  short wIndexInRouteTable;
  wIndexInRouteTable = wLevel1IndexInRouteTable(pstMes->uxSenderId);
  if (wIndexInRouteTable>=0)
  {
    gs_d_stRouteTable[wIndexInRouteTable].eProtocol=eProt;
    gs_d_stRouteTable[wIndexInRouteTable].uwChannel=uwChannel;
    vLevel1AddBroadcastPath(gs_ulLocalPlayerId,pstMes->uxSenderId);
  }
}
 
/*****************************************************************************
 * Description: vLevel1SetReemissionMode
 * Sets the reemission mode
 *****************************************************************************
 * Input:   ucNewReemissionMode : the new reemission mode
 *  -> 0 : the message is not reemited in case of problem
 *  -> 1 : the message is reemited in case of problem
 * Output:  None.
 *****************************************************************************
 * Creation Date: April 12, 1996         Author: Albert Pais
 *****************************************************************************/
void vLevel1SetReemissionMode(unsigned char ucNewReemissionMode)
{
  gs_ucReemissionMode = ucNewReemissionMode;
}
 
/*****************************************************************************
 * Description: ucLevel1GetReemissionMode
 * Gets the reemission mode
 *****************************************************************************
 * Input:   None
 * Output:  The reemission mode :
 *  -> 0 : the message is not reemited in case of problem
 *  -> 1 : the message is reemited in case of problem
 *****************************************************************************
 * Creation Date: April 12, 1996         Author: Albert Pais
 *****************************************************************************/
unsigned char ucLevel1GetReemissionMode(void)
{
  return gs_ucReemissionMode;
}
 
/*****************************************************************************
 * Description: wLevel1IndexInRouteTable
 * Retrieves the index in the dynamic route table of the route structure
 * for a given player identification.
 *****************************************************************************
 * Input:   ulPlayerId, the identification of the specified player.
 * Output:  the index in the gs_d_stRouteTable dynamic array if the player
 *          has an entry, else -1.
 *****************************************************************************
 * Creation Date: April 2, 1996         Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date: April 16, 1996                 Author: Albert PAIS
 * Comment : if called with C_uxNetInvalidId, returns a free slot
 ****************************************************************************/
short wLevel1IndexInRouteTable(NetLib_tduxPlayerId ulPlayerId)
{
  short wRouteIndex;
 
  /* if the player is not the local player */
  if (ulPlayerId != gs_ulLocalPlayerId)
  {
    /* scan the route table */
    wRouteIndex = gs_uwMaxNumberOfRemotePlayers-1;
    while((wRouteIndex >= 0) && (gs_d_stRouteTable[wRouteIndex].ulPlayerId != ulPlayerId))
      wRouteIndex --;
    /*Tip : if no player was found in the list, wRouteIndex value is -1, that is to
    say the invalid index :*/
    return wRouteIndex;
  }
  return (short)-1;
}
 
/*//////////////////////////////////////////////////////////////////////////////
Description : NetLib_tdeErrorStatus eLevel1IsSendingListEmpty(NetLib_tduxPlayerId ulPlayerId)
 Tells if the sending list of the player is empty or not
////////////////////////////////////////////////////////////////////////////////
Input : A player  id
////////////////////////////////////////////////////////////////////////////////
Output : NetLib_E_es_True if the sending list is empty
         NetLib_E_es_False if not
         NetLib_E_es_UnknownPlayerId if the id is unknown
////////////////////////////////////////////////////////////////////////////////
Creation date : April 15, 96
Author : Albert Pais
//////////////////////////////////////////////////////////////////////////////*/
NetLib_tdeErrorStatus eLevel1IsSendingListEmpty(NetLib_tduxPlayerId ulPlayerId)
{
  if (ulNetList2NbrElem(&gs_stOutgoingMsgList)) return NetLib_E_es_False; /* the list is not empty :*/
  else return NetLib_E_es_True;  /* the list is empty :*/
}
 
/*//////////////////////////////////////////////////////////////////////////////
Description : NetLib_tdeErrorStatus eLevel1IsSendingListOk(NetLib_tduxPlayerId ulPlayerId)
 Tells if the sending list of the player is ok
////////////////////////////////////////////////////////////////////////////////
Input : A player  id
////////////////////////////////////////////////////////////////////////////////
Output : NetLib_E_es_NoError if the sending list is ok
         NetLib_E_es_UnknownPlayerId if the player is unknown
         an error code otherwise
////////////////////////////////////////////////////////////////////////////////
Creation date : April 15, 96
Author : Albert Pais
//////////////////////////////////////////////////////////////////////////////*/
NetLib_tdeErrorStatus eLevel1IsSendingListOk(NetLib_tduxPlayerId ulPlayerId)
{
  return NetLib_E_es_NoError;
}
 
/*****************************************************************************
 * Description: p_stInterfaceOfProtocol
 * scans the protocol interfaces and retrieves the one we want.
 *****************************************************************************
 * Input:   eProtocol, the identification of the protocol we need.
 * Output:  pointer on the interface structure of this protocol.
 *****************************************************************************
 * Creation Date: April 4, 1996         Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date:                                Author:
 ****************************************************************************/
tdstNetProtocolInterface *p_stLevel1GetInterfaceOfProtocol(tdeNetProtocol eProtocol)
{
  unsigned char ucProtocolIndex;
 
  /* scan all protocol interface entries */
  for (ucProtocolIndex=0;ucProtocolIndex<gs_uwNumberOfProtocol;ucProtocolIndex++) {
    /* if we found the one we want */
    if  (gs_d_stAvailableProtocolInterfaces[ucProtocolIndex].eProtocol == eProtocol)
      return gs_d_stAvailableProtocolInterfaces+ucProtocolIndex;
  }
  /* else return a C_pNull pointer */
  return (tdstNetProtocolInterface *) C_pNull;
}
 
/*****************************************************************************
 * Description: eLevel1SendMessage
 * Delivers the given message in the outgoing queue of the recipient.
 *****************************************************************************
 * Input:   uxRecipientId, the recipient player's identification number.
 *          p_stMessageToSend, a pointer to a message structure followed by
 *          the corresponding message body.
 * Output:  an error condition depending on the action's result.
 *****************************************************************************
 * Creation Date: April 2, 1996         Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date:                                Author:
 ****************************************************************************/
NetLib_tdeErrorStatus eLevel1SendMessage(tdstNetMessage *p_stMessageToSend,tdeNetProtocol eProt,tduwNetChannel uwChannel)
{
  tdstNetIter stIter;
  tdstNetL1Message stL1Message;
  short wIndexInRouteTable,i,j;
  tdstProtChannel stProtChannel;
  tdstNetProtocolInterface *p_stProtocolInterface;
  int bTCP=0;
 
  /* if the recipient is in the route table */
  if ((eProt==E_Net_pr_InvalidProtocol) && (uwChannel==C_uwNetInvalidChannel) &&
      (wLevel1IndexInRouteTable(p_stMessageToSend->uxRecipientId)==-1) &&
      (p_stMessageToSend->uxRecipientId != C_uxNetBroadcastId) &&
      (p_stMessageToSend->uxRecipientId != C_uxNetInvalidId))
    /* warn that the recipient is not known */
    return NetLib_E_es_UnknownPlayerId;
 
  stL1Message.pMessage=p_stMessageToSend;
  if (p_stMessageToSend->uxRecipientId != C_uxNetBroadcastId)
  {
    stL1Message.uwNbrDest=1;
    stL1Message.dstTabDest=(tdstProtChannel *)pMalloc(stL1Message.uwNbrDest*sizeof(tdstProtChannel));
    if ((eProt!=E_Net_pr_InvalidProtocol) && (uwChannel!=C_uwNetInvalidChannel))
    {
/*
CHRISTOPHE
commented out to allow connection denial messages to be handled properly 
--- but it may have side effects ?
 
      p_stMessageToSend->uxRecipientId=C_uxNetInvalidId;
*/      stL1Message.dstTabDest->eProtocol=eProt;
      stL1Message.dstTabDest->uwChannel=uwChannel;
    }
    else
    {
      wIndexInRouteTable = wLevel1IndexInRouteTable(p_stMessageToSend->uxRecipientId);
      if (wIndexInRouteTable>=0)
      {
        stL1Message.dstTabDest->eProtocol=gs_d_stRouteTable[wIndexInRouteTable].eProtocol;
        stL1Message.dstTabDest->uwChannel=gs_d_stRouteTable[wIndexInRouteTable].uwChannel;
      }
      else return NetLib_E_es_UnknownPlayerId;
    }
  }
  else
  {
    NetLib_tduxPlayerId *TabDestPlayer,NbrDestPlayer;
    vLevel1GetBroadcastSource(p_stMessageToSend->uxSenderId,&TabDestPlayer,&NbrDestPlayer);
    if (!NbrDestPlayer)
    {
      vFree(p_stMessageToSend);
      return NetLib_E_es_NoError;
    }
    stL1Message.uwNbrDest=0;
    stL1Message.dstTabDest=(tdstProtChannel *)pMalloc(NbrDestPlayer*sizeof(tdstProtChannel));
    if (!stL1Message.dstTabDest) return NetLib_E_es_NotEnoughMemory;
    vLevel1RandomList(NbrDestPlayer,gs_dcRndList);
    if (gs_uwMode==NetLib_Mode_UBI)
    {
      for (i=0;i<NbrDestPlayer;i++)
      {
        wIndexInRouteTable = wLevel1IndexInRouteTable(TabDestPlayer[gs_dcRndList[i]]);
        if (wIndexInRouteTable>=0)
        {
          stProtChannel.eProtocol=gs_d_stRouteTable[wIndexInRouteTable].eProtocol;
          if (stProtChannel.eProtocol==E_Net_pr_Windows95TCPProtocol)
            bTCP=1;
        }
      }
    }
    for (i=0;i<NbrDestPlayer;i++)
    {
      wIndexInRouteTable = wLevel1IndexInRouteTable(TabDestPlayer[gs_dcRndList[i]]);
      if (wIndexInRouteTable>=0)
      {
        stProtChannel.eProtocol=gs_d_stRouteTable[wIndexInRouteTable].eProtocol;
        if ((stProtChannel.eProtocol==E_Net_pr_Windows95IPXProtocol) && gs_wBroadcastIpx)
        {
          p_stProtocolInterface=p_stLevel1GetInterfaceOfProtocol(E_Net_pr_Windows95IPXProtocol);
          stProtChannel.uwChannel=p_stProtocolInterface->fn_uwStartBroadcastChannelScan();
        }
        else stProtChannel.uwChannel=gs_d_stRouteTable[wIndexInRouteTable].uwChannel;
        if ((bTCP) && (stProtChannel.eProtocol==E_Net_pr_Windows95GSUBIProtocol))
        {
          stProtChannel.uwChannel=TabDestPlayer[gs_dcRndList[i]]+2;
        }
        for (j=0;j<stL1Message.uwNbrDest;j++)
        {
          if (memcmp(stL1Message.dstTabDest+j,&stProtChannel,sizeof(tdstProtChannel))==0)
            break;
        }
        if (j==stL1Message.uwNbrDest)
        {
          memcpy(stL1Message.dstTabDest+j,&stProtChannel,sizeof(tdstProtChannel));
          stL1Message.uwNbrDest++;
        }
      }
    }
  }
  /* fill standard slots of the header : */
  eLevel1FillMessageHeader(stL1Message.pMessage);
  pNetList2IterInit(&stIter,&gs_stOutgoingMsgList);
  return eNetLevel1InsertNewMsg(&stIter,&stL1Message);
}
 
/*****************************************************************************
 * Description: eLevel1GoesToIP
 * tells whether the next step in a route to a player is through an "internet"
 * protocol
 *****************************************************************************
 * Input:   uxRecipientId, the recipient player's identification number.
 * Output:  an error condition depending on the action's result.
 *****************************************************************************
 * Creation Date: December 17, 1996         Author: Christophe Roguet
 *****************************************************************************
 * Modification log:
 * Date:                                Author:
 ****************************************************************************/
NetLib_tdeErrorStatus eLevel1GoesToIP(NetLib_tduxPlayerId uxRecipientId)
{
  short wIndexInRouteTable;
 
  /* if the recipient is in the route table */
  if ((uxRecipientId == C_uxNetInvalidId) ||
      ((wIndexInRouteTable = wLevel1IndexInRouteTable(uxRecipientId)) == -1))
    /* warn that the recipient is not known */
    return NetLib_E_es_UnknownPlayerId;
 
  return gs_d_stAvailableProtocolInterfaces[gs_d_stRouteTable[wIndexInRouteTable].eProtocol].eIsInternet;
}
 
/*****************************************************************************
 * Description: eLevel1ReadMessage
 * Retrieves a pointer to the next incoming message addressed to the specified
 * player.
 *****************************************************************************
 * Input:   ulPlayerId, identification number of the recipient.
 *          pp_stMessage, pointer to the variable that will hold the address
 *          of the read message.
 * Output:  the location pointed by pp_stMessage is modified to hold the
 *          address of the retrieved message. An error condition is returned.
 *****************************************************************************
 * Creation Date: April 2, 1996         Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date:                                Author:
 ****************************************************************************/
NetLib_tdeErrorStatus eLevel1ReadMessage(NetLib_tduxPlayerId ulPlayerId,tdstNetMessage **pp_stMessage,unsigned char bRemoveFromQueue)
{
  tdstNetIter stIter;
  tdstNetL1Message *pL1Mes;
  pL1Mes=(tdstNetL1Message*)pNetList2IterInit(&stIter,&gs_stIncomingEngineMsgList);
  if (ulPlayerId!=C_uxNetBroadcastId)
  {
    while (pL1Mes)
    {
      if (pL1Mes->pMessage->uxSenderId==ulPlayerId) break;
      pL1Mes=pNetList2Next(&stIter);
    }
  }
  if (pL1Mes)
  {
    *pp_stMessage=pL1Mes->pMessage;
    if (bRemoveFromQueue)
    {
      vFree(pL1Mes->dstTabDest);
      pNetList2DeleteElem(&stIter);
    }
    return NetLib_E_es_NoError;
  }
  else return NetLib_E_es_FIFOIsEmpty;
}
 
/*****************************************************************************
 * Description: eLevel1SendSessionRequestMsg
 *****************************************************************************
 * Creation Date:                       Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date: April 16, 1996                 Author: Albert Pais
 * Adding some errors
 ****************************************************************************/
NetLib_tdeErrorStatus eLevel1SendSessionRequestMsg(NetLib_tduxDescriptionLength uxMaxPlayerInfo)
{
  tdstNetMessage *p_stMessage;
  unsigned char ucProtocolIndex;
  tdstNetProtocolInterface *p_stProtocolInterface;
  tduwNetChannel uwChannel;
 
  /* scan all protocols */
  for (ucProtocolIndex = 0;ucProtocolIndex < gs_uwNumberOfProtocol;ucProtocolIndex ++)
  {
        p_stProtocolInterface = &gs_d_stAvailableProtocolInterfaces[ucProtocolIndex];
 
    uwChannel = p_stProtocolInterface->fn_uwStartBroadcastChannelScan();
 
    /* scan all broadcast interfaces of the protocol */
    while (uwChannel != C_uwNetInvalidChannel)
    {
      /* allocate and setup a new message with an empty body */
      p_stMessage = (tdstNetMessage *) pMalloc(sizeof(tdstNetMessage)+sizeof(NetLib_tduxDescriptionLength));
      if(p_stMessage == (tdstNetMessage*)C_pNull) return NetLib_E_es_NotEnoughMemory;
 
      p_stMessage->eMessageType = E_Net_mt_SysRequestActiveSessions;
      p_stMessage->uxRecipientId = C_uxNetInvalidId;
      p_stMessage->uxSenderId = gs_ulLocalPlayerId;
      p_stMessage->uwMessageSizeInBytes = sizeof(NetLib_tduxDescriptionLength);
      *((NetLib_tduxDescriptionLength*)(p_stMessage+1))=uxMaxPlayerInfo;
 
      /* store the message in the outgoing system message queue */
      if (eLevel1SendMessage(p_stMessage,p_stProtocolInterface->eProtocol,uwChannel)!=NetLib_E_es_NoError)
      {/* the last message couldn't be sent, so delete it first*/
        vFree((tdpPointer)p_stMessage);
        p_stMessage = (tdstNetMessage*)C_pNull;
        return NetLib_E_es_FIFOIsFull;
      }
      /* go to next broadcast channel of the protocol */
      uwChannel = p_stProtocolInterface->fn_uwNextBroadcastChannel(uwChannel);
    }/* end of while */
  }
  return NetLib_E_es_NoError;
}
 
/*****************************************************************************
 * Description: eLevel1HandleSysMsgUnknownSender
 * Handles an incoming message saying that its sender field (which thus does
 * not necessarily specify its effective sender) contains the identification
 * of an invalid player.
 *****************************************************************************
 * Input:   p_stMessage, pointer to the incoming message that tells me all
 *          about it
 * Output:  an error condition depending on the handling's result.
 *****************************************************************************
 * Creation Date: April 2, 1996         Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date:                                Author:
 ****************************************************************************/
NetLib_tdeErrorStatus eLevel1HandleSysMsgUnknownSender(tdstNetMessage *p_stMessage)
{
  short wIndexInRouteTable;
  NetLib_tdeErrorStatus eErrorReturned;
 
  /* if the recipient is the local player */
  if (p_stMessage->uxRecipientId == gs_ulLocalPlayerId)
  {
   /* if the sender has an entry in the route table */
    if ((p_stMessage->uxSenderId != C_uxNetInvalidId)&&
        ((wIndexInRouteTable = wLevel1IndexInRouteTable(p_stMessage->uxSenderId)) != -1))
    {
      /* free the memory allocated for this message */
      vFree((tdpPointer) p_stMessage);
      return NetLib_E_es_NoError;
    }
    else
    {/* don't care any more */
      vFree((tdpPointer) p_stMessage);
      return NetLib_E_es_UnknownSenderId;
    }
  }
  /* the recipient is not the local player */
  else
  {
    /* if the recipient has an entry in the route table */
    p_stMessage->uxPriority = NetLib_C_uxMaxPriority;
    p_stMessage->uxReplaceType = 0;
    p_stMessage->uxReplace = 0;
    if((eErrorReturned = eLevel1SendMessage(p_stMessage,E_Net_pr_InvalidProtocol,C_uwNetInvalidChannel))
     !=NetLib_E_es_NoError)
    {/* We don't care any more about the message :*/
      vFree((tdpPointer)p_stMessage);
      return eErrorReturned;
    }
    /*else*/
    return NetLib_E_es_NoError;
  }
}
 
/*****************************************************************************
 * Description: eLevel1HandleSysMsgUnknownRecipient
 * Handles an incoming message saying that the full message its contains has
 * an initial unknown recipient.
 *****************************************************************************
 * Input:   p_stMessage, pointer to the message annoucing the information.
 * Output:  an error condition depending on the handling's result.
 *****************************************************************************
 * Creation Date: April 2, 1996         Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date:                                Author:
 ****************************************************************************/
NetLib_tdeErrorStatus eLevel1HandleSysMsgUnknownRecipient(tdstNetMessage *p_stMessage)
{
  tdstNetMessage *p_stReturnedMessage;
  short wIndexInRouteTable;
 
  /* if the recipient is the local player */
  if (p_stMessage->uxRecipientId == gs_ulLocalPlayerId)
  {
    /* the message body is in itself a full-fledged message */
    p_stReturnedMessage = p_stMessage + 1;
    /* if the original recipient is in the route table */
    if ((p_stReturnedMessage->uxRecipientId != C_uxNetInvalidId)&&
        ((wIndexInRouteTable = wLevel1IndexInRouteTable(p_stReturnedMessage->uxRecipientId)) != -1))
    {
      /* free the memory allocated for this message */
      vFree( (tdpPointer) p_stMessage);
      return NetLib_E_es_NoError;
    }
    else
    {
      vFree((tdpPointer)p_stMessage);
      return NetLib_E_es_UnknownInitialRecipient;
    }
  }
  /* the recipient is not the local player */
  else
  {
    /* if the recipient is in the route table */
    if ((p_stMessage->uxRecipientId != C_uxNetInvalidId)&&
        ((wIndexInRouteTable = wLevel1IndexInRouteTable(p_stMessage->uxRecipientId)) != -1))
    {
      /* queue the full message in his outgoing queue */
      p_stMessage->uxPriority = NetLib_C_uxMaxPriority;
      p_stMessage->uxReplaceType = 0;
      p_stMessage->uxReplace = 0;
      if(eLevel1SendMessage(p_stMessage,E_Net_pr_InvalidProtocol,C_uwNetInvalidChannel)!= NetLib_E_es_NoError)
      {
       vFree((tdpPointer)p_stMessage);
       return NetLib_E_es_FIFOIsFull;
      }
      /*else*/
      return NetLib_E_es_NoError;
    }
    else
    {
      vFree((tdpPointer)p_stMessage);
      p_stMessage = (tdstNetMessage*)C_pNull;
      return NetLib_E_es_UnknownRecipientId;
    }
  }
}
 
/*****************************************************************************
 * Description: eLevel1RouteSysMsg
 * route system messages to their proper recipients, or issues adequate
 * system messages to warn in case of internal problem.
 *****************************************************************************
 * Input:   p_stIOSystemCell, pointer to the system message to handle.
 * Output:  an error condition depending on the handling's result.
 ****************************************************************************/
NetLib_tdeErrorStatus eLevel1RouteSysMsg(tdstNetMessage *pstMes,tdeNetProtocol eProt,tduwNetChannel uwChannel)
{
  short wIndexInRouteTable;
  tdstNetMessage *p_stNewMessage;
  NetLib_tdeErrorStatus eErrorReturned;
 
  if ((pstMes->uxRecipientId == gs_ulLocalPlayerId)&&
      (pstMes->uxSenderId != C_uxNetInvalidId)&&
      (wLevel1IndexInRouteTable(pstMes->uxSenderId) == -1))
  {
    /* return the message to its sender */
    pstMes->eMessageType = E_Net_mt_SysUnknownSender;
    pstMes->uxRecipientId = pstMes->uxSenderId;
    return eLevel1SendMessage(pstMes,eProt,uwChannel);
  }
 
  /* if the initial recipient is not the local player */
  if (pstMes->uxRecipientId != gs_ulLocalPlayerId)
  {
    /* if the initial recipient is in the route table */
    if ((pstMes->uxRecipientId != C_uxNetInvalidId)&&
        ((wIndexInRouteTable=wLevel1IndexInRouteTable(pstMes->uxRecipientId)) != -1))
    {
      eErrorReturned=eLevel1SendMessage(pstMes,E_Net_pr_InvalidProtocol,C_uwNetInvalidChannel);
      return eErrorReturned;
    }
    /* recipient is not in the route table */
    else
    {
      /* build a new message to return the initial one to its sender */
      p_stNewMessage = (tdstNetMessage *) pMalloc(2 * sizeof(tdstNetMessage) + pstMes->uwMessageSizeInBytes);
      if(p_stNewMessage)
      {
        p_stNewMessage->eMessageType = E_Net_mt_SysUnknownRecipient;
        p_stNewMessage->uxRecipientId = pstMes->uxSenderId;
        p_stNewMessage->uxSenderId = gs_ulLocalPlayerId;
        p_stNewMessage->uxPriority = NetLib_C_uxMaxPriority;
        p_stNewMessage->uxReplaceType = 0;
        p_stNewMessage->uxReplace = 0;
        /* The size of the new message is the old size plus the header size :*/
        p_stNewMessage->uwMessageSizeInBytes = pstMes->uwMessageSizeInBytes + sizeof(tdstNetMessage);
        g_pfn_vNetMemcpy(p_stNewMessage + 1, pstMes, p_stNewMessage->uwMessageSizeInBytes);
 
        /* free the old message */
        vFree(pstMes);
 
        /* if the recipient (initial sender) is in the route table */
        if ((p_stNewMessage->uxRecipientId != C_uxNetInvalidId)&&
            ((wIndexInRouteTable = wLevel1IndexInRouteTable(p_stNewMessage->uxRecipientId)) != -1))
        {
          eErrorReturned = eLevel1SendMessage(p_stNewMessage,E_Net_pr_InvalidProtocol,C_uwNetInvalidChannel);
          if(eErrorReturned != NetLib_E_es_NoError)
          {
            vFree((tdpPointer) p_stNewMessage);
          }
        }
        else
        /* the recipient is not in the route table */
        {
          eErrorReturned = eLevel1SendMessage(p_stNewMessage,eProt,uwChannel);
          if(eErrorReturned != NetLib_E_es_NoError)
          {
            vFree((tdpPointer)p_stNewMessage);
          }
        }
        return NetLib_E_es_NoError;
      }
      else return NetLib_E_es_NotEnoughMemory;
    }
  }
  else return NetLib_E_es_ShouldNotReach;
}
 
/*****************************************************************************
 * Description: eLevel1HandleSysMsgFullBuffer
 * Handle an incoming message saying that its body could not be retrieved by
 * its original recipient because of buffer shortage. What we have to do is
 * either reemit the message if we were the initial sender, or route the
 * warning to the initial sender.
 *****************************************************************************
 * Input:   p_stIOSystemCell, pointer to the system message structure.
 * Output:  an error condition depending on the handling's result.
 *****************************************************************************
 * Creation Date: April 3, 1996         Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date:                                Author:
 ****************************************************************************/
NetLib_tdeErrorStatus eLevel1HandleSysMsgFullBuffer(tdstNetMessage *pstMes,tdeNetProtocol eProt,tduwNetChannel uwChannel)
{
  short wIndexInRouteTable;
  NetLib_tdeErrorStatus eErrorReturned;
 
  /* if the recipient is the local player and the sender is in the route table */
  if ((pstMes->uxRecipientId == gs_ulLocalPlayerId) &&
      ((wIndexInRouteTable=wLevel1IndexInRouteTable(pstMes->uxSenderId))!=-1))
  {
    if (gs_ucReemissionMode /* in reemission mode : the message is resent*/)
    {
      /*
       * we have to make sure that the address of the re-sent message is the
       * start address of a memory block, else it would not be freed after
       * being sent (which is of the sending machanism's responsibility).
       * to solve this we can either free the head of the block (which is
       * time consuming), create a new message from scratch (which also
       * requires to copy the full message body), or move the initial message
       * to the top of the memory block, which is much simpler.
       */
      memmove(pstMes,pstMes+1,sizeof(tdstNetMessage)+pstMes->uwMessageSizeInBytes);
 
      pstMes->uxRecipientId = pstMes->uxSenderId;
      pstMes->uxSenderId = gs_ulLocalPlayerId;
      eErrorReturned=eLevel1SendMessage(pstMes,E_Net_pr_InvalidProtocol,C_uwNetInvalidChannel);
 
      if (eErrorReturned != NetLib_E_es_NoError)
      {
        /* Free the message */
        vFree(pstMes);
      }
 
      return eErrorReturned;
    }
    else
    { /*gs_ucReemissionMode equals 0 so the message is not reemited*/
      /* we dont care, the message is lost forever... */
      vFree(pstMes);
      return NetLib_E_es_NoError;
    }
  }
  else return eLevel1RouteSysMsg(pstMes,eProt,uwChannel);
}
 
/*****************************************************************************
 * Description: eLevel1FillNewRouteCell
 * Allocate memory and initialize the route structure for a player so that
 * incoming, outgoing and transiting messages can be handled for the specified
 * player.
 *****************************************************************************
 * Input:   p_stRouteCell, pointer to the structure to initialize.
 *          ulPlayerId, identification of the player.
 *          eProtocol, uwChannel, physical route information to know which
 *          physical channel is to be used to address this player.
 * Output:  an error condition depending on the actions' results.
 *****************************************************************************
 * Creation Date: April 3, 1996         Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date: April 16, 1996                 Author: Albert Pais
 * Adding some errors management
 ****************************************************************************/
NetLib_tdeErrorStatus eLevel1FillNewRouteCell(tdstRouteCell *p_stRouteCell,NetLib_tduxPlayerId ulPlayerId,tdeNetProtocol eProtocol,tduwNetChannel uwChannel)
{
  /* fill the route and identification fields */
  p_stRouteCell->ulPlayerId = ulPlayerId;
  p_stRouteCell->eProtocol = eProtocol;
  p_stRouteCell->uwChannel = uwChannel;
 
  return NetLib_E_es_NoError;
}
 
/*****************************************************************************
 * Description: eLevel1AddNewSession
 * Adds the session described by the incoming system message in the internal
 * active session database.
 *****************************************************************************
 * Input:   p_stIOSystemCell, the incoming message.
 * Output:  an error condition.
 *****************************************************************************
 * Creation Date: April 3, 1996         Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date: April 16, 1996                 Author: Albert Pais
 * Adding some error management
 ****************************************************************************/
NetLib_tdeErrorStatus eLevel1AddNewSession(tdstNetMessage *pstMes,tdeNetProtocol eProt,tduwNetChannel uwChannel)
{
  tdstSessionCell *p_stNewSessionCell, *p_stCurrentSessionCell, *p_stPrevSessionCell;
  NetLib_tduxSessionId ulNewSessionId;
  tdpPointer pMessageBody;
  unsigned short uwIndex;
 
  /* read the session identifier from the message body */
  pMessageBody = (tdpPointer) (pstMes + 1);
  ulNewSessionId = *((NetLib_tduxSessionId *)pMessageBody);
  pMessageBody+=sizeof(NetLib_tduxSessionId);
  /*
   * scan all previous answers we got about active sessions.
   * if an answer with the same session id was already received,
   * we simply return an error message.
   */
  p_stPrevSessionCell = (tdstSessionCell *)C_pNull;
  p_stCurrentSessionCell = gs_p_stSessionCell;
  while (p_stCurrentSessionCell)
  {
    if (p_stCurrentSessionCell->ulSessionId == ulNewSessionId)
      return NetLib_E_es_SessionIdAlreadyAcknowledged;
 
    p_stPrevSessionCell = p_stCurrentSessionCell;
    p_stCurrentSessionCell = p_stCurrentSessionCell->p_stNextSessionCell;
  }
 
  /*
   * here we know that the session descriptor we just received is
   * not in our internal database.
   */
 
  /* allocate a new session descriptor structure */
  p_stNewSessionCell = (tdstSessionCell *) pMalloc(sizeof(tdstSessionCell));
  if (!p_stNewSessionCell) return NetLib_E_es_NotEnoughMemory;
 
  /* and fill it with data we read from the message body */
  p_stNewSessionCell->ulSessionId = ulNewSessionId;
  p_stNewSessionCell->uwMaxNumberOfPlayers = (*((unsigned short *)pMessageBody))-1;
  pMessageBody+=sizeof(unsigned short);
  p_stNewSessionCell->uwNumberOfPlayers = *((unsigned short *)pMessageBody);
  pMessageBody+=sizeof(unsigned short);
  p_stNewSessionCell->d_stRouteCell = (tdstRouteCell*)C_pNull;
 
  /* there is no following structure in the linked list */
  p_stNewSessionCell->p_stNextSessionCell = (tdstSessionCell *)C_pNull;
 
  /* allocate route info pointer array for the new session's players */
  p_stNewSessionCell->d_stRouteCell=(tdstRouteCell *)pMalloc(p_stNewSessionCell->uwMaxNumberOfPlayers * sizeof(tdstRouteCell));
  if (!(p_stNewSessionCell->d_stRouteCell))
  {
    vFree((tdpPointer) p_stNewSessionCell);
    return NetLib_E_es_NotEnoughMemory;
  }
 
  /* and fill it with data from the message body */
  for (uwIndex=0;uwIndex<p_stNewSessionCell->uwNumberOfPlayers;uwIndex++,
       pMessageBody+=sizeof(NetLib_tduxPlayerId))
  {
    /* store the player id */
    if(eLevel1FillNewRouteCell(&p_stNewSessionCell->d_stRouteCell[uwIndex],
                               *((NetLib_tduxPlayerId *)pMessageBody),eProt,uwChannel
                              ) != NetLib_E_es_NoError)
    {
      vFree((tdpPointer)p_stNewSessionCell->d_stRouteCell);
      vFree((tdpPointer)p_stNewSessionCell);
      return NetLib_E_es_NotEnoughMemory;
    }/* No more problem */
  }
 
  for (uwIndex=p_stNewSessionCell->uwNumberOfPlayers;uwIndex<p_stNewSessionCell->uwMaxNumberOfPlayers;uwIndex++)
  {
    p_stNewSessionCell->d_stRouteCell[uwIndex].ulPlayerId = C_uxNetInvalidId;
    p_stNewSessionCell->d_stRouteCell[uwIndex].eProtocol = E_Net_pr_InvalidProtocol;
    p_stNewSessionCell->d_stRouteCell[uwIndex].uwChannel = C_uwNetInvalidChannel;
  }
 
  /* insert the new session cell at the end of the queue */
  p_stNewSessionCell->p_stNextSessionCell = gs_p_stSessionCell;
  gs_p_stSessionCell = p_stNewSessionCell;
 
  return NetLib_E_es_NoError;
}
 
/*****************************************************************************
 * Description: eLevel1BuildRouteInfo
 * Changes the route information for a player when we receive a message
 * directly from him.
 *****************************************************************************
 * Input:   p_stIOSystemCell, pointer to the received message.
 * Output:  an error condition.
 *****************************************************************************
 * Creation Date: April 3, 1996         Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date: April 16, 1996                 Author: Albert Pais
 * Some changes to manage errors and avoid too long debuging
 ****************************************************************************/
NetLib_tdeErrorStatus eLevel1BuildRouteInfoOfSessionPlayer(tdstNetMessage *pstMes,tdeNetProtocol eProt,tduwNetChannel uwChannel)
{
  unsigned short uwIndex;
  /*
   * scan the player list of this session for the identification number of
   * the sender of this message
   */
  uwIndex = 0;
  while((uwIndex < gs_uwMaxNumberOfRemotePlayers)&&
        (gs_d_stRouteTable[uwIndex].ulPlayerId!= pstMes->uxSenderId))
    uwIndex++;
  /* if the sender is in the route table of this session */
  if (uwIndex<gs_uwMaxNumberOfRemotePlayers)
  {
    /* update the physical path for this player */
    gs_d_stRouteTable[uwIndex].eProtocol = eProt;
    gs_d_stRouteTable[uwIndex].uwChannel = uwChannel;
    return NetLib_E_es_NoError;
  }
  return NetLib_E_es_UnknownSenderId;
}
 
NetLib_tdeErrorStatus eLevel1BuildRouteInfo(tdstNetMessage *pstMes,tdeNetProtocol eProt,tduwNetChannel uwChannel)
{
  tdstSessionCell *p_stCurrentSessionCell;
  NetLib_tduxSessionId ulNewSessionId;
  tdpPointer pMessageBody;
  unsigned short uwIndex;
 
  /* read the session identifier from the message body */
  pMessageBody = (tdpPointer) (pstMes + 1);
  ulNewSessionId = *((unsigned char *)pMessageBody);
 
  /*
   * scan all previous answers we got about active sessions.
   * if an answer with the same session id was already received,
   * lets stop there.
   */
  p_stCurrentSessionCell = gs_p_stSessionCell;
  while ((p_stCurrentSessionCell)&&(p_stCurrentSessionCell->ulSessionId != ulNewSessionId))
  {
    p_stCurrentSessionCell = p_stCurrentSessionCell->p_stNextSessionCell;
  }
 
  /*
   * if no session information about the session this message describes
   * is found ,this is an error
   */
  if (!p_stCurrentSessionCell) return NetLib_E_es_UnknownSessionId;
 
  /*
   * scan the player list of this session for the identification number of
   * the sender of this message
   */
  uwIndex = 0;
  while((uwIndex < p_stCurrentSessionCell->uwMaxNumberOfPlayers)&&
        (p_stCurrentSessionCell->d_stRouteCell[uwIndex].ulPlayerId!= pstMes->uxSenderId))
   uwIndex++;
  /* if the sender is in the route table of this session */
  if (uwIndex<p_stCurrentSessionCell->uwMaxNumberOfPlayers)
  {
    /* update the physical path for this player */
    p_stCurrentSessionCell->d_stRouteCell[uwIndex].eProtocol = eProt;
    p_stCurrentSessionCell->d_stRouteCell[uwIndex].uwChannel = uwChannel;
    return NetLib_E_es_NoError;
  }
  /* if we arrive here, no entry for this player was found in the session's route */
  return NetLib_E_es_UnknownSenderId;
}
 
/*****************************************************************************
 * Description: eLevel1UpdateRouteInfo
 *****************************************************************************
 * Input: p_stIOSystemCell, pointer to the received message.
 * Output: an error code
 *****************************************************************************
 * Creation Date:                       Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date:                                Author:
 ****************************************************************************/
NetLib_tdeErrorStatus eLevel1UpdateRouteInfo(tdstNetMessage *pstMes,tdeNetProtocol eProt,tduwNetChannel uwChannel)
{
  tdstSessionCell *p_stCurrentSessionCell, *p_stPrevSessionCell;
  NetLib_tduxSessionId ulNewSessionId;
  unsigned short uwNewNumberOfPlayers;
  tdpPointer pMessageBody;
 
  /* read the session identifier from the message body */
  pMessageBody = (tdpPointer) (pstMes + 1);
  ulNewSessionId = *((NetLib_tduxSessionId*)pMessageBody);
  pMessageBody+=sizeof(NetLib_tduxSessionId);
 
  /* scan all previous answers we got about active sessions.
   * if an answer with the same session id was already received,
   * lets stop there. */
  p_stPrevSessionCell = (tdstSessionCell *)C_pNull;
  p_stCurrentSessionCell = gs_p_stSessionCell;
  while ((p_stCurrentSessionCell)&&(p_stCurrentSessionCell->ulSessionId != ulNewSessionId))
  {
    p_stPrevSessionCell = p_stCurrentSessionCell;
    p_stCurrentSessionCell = p_stCurrentSessionCell->p_stNextSessionCell;
  }
 
  /* if no session information about the session this message describes
   * is found ,this is an error */
  if (!p_stCurrentSessionCell) return NetLib_E_es_UnknownSessionId;
  else
  {
    typedef struct tdstPlayerMix_
    {
      NetLib_tduxPlayerId ulPlayerId;
      unsigned short uwIndexInOldRoute;
      unsigned char ucOldNew;
    }tdstPlayerMix;
 
    tdstPlayerMix *d_stPlayerMix;
    unsigned short uwNbMixedPlayers, uwOldPlayerIndex, uwNewPlayerIndex, uwRouteIndex;
    unsigned char b_ucInNewList;
 
    /* skip max number of players */
    pMessageBody=(tdpPointer)(((unsigned short *)pMessageBody)+1);
    /* read number of players listed in the message */
    uwNewNumberOfPlayers = *((unsigned short*)pMessageBody);
    pMessageBody+=sizeof(unsigned short);
 
    /* allocate a few structures... */
    d_stPlayerMix = (tdstPlayerMix *)pMalloc(sizeof(tdstPlayerMix)*(uwNewNumberOfPlayers + p_stCurrentSessionCell->uwNumberOfPlayers));
    if(!d_stPlayerMix) return NetLib_E_es_NotEnoughMemory;
 
    /* read the list of player ids in the message */
    for (uwNbMixedPlayers=0;uwNbMixedPlayers < uwNewNumberOfPlayers;uwNbMixedPlayers ++)
    {
      /* read the player id */
      d_stPlayerMix[uwNbMixedPlayers].ulPlayerId = *((NetLib_tduxPlayerId*)pMessageBody);
      pMessageBody+=sizeof(NetLib_tduxPlayerId);
      /* the id was seen in the 'new' list */
      d_stPlayerMix[uwNbMixedPlayers].ucOldNew = 0x0F;
    }
 
    /* scan player ids of the 'old' list */
    for (uwOldPlayerIndex=0;uwOldPlayerIndex<p_stCurrentSessionCell->uwNumberOfPlayers;uwOldPlayerIndex ++)
    {
      b_ucInNewList = 0;
      /* look if they appear in the 'new' list */
      for (uwNewPlayerIndex=0;uwNewPlayerIndex<uwNewNumberOfPlayers;uwNewPlayerIndex++)
      {
        if (p_stCurrentSessionCell->d_stRouteCell[uwOldPlayerIndex].ulPlayerId
            == d_stPlayerMix[uwNewPlayerIndex].ulPlayerId)
        {
          /* remember the index of the player id in the old route array */
          d_stPlayerMix[uwNewPlayerIndex].uwIndexInOldRoute = uwOldPlayerIndex;
          /* this entry in the 'new' list is also 'old' */
          d_stPlayerMix[uwNewPlayerIndex].ucOldNew |= 0xF0;
          b_ucInNewList = 1;
          break;
        }
      }
      /* if this 'old' player id is not in the 'new' list */
      if(!b_ucInNewList)
      {
        /* remember the index of the player id in the old route array */
        d_stPlayerMix[uwNbMixedPlayers].uwIndexInOldRoute=uwOldPlayerIndex;
        /* add the entry, saying that this is an 'old' player id */
        d_stPlayerMix[uwNbMixedPlayers].ulPlayerId=p_stCurrentSessionCell->d_stRouteCell[uwOldPlayerIndex].ulPlayerId;
        d_stPlayerMix[uwNbMixedPlayers].ucOldNew = 0xF0;
        uwNbMixedPlayers ++;
      }
    }
    /* now all players, old, new and both are listed in the PlayerMix array.
     * the exclusively new have a ucOldNew = 0x0F
     * the exclusively old have a ucOldNew = 0xF0
     * those is both lists have a ucOldNew = 0xFF */
 
    /* scan the mixed list */
    for (uwNewPlayerIndex = 0;uwNewPlayerIndex < uwNbMixedPlayers;uwNewPlayerIndex ++)
    {
      switch (d_stPlayerMix[uwNewPlayerIndex].ucOldNew)
      {
        /* if this is player id was exclusively in the old list */
        case 0xF0:
          p_stCurrentSessionCell->d_stRouteCell[d_stPlayerMix[uwNewPlayerIndex].uwIndexInOldRoute].ulPlayerId = C_uxNetInvalidId;
          p_stCurrentSessionCell->d_stRouteCell[d_stPlayerMix[uwNewPlayerIndex].uwIndexInOldRoute].eProtocol = E_Net_pr_InvalidProtocol;
          break;
 
        /* if this is player id was exclusively in the new list */
        case 0x0F:
          /* look for a free slot in the route table of the session */
          for (uwRouteIndex=0;uwRouteIndex<p_stCurrentSessionCell->uwMaxNumberOfPlayers;uwRouteIndex++)
            if (p_stCurrentSessionCell->d_stRouteCell[uwRouteIndex].ulPlayerId == C_uxNetInvalidId)
            {
              /* store the route for the 'new' player in this slot */
              eLevel1FillNewRouteCell(&p_stCurrentSessionCell->d_stRouteCell[uwRouteIndex],
                                      d_stPlayerMix[uwNewPlayerIndex].ulPlayerId,
                                      eProt,uwChannel);
              break;
            }
          break;
 
        /* if this is player id was in both lists */
        case 0xFF:
          /* update it */
          p_stCurrentSessionCell->d_stRouteCell[d_stPlayerMix[uwNewPlayerIndex].uwIndexInOldRoute].eProtocol = eProt;
          p_stCurrentSessionCell->d_stRouteCell[d_stPlayerMix[uwNewPlayerIndex].uwIndexInOldRoute].uwChannel = uwChannel;
          break;
 
        /* if this player was in none of the lists */
        default:
          /* theoretically impossible... */
          break;
      }
    }
    /* we no longer need the mixed list of player identification structures */
    vFree((tdpPointer) d_stPlayerMix);
  }
  return NetLib_E_es_NoError;
}
 
/*****************************************************************************
 * Description: vLevel1SetPlayerId
 * Sets the internal variable telling the identification of the local player.
 *****************************************************************************
 * Input:   ulPlayerId, the new identification of the local player.
 * Output:  none.
 *****************************************************************************
 * Creation Date: April 4, 1996         Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date:                                Author:
 ****************************************************************************/
void vLevel1SetPlayerId(NetLib_tduxPlayerId ulPlayerId)
{
  gs_ulLocalPlayerId = ulPlayerId;
}
 
/*****************************************************************************
 * Description: vLevel1SetSessionId
 * Sets the internal variable telling the identification of the session.
 *****************************************************************************
 * Input:   ulSessionId, the new identification of the local player.
 * Output:  none.
 *****************************************************************************
 * Creation Date: April 4, 1996         Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date:                                Author:
 ****************************************************************************/
void vLevel1SetSessionId(NetLib_tduxPlayerId ulSessionId)
{
  gs_ulSessionId = ulSessionId;
}
 
/*****************************************************************************
 * Description: vLevel1DestroySessionCellList
 * Free all memory used to store information about the routes of the
 * sessions who replied to the presence request.
 *****************************************************************************
 * Creation Date: April 4, 1996         Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date:                                Author:
 ****************************************************************************/
void vLevel1DestroySessionCellList(void)
{
  tdstSessionCell *p_stSessionCell;
  unsigned short uwPlayerIndex;
 
  /* while there is a session */
  while (gs_p_stSessionCell)
  {
    /* cut the head from the linked list */
    p_stSessionCell = gs_p_stSessionCell;
    gs_p_stSessionCell = gs_p_stSessionCell->p_stNextSessionCell;
 
    /* scan the route table for this session */
    for (uwPlayerIndex=0;uwPlayerIndex<p_stSessionCell->uwMaxNumberOfPlayers;uwPlayerIndex++)
    {
      /* if this slot contains info about a player of the session */
      if (p_stSessionCell->d_stRouteCell[uwPlayerIndex].ulPlayerId != C_uxNetInvalidId)
      {
        /* Close the channel used :*/
        vLevel1NetCloseChannel(p_stSessionCell->d_stRouteCell[uwPlayerIndex].eProtocol,
                               p_stSessionCell->d_stRouteCell[uwPlayerIndex].uwChannel,
                               p_stSessionCell->d_stRouteCell[uwPlayerIndex].ulPlayerId);
      }
    }
 
    /* free the route information array */
    vFree((tdpPointer) p_stSessionCell->d_stRouteCell);
    vFree((tdpPointer)p_stSessionCell);
  }
}
 
/*****************************************************************************
 * Description: eLevel1StripSessionList
 * Remove all session information but for the session of specified id.
 *****************************************************************************
 * Creation Date:                       Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date:                                Author:
 ****************************************************************************/
NetLib_tdeErrorStatus eLevel1StripSessionList(NetLib_tduxSessionId ulSessionIdToKeep)
{
  tdstSessionCell *p_stSessionCell, *p_stPrevSessionCell;
 
  /* start at the beginning of the linked list */
  p_stPrevSessionCell = (tdstSessionCell*)C_pNull;
  p_stSessionCell = gs_p_stSessionCell;
 
  /* scan the list for a session with the specified identification */
  while (p_stSessionCell && p_stSessionCell->ulSessionId != ulSessionIdToKeep)
  {
    /* remember the session we come from */
    p_stPrevSessionCell = p_stSessionCell;
    /* go to the next session */
    p_stSessionCell = p_stSessionCell->p_stNextSessionCell;
  }
 
  /* if we found one */
  if (p_stSessionCell)
  {
    /*
     * if it is not the first session in the list, make the previous
     * session skip the one we found, else make the list start at the
     * session following the one we chose.
     */
    if  (p_stPrevSessionCell)
      p_stPrevSessionCell->p_stNextSessionCell = p_stSessionCell->p_stNextSessionCell;
    else
      gs_p_stSessionCell = p_stSessionCell->p_stNextSessionCell;
 
    /* keep reference on the session's route table in our internal table */
    if(gs_d_stRouteTable)
    {
      vFree((tdpPointer)gs_d_stRouteTable);
      gs_d_stRouteTable = (tdstRouteCell *)C_pNull;
    }
    gs_d_stRouteTable = p_stSessionCell->d_stRouteCell;
    gs_uwMaxNumberOfRemotePlayers = p_stSessionCell->uwMaxNumberOfPlayers;
    vFree(p_stSessionCell);
    /* now the session we want is no longer in the session list */
    vLevel1DestroySessionCellList();
 
    return NetLib_E_es_NoError;
  }
  else return NetLib_E_es_UnknownSessionId;
}
 
/*****************************************************************************
 * Description: eLevel1HandleSysMsgConnectRequest
 * stores information about a player requesting to join the session.
 *****************************************************************************
 * Input:   p_stIOSystemCell, pointer to the system message.
 * Output:  an error condition.
 *****************************************************************************
 * Creation Date: April 4, 1996         Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date:                                Author:
 ****************************************************************************/
NetLib_tdeErrorStatus eLevel1HandleSysMsgConnectRequest(tdstNetMessage *pstMes,tdeNetProtocol eProt,tduwNetChannel uwChannel)
{
  short wFreeSlot;
 
  /* if no route info about the sender of the message is found */
  if (wLevel1IndexInRouteTable(pstMes->uxSenderId) == -1)
  {
    /* get a free slot index */
    if ((wFreeSlot = wLevel1IndexInRouteTable(C_uxNetInvalidId)) != -1)
    {
      /* setup the information for this new route info */
      gs_d_stRouteTable[wFreeSlot].ulPlayerId = pstMes->uxSenderId;
      gs_d_stRouteTable[wFreeSlot].eProtocol = eProt;
      gs_d_stRouteTable[wFreeSlot].uwChannel = uwChannel;
 
      return NetLib_E_es_NoError;
    }
    else return NetLib_E_es_RouteTableIsFull;
  }
  else return NetLib_E_es_IdAlreadyInRouteTable;
}
 
/*****************************************************************************
 * Description: eLevel1DisconnectRemotePlayer
 * Remove route information about the specified player.
 *****************************************************************************
 * Input:   ulRemotePlayerId, identification of the player to disconnect.
 * Output:  an error condition.
 *****************************************************************************
 * Creation Date: April 4, 1996         Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date:                                Author:
 ****************************************************************************/
NetLib_tdeErrorStatus eLevel1DisconnectRemotePlayer(NetLib_tduxPlayerId ulRemotePlayerId)
{
  short wIndexInRouteTable;
  short wCurrentIndex;
  tdstRouteCell *p_stRouteCell;
 
  /* if the remote player is in the route table */
  if ((wIndexInRouteTable = wLevel1IndexInRouteTable(ulRemotePlayerId)) != -1)
  {
    p_stRouteCell = &gs_d_stRouteTable[wIndexInRouteTable];
 
    /* Check if no one use any more this protocol :*/
    /* Ask the corresponding protocol to close the channel */
    wCurrentIndex = 0;
    while ((wCurrentIndex<gs_uwMaxNumberOfRemotePlayers)&&
           (gs_d_stRouteTable[wCurrentIndex].eProtocol !=p_stRouteCell->eProtocol)&&
           (gs_d_stRouteTable[wCurrentIndex].uwChannel !=p_stRouteCell->uwChannel))
      wCurrentIndex++;
 
    if ((wCurrentIndex>=gs_uwMaxNumberOfRemotePlayers)&&
        (p_stRouteCell->eProtocol != E_Net_pr_InvalidProtocol))
    {
      vLevel1NetCloseChannel(p_stRouteCell->eProtocol,p_stRouteCell->uwChannel,p_stRouteCell->ulPlayerId);
    }
    /* make the route info invalid */
    p_stRouteCell->ulPlayerId = C_uxNetInvalidId;
    p_stRouteCell->eProtocol = E_Net_pr_InvalidProtocol;
 
    return NetLib_E_es_NoError;
  }
  else return NetLib_E_es_UnknownPlayerId;
}
 
/*****************************************************************************
 * Description: eLevel1GetSysMsg
 * retrieves the next incoming system message in the system message queue.
 *****************************************************************************
 * Input:   pointer to the variable that will store the address of the
 *          retrieved message.
 * Output:  an error condition depending on the action's success.
 ****************************************************************************/
NetLib_tdeErrorStatus eLevel1GetSysMsg(tdstNetMessage **ppstMes,tdeNetProtocol *pProt,tduwNetChannel *pChannel)
{
  tdstNetIter stIter;
  tdstNetL1Message *pL1Mes;
  pL1Mes=(tdstNetL1Message*)pNetList2IterInit(&stIter,&gs_stIncomingSysMsgList);
  if (pL1Mes)
  {
    *ppstMes=pL1Mes->pMessage;
    if (pL1Mes->dstTabDest)
    {
      *pProt=pL1Mes->dstTabDest->eProtocol;
      *pChannel=pL1Mes->dstTabDest->uwChannel;
    }
    else
    {
      *pProt=E_Net_pr_InvalidProtocol;
      *pChannel=E_Net_pr_InvalidProtocol;
    }
    vFree(pL1Mes->dstTabDest);
    pNetList2DeleteElem(&stIter);
    return NetLib_E_es_NoError;
  }
  else return NetLib_E_es_FIFOIsEmpty;
}
 
/*//////////////////////////////////////////////////////////////////////////////
Description : eLevel1NetUpdateSendStatus
Makes the correspondance beetween a NetLib_tdeErrorStatus and a tdeNetTransferStatus
in order to update a send status
////////////////////////////////////////////////////////////////////////////////
Input : NetLib_tdeErrorStatus
////////////////////////////////////////////////////////////////////////////////
Output : tdeNetTransferStatus
////////////////////////////////////////////////////////////////////////////////
Creation date : June 17,96
Author : Albert Pais
//////////////////////////////////////////////////////////////////////////////*/
tdeNetTransferStatus eLevel1NetUpdateSendStatus(NetLib_tdeErrorStatus eErrorStatus)
{
  switch(eErrorStatus)
  {
    case NetLib_E_es_ChannelIsInvalid:
      return E_ts_Invalid;
    case NetLib_E_es_InvalidChannel:
      return E_ts_InvalidChannel;
    case NetLib_E_es_ChannelUninitialized:
      return E_ts_ChannelUnitialized;
    case NetLib_E_es_NoError :
      return E_ts_OK;
    case NetLib_E_es_EmissionInProgress:
      return E_ts_EmissionInProgress;
    case NetLib_E_es_SendSocketFailure :
    case NetLib_E_es_MessagePartlySent :
      return E_ts_PacketFailure;
    default :
      return E_ts_OK;
  }
}
 
/*****************************************************************************
 * Description: vLevel1OutgoingNetEngine
 * Handles outgoing messages queues. Initiates level 0 emission of a new
 * message if a channel is free.
 * Random version.
 *****************************************************************************
 * Input:   none
 * Output:  none
 ****************************************************************************/
void vLevel1OutgoingNetEngine(void)
{
  unsigned short BitProtocol=0,BitAllProt;
  tdstNetIter stIter;
  tdstNetL1Message *pstL1Msg;
  tdstNetMessage *pSndMsgBroad,*pstSndMsg;
  tdstNetProtocolInterface *p_stProtocolInterface;
  NetLib_tdeErrorStatus eErrorReturned;
  int bMesSent;
  tdeNetProtocol eProtocol;
  tduwNetChannel uwChannel;
  short i;
  
  BitAllProt=(0xFFFF>>(16-gs_uwNumberOfProtocol));
  pstL1Msg=(tdstNetL1Message *)pNetList2IterInit(&stIter,&gs_stOutgoingMsgList);
  while ((pstL1Msg) && (BitProtocol!=BitAllProt))
  {
    pstSndMsg=pstL1Msg->pMessage;
    for (i=0;i<pstL1Msg->uwNbrDest;)
    {
      bMesSent=0;
      eProtocol=pstL1Msg->dstTabDest[i].eProtocol;
      if (!(BitProtocol & (1<<eProtocol)))
      {
        uwChannel=pstL1Msg->dstTabDest[i].uwChannel;
        p_stProtocolInterface=p_stLevel1GetInterfaceOfProtocol(eProtocol);
        eErrorReturned=eLevel1NetUpdateSendStatus(p_stProtocolInterface->fn_eQueryChannelStatus(uwChannel));
        if (eErrorReturned == E_ts_OK)
        {
          pSndMsgBroad=(tdstNetMessage *)pMalloc(sizeof(tdstNetMessage)+pstSndMsg->uwMessageSizeInBytes);
          if (!pSndMsgBroad) break;
          memcpy(pSndMsgBroad,pstSndMsg,sizeof(tdstNetMessage)+pstSndMsg->uwMessageSizeInBytes);
          eErrorReturned=p_stProtocolInterface->fn_eSendData(uwChannel,(tdpPointer)pSndMsgBroad);
          if(eErrorReturned==NetLib_E_es_NoError)
          {
            memmove(pstL1Msg->dstTabDest+i,pstL1Msg->dstTabDest+i+1,
                    (pstL1Msg->uwNbrDest-i-1)*sizeof(tdstProtChannel));
            pstL1Msg->uwNbrDest--;
            bMesSent=1;
          }
          else
          {
            vFree(pSndMsgBroad);
            BitProtocol|=(1<<eProtocol);
          }
        }
      }
      if (!bMesSent) i++;
    }
 
    if (pstL1Msg->uwNbrDest==0)
    {
      vFree(pstSndMsg);
      vFree(pstL1Msg->dstTabDest);
      pstL1Msg=(tdstNetL1Message *)pNetList2DeleteElem(&stIter);
    }
    else pstL1Msg=(tdstNetL1Message *)pNetList2Next(&stIter);
  }
}
 
/*****************************************************************************
 * Description: eLevel1BackfireSysMessage
 * puts in the outgoing system message queue a system message of the specified
 * code with a body containing the optional offending game engine message.
 *****************************************************************************
 * Input:   eMessageType, type of system message to output.
 *          eProtocol, protocol where the offending message came from.
 *          uwChannel, channel where the offending message came from.
 *          p_stInitialMessage, pointer to the offending incoming message.
 * Output:  an error condition depending on the action's results.
 *****************************************************************************
 * Creation Date: April 4, 1996         Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date:                                Author:
 ****************************************************************************/
NetLib_tdeErrorStatus eLevel1BackfireSysMessage(tdeNetMessageType eMessageType,tdeNetProtocol eProtocol,tduwNetChannel uwChannel,tdstNetMessage *p_stInitialMessage)
{
  tdstNetMessage *p_stSysMessage;
  NetLib_tdeErrorStatus eErrorReturned;
 
  /*
   * allocate a system cell to store the message in the outgoing
   * system message queue.
   */
  /* prepare the message body to send back the whole incoming message */
  p_stSysMessage = (tdstNetMessage *) pMalloc(2 * sizeof(tdstNetMessage) + p_stInitialMessage->uwMessageSizeInBytes);
  if(!p_stSysMessage)
  {
    vFree((tdpPointer)p_stInitialMessage);
    return NetLib_E_es_NotEnoughMemory;
  }
 
  /* fill the system message body */
  p_stSysMessage->eMessageType = eMessageType;
  p_stSysMessage->uxRecipientId = p_stInitialMessage->uxSenderId;
  p_stSysMessage->uwMessageSizeInBytes = sizeof(tdstNetMessage) + p_stInitialMessage->uwMessageSizeInBytes;
  g_pfn_vNetMemcpy(p_stSysMessage + 1, p_stInitialMessage, p_stSysMessage->uwMessageSizeInBytes);
 
  /* erase the offending incoming message */
  vFree((tdpPointer) p_stInitialMessage);
  p_stInitialMessage = (tdstNetMessage*)C_pNull;
 
  eErrorReturned = eLevel1SendMessage(p_stSysMessage,eProtocol,uwChannel);
  if(eErrorReturned!=NetLib_E_es_NoError) vFree(p_stSysMessage);
  return eErrorReturned;
}
 
/*//////////////////////////////////////////////////////////////////////////////
Description : vNetL1OrderDest
When a message "NewL1Mes" replace an other "OldL1Mes", the waiting destinations for
the old message become prioritary in the new message.
Return "NetLib_E_es_False" if an old destination is not in the new message.
Return "NetLib_E_es_True" otherwise.
////////////////////////////////////////////////////////////////////////////////
Input : Old and new NetL1 message
//////////////////////////////////////////////////////////////////////////////*/
static NetLib_tdeErrorStatus eNetL1OrderDest(tdstNetL1Message *pstNewL1Mes,tdstNetL1Message *pstOldL1Mes)
{
  int i,j,p=0;
  tdstProtChannel *pstOldProtChannel,*pstNewProtChannel,varswap;
  pstOldProtChannel=pstOldL1Mes->dstTabDest;
  i=pstOldL1Mes->uwNbrDest;
  while (i--)
  {
    pstNewProtChannel=pstNewL1Mes->dstTabDest+p;
    j=pstNewL1Mes->uwNbrDest-p;
    while (j--)
    {
      if ((pstOldProtChannel->eProtocol==pstNewProtChannel->eProtocol) &&
          (pstOldProtChannel->uwChannel==pstNewProtChannel->uwChannel))
      {
        if (j!=p)
        {
          varswap=pstNewL1Mes->dstTabDest[p];
          pstNewL1Mes->dstTabDest[p]=*pstNewProtChannel;
          *pstNewProtChannel=varswap;
        }
        p++;
        break;
      }
      pstNewProtChannel++;
    }
    if (j<0) return NetLib_E_es_False;
    pstOldProtChannel++;
  }
  return NetLib_E_es_True;
}
 
/*//////////////////////////////////////////////////////////////////////////////
Description : eNetLevel1InsertNewMsg
Insert a new message in the list with priority and replace constraint.
////////////////////////////////////////////////////////////////////////////////
Input : an iterator and a NetL1 message
////////////////////////////////////////////////////////////////////////////////
Output : NetLib_E_es_NoError if no error occured
//////////////////////////////////////////////////////////////////////////////*/
static NetLib_tdeErrorStatus eNetLevel1InsertNewMsg(tdstNetIter *pstIter,tdstNetL1Message *pstL1Message)
{
  long lPosPrior=0;
  tdstNetL1Message *pstL1MsgInt;
  tdstNetMessage *pstInsMsg,*pstCurMsg;
 
  pstInsMsg=pstL1Message->pMessage;
  vNetList2ToEnd(pstIter);
  while (!iNetList2IsFirst(pstIter))
  {
    pstL1MsgInt=(tdstNetL1Message *)pNetList2Prev(pstIter);
    pstCurMsg=pstL1MsgInt->pMessage;
    if ((lPosPrior==0) && (pstCurMsg->uxPriority>=pstInsMsg->uxPriority))
    {
      lPosPrior=ulNetList2PosIter(pstIter)+1;
    }
    if ((pstCurMsg->uxReplace) &&
        (pstCurMsg->uxSenderId == pstInsMsg->uxSenderId) &&
        (pstCurMsg->uxRecipientId == pstInsMsg->uxRecipientId) &&
        (pstCurMsg->eMessageType == pstInsMsg->eMessageType) &&
        (pstCurMsg->uxReplaceType == pstInsMsg->uxReplaceType))
    {
      if (pstCurMsg->uxRecipientId!=C_uxNetInvalidId)
      {
        if (eNetL1OrderDest(pstL1Message,pstL1MsgInt)==NetLib_E_es_True)
        {
          vFree(pstL1MsgInt->dstTabDest);
          vFree(pstCurMsg);
          pNetList2DeleteElem(pstIter);
          lPosPrior=ulNetList2PosIter(pstIter);
          break;
        }
      }
    }
  }
 
  if (ulNetList2NbrElemIter(pstIter)>=gs_uwMaxNbrOfMsgPerList)
  {
    if (ulNetList2NbrElemIter(pstIter)<=lPosPrior) return NetLib_E_es_FIFOIsFull;
    else
    {
      pstL1MsgInt=(tdstNetL1Message *)pNetList2ToLast(pstIter);
      vFree(pstL1MsgInt->pMessage);
      pNetList2DeleteElem(pstIter);
    }
  }
  pNetList2SetPos(pstIter,lPosPrior);
  if (!pNetList2InsertElem(pstIter,pstL1Message,sizeof(tdstNetL1Message)))
    return NetLib_E_es_NotEnoughMemory;
  else return NetLib_E_es_NoError;
}
 
/*//////////////////////////////////////////////////////////////////////////////
Description :eLevel1HandleIncomingMsg
Handles incoming msg
////////////////////////////////////////////////////////////////////////////////
Input : a tdstNetIOSystemCell that contains the information about the message
////////////////////////////////////////////////////////////////////////////////
Output : NetLib_E_es_NoError if no error occured
         another error condition if the reply could not be sent back
//////////////////////////////////////////////////////////////////////////////*/
static NetLib_tdeErrorStatus eLevel1HandleIncomingMsg(tdstNetMessage *p_stReceivedMessage,tdeNetProtocol eProtocol,tduwNetChannel uwChannel)
{
  tdstNetIter stIter;
  tdstNetL1Message stL1Message;
  NetLib_tdeErrorStatus eErrorReturned;
 
  if(eNetCheckMessageBody(p_stReceivedMessage)!=NetLib_E_es_NoError)
  {/* An error inside the message :*/
    vFree(p_stReceivedMessage);
#ifdef NET_USE_DEBUG
          vDebugFormat(Net_C_Debug_NetSer, "NetL1: HandleIncomingMsg: bad checksum");
#endif
 
    return NetLib_E_es_NoError;
  }
 
  /*If the recipient is broadcast, set it to the local player*/
  if(p_stReceivedMessage->uxRecipientId == C_uxNetBroadcastId)
  {
    if (gs_uwMode==NetLib_Mode_Direct)
    {
      NetLib_tduxPlayerId *TabDestPlayer,NbrDestPlayer;
      vLevel1GetBroadcastSource(p_stReceivedMessage->uxSenderId,&TabDestPlayer,&NbrDestPlayer);
      if (NbrDestPlayer)
      {
        tdstNetMessage *pstMes;
        pstMes=(tdstNetMessage *)pMalloc(sizeof(tdstNetMessage)+p_stReceivedMessage->uwMessageSizeInBytes);
        if (pstMes)
        {
          memcpy(pstMes,p_stReceivedMessage,sizeof(tdstNetMessage)+p_stReceivedMessage->uwMessageSizeInBytes);
          eErrorReturned=eLevel1SendMessage(pstMes,E_Net_pr_InvalidProtocol,C_uwNetInvalidChannel);
          if (eErrorReturned!=NetLib_E_es_NoError) vFree(pstMes);
        }
      }
    }
    p_stReceivedMessage->uxRecipientId=gs_ulLocalPlayerId;
  }
  else if(p_stReceivedMessage->eMessageType < E_Net_mt_FirstSysMessage)
  {
    if (p_stReceivedMessage->uxRecipientId!=gs_ulLocalPlayerId)
    {
      if ((p_stReceivedMessage->uxRecipientId != C_uxNetInvalidId) &&
          (wLevel1IndexInRouteTable(p_stReceivedMessage->uxRecipientId) != -1))
      {
        eErrorReturned=eLevel1SendMessage(p_stReceivedMessage,E_Net_pr_InvalidProtocol,C_uwNetInvalidChannel);
        if (eErrorReturned==NetLib_E_es_NoError) return NetLib_E_es_NoError;
      }
#ifdef NET_USE_DEBUG
          vDebugFormat(Net_C_Debug_NetSer, "NetL1: HandleIncomingMsg: unknown recipient");
#endif
 
          /* one could send back an "unknown recipient" system message */
      vFree(p_stReceivedMessage);
      return NetLib_E_es_NoError;
    }
  }
#ifdef NET_USE_DEBUG
          vDebugFormat(Net_C_Debug_NetSer, "NetL1: HandleIncomingMsg: new msg for local host");
#endif
 
  stL1Message.pMessage=p_stReceivedMessage;
  stL1Message.uwNbrDest=1;
  stL1Message.dstTabDest=(tdstProtChannel *)pMalloc(stL1Message.uwNbrDest*sizeof(tdstProtChannel));
  if (!stL1Message.dstTabDest)
  {
    vFree((tdpPointer)p_stReceivedMessage);
    return NetLib_E_es_NotEnoughMemory;
  }
  stL1Message.dstTabDest->eProtocol=eProtocol;
  stL1Message.dstTabDest->uwChannel=uwChannel;
  if(p_stReceivedMessage->eMessageType >= E_Net_mt_FirstSysMessage)
    pNetList2IterInit(&stIter,&gs_stIncomingSysMsgList);
  else pNetList2IterInit(&stIter,&gs_stIncomingEngineMsgList);
  eErrorReturned=eNetLevel1InsertNewMsg(&stIter,&stL1Message);
  if (eErrorReturned!=NetLib_E_es_NoError)
  {
    vFree((tdpPointer)stL1Message.dstTabDest);
    vFree((tdpPointer)p_stReceivedMessage);
        /* send back a "buffer is full" system message*/
        eLevel1BackfireSysMessage(E_Net_mt_SysFullBuffer, eProtocol, uwChannel, p_stReceivedMessage);
  }
  return eErrorReturned;
}
 
/*****************************************************************************
 * Description: vLevel1IncomingNetEngine
 * Reads all incoming messages from the level 0 buffers, and store the full
 * messages in the appropriate incoming queues. Some system messages can
 * also be generated in case of major problem.
 *****************************************************************************
 * Input:   none
 * Output:  none
 *****************************************************************************
 * Creation Date: April 4, 1996         Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date: October 1996                   Author: Albert PAIS
 * -> All the function has been changed
 ****************************************************************************/
static void vLevel1IncomingNetEngine(void)
{
  unsigned char ucCurrentProtocolIndex;
  tdeNetProtocol eProtocol;
  tduwNetChannel uwChannel;
  tduwNetChannel uwChannelReturned;
  tdstNetProtocolInterface *p_stCurrentInterface;
  tdstNetMessage *p_stReceivedMessage;
  NetLib_tdeErrorStatus eReadStatus;
  unsigned short uwCountMsg;
 
  for (ucCurrentProtocolIndex=0;ucCurrentProtocolIndex<gs_uwNumberOfProtocol;ucCurrentProtocolIndex++)
  {
    p_stCurrentInterface = &gs_d_stAvailableProtocolInterfaces[ucCurrentProtocolIndex];
    /* retrieve protocol identifier */
    eProtocol = p_stCurrentInterface->eProtocol;
    /* ask protocol the first channel identifier */
    uwChannel = p_stCurrentInterface->fn_uwStartChannelScan();
 
    /* while there is an available channel for the current protocol */
    while (uwChannel != C_uwNetInvalidChannel)
    {
      /*
       * retreive data that arrived in the specified channel. if these were
       * the last bytes of a message, we get a pointer on the allocated block
       * that contains it.
       */
      uwCountMsg =0;
      do
      {
        uwChannelReturned = uwChannel;
        eReadStatus = p_stCurrentInterface->fn_eReadData(&uwChannelReturned, (tdpPointer *) &p_stReceivedMessage);
        /* if we got a full message in the level 0 buffer */
        if (eReadStatus == NetLib_E_es_NoError && p_stReceivedMessage)
        {
          eReadStatus = eLevel1HandleIncomingMsg(p_stReceivedMessage,eProtocol,uwChannelReturned);
#ifdef NET_USE_DEBUG
                  vDebugFormat(Net_C_Debug_NetSer, "NetL1: vLevel1IncomingNetEngine: eReadStatus: %ld", eReadStatus);
#endif
          uwCountMsg++;
        }
      }while (((eReadStatus) == NetLib_E_es_NoError) /*&& (uwCountMsg<5)*/);
      /* Check if no problems occured : */
      /*if(eReadStatus != NetLib_E_es_NoError)*/
      /* treat the error if it can be*/
 
      /* proceed to next channel of the current protocol */
      uwChannel = p_stCurrentInterface->fn_uwNextChannel(uwChannel);
    }
  }
}
 
/*****************************************************************************
 * Description: iLevel1NetEngine
 * performs all operations to read, write and route messages.
 *****************************************************************************
 * Input:   none
 * Output:  none
 *****************************************************************************
 * Creation Date: April 4, 1996         Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 4, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date:                                Author:
 ****************************************************************************/
int _NET_CALLING_CONV_ iLevel1NetEngine(void)
{
  unsigned char ucCurrentProtocolIndex;
  /* Scans all the protocol and call the NetEngine function :*/
  for(ucCurrentProtocolIndex=0;ucCurrentProtocolIndex<gs_uwNumberOfProtocol;ucCurrentProtocolIndex++)
  {
    if(gs_d_stAvailableProtocolInterfaces[ucCurrentProtocolIndex].fn_vLevel0NetEngine)
      gs_d_stAvailableProtocolInterfaces[ucCurrentProtocolIndex].fn_vLevel0NetEngine();
  }
  vLevel1IncomingNetEngine();
  vLevel1OutgoingNetEngine();
  return 0;
}
 
void vLevel1InitProtocolTable()
 
{         
        gs_d_stAvailableProtocolInterfaces=(tdstNetProtocolInterface *)C_pNull;
        gs_uwNumberOfProtocol=0;
}
 
/*****************************************************************************
 * Description: pstLevel1AddProtocol
 * setup global variables with safe default values.
 *****************************************************************************
 * Input:   none
 * Output:  a pointer on the new protocol. Null if not enought memory
 ****************************************************************************/
tdstNetProtocolInterface *pstLevel1AddProtocol(void)
{
  tdstNetProtocolInterface *dstNewTab;
 
  dstNewTab=(tdstNetProtocolInterface *)pRealloc(
                                gs_d_stAvailableProtocolInterfaces,
                                sizeof(tdstNetProtocolInterface)*(gs_uwNumberOfProtocol+1));
 
  if(dstNewTab) {
          gs_d_stAvailableProtocolInterfaces=dstNewTab;
          vNetFillInvalidProtocolInterface(gs_d_stAvailableProtocolInterfaces +
                                                                                gs_uwNumberOfProtocol);
 
          return gs_d_stAvailableProtocolInterfaces + 
                         gs_uwNumberOfProtocol++;
  } else
          return NULL;
}
 
void vLevel1RemoveProtocol(tdeNetProtocol eProtocol)
 
{
        unsigned short i;
 
        for(i=0; i<gs_uwNumberOfProtocol; i++) {
                if(gs_d_stAvailableProtocolInterfaces[i].eProtocol==eProtocol) {
 
                        if(gs_uwNumberOfProtocol-i-1>0)
                                memmove(&gs_d_stAvailableProtocolInterfaces[i],
                                &gs_d_stAvailableProtocolInterfaces[i+1],
                                (gs_uwNumberOfProtocol-i-1)*sizeof(tdstNetProtocolInterface));
 
                        /* one could reallocate d_astAvailableProtocolInterfaces ... */
 
                        gs_uwNumberOfProtocol--;
 
                        break;
                }
        }
}
 
void vLevel1DeleteProtocolTable()
 
{
        if(gs_d_stAvailableProtocolInterfaces)
                vFree(gs_d_stAvailableProtocolInterfaces);
        gs_uwNumberOfProtocol=0;
}
 
/*****************************************************************************
 * Description: eLevel1GlobalSetup
 * setup global variables with safe default values.
 *****************************************************************************
 * Input:   none
 * Output:  an error code if an error occures
 *****************************************************************************
 * Creation Date: April 9, 1996         Author: Benoit GERMAIN
 *****************************************************************************
 * Modification log:
 * Date: April 9, 1996                  Author: Benoit GERMAIN
 * -> first public release
 * Date: April 15-16, 1996              Author: Albert Pais
 * Adding some initialisations
 ****************************************************************************/
NetLib_tdeErrorStatus eLevel1GlobalSetup(NetLib_tduwMode Mode,short BroadcastIpx)
{
  gs_wBroadcastIpx=BroadcastIpx;
  gs_uwMode=Mode;
 
  /* Initialise route table :*/
  gs_d_stRouteTable = (tdstRouteCell*)C_pNull;
  /* Initialise number of players and max number of players :*/
  gs_uwMaxNumberOfRemotePlayers = 0;
  /* Initialize local player id :*/
  gs_ulLocalPlayerId = C_uxNetInvalidId;
 
  NbrBroadcastSource = 0;
  TabBroadcastSource = NULL;
 
  gs_dcRndList=NULL;
 
  /* initialize the gs_uwMaxNbrOfMsgPerList */
  eLevel1NetSetMaxNbrOfMsgPerList(C_MaxNbrElement);
 
  if (iNetList2Init(&gs_stOutgoingMsgList)) return NetLib_E_es_NotEnoughMemory;
  if (iNetList2Init(&gs_stIncomingSysMsgList)) return NetLib_E_es_NotEnoughMemory;
  if (iNetList2Init(&gs_stIncomingEngineMsgList)) return NetLib_E_es_NotEnoughMemory;
 
  gs_p_stSessionCell = (tdstSessionCell*)C_pNull;
  gs_ucReemissionMode = 0;
 
  NetLib_eProcessLittleBigEndian();
 
  vLevel1InitProtocolTable();
 
  gs_uwNumberOfProtocol=0;
  gs_d_stAvailableProtocolInterfaces=NULL;
 
  return NetLib_E_es_NoError;
}
 
/*//////////////////////////////////////////////////////////////////////////////
Description : vLevel1CopyRouteCell
 Copy a route cell
////////////////////////////////////////////////////////////////////////////////
Input : Src and dest
////////////////////////////////////////////////////////////////////////////////
Output : None
////////////////////////////////////////////////////////////////////////////////
Author : Albert Pais
//////////////////////////////////////////////////////////////////////////////*/
void vLevel1CopyRouteCell(tdstRouteCell *p_stDest,tdstRouteCell *p_stSrc)
{
  p_stDest->ulPlayerId = p_stSrc->ulPlayerId;
  p_stDest->eProtocol = p_stSrc->eProtocol;
  p_stDest->uwChannel = p_stSrc->uwChannel;
}
 
/*//////////////////////////////////////////////////////////////////////////////
Description : NetLib_tdeErrorStatus eLevel1SetMaxNumberOfRemotePlayers
 Sets the maximum number of players.  Initialize data with this information
////////////////////////////////////////////////////////////////////////////////
Input : an unsigned short representing the max number of remote players
////////////////////////////////////////////////////////////////////////////////
Output : NetLib_E_es_NoError if no error occures
         NetLib_E_es_NotEnoughMemory if there was not enough memory to complete the function
         NetLib_E_es_AlreadyTooMuchPlayers if the current number of players in the session
                is larger than the new max number of players
////////////////////////////////////////////////////////////////////////////////
Creation date : April 16, 96
Author : Albert Pais
////////////////////////////////////////////////////////////////////////////////
Comment :
If the functions does not end correctly, either the old data is preserved or
new data has been built, but only up to the error.  It means that the maximum
number of remote players can be lesser than the one specified by the argument.
However, the information about remote players effectively in the session is not lost
//////////////////////////////////////////////////////////////////////////////*/
NetLib_tdeErrorStatus eLevel1SetMaxNumberOfRemotePlayers(unsigned short uwNewMaxNumberOfRemotePlayers)
{
  unsigned short c_uwCurrentRemotePlayer;
  unsigned short c_uwNumberOfRemotePlayerProcessed;
  unsigned short c_uwCurrentNewFreeSlot;
  unsigned short uw_NumberOfRemotePlayer;
  NetLib_tdeErrorStatus eErrorReturned;
  tdstRouteCell *d_stNewRoute;
  tdstRouteCell *p_stDestRoute;
 
  if (gs_dcRndList) vFree((tdpPointer)gs_dcRndList);
  gs_dcRndList=(unsigned char*)pMalloc(uwNewMaxNumberOfRemotePlayers);
 
  if(gs_d_stRouteTable != (tdstRouteCell *)C_pNull)
  {
    /* Recopy data*/
    if(gs_uwMaxNumberOfRemotePlayers == uwNewMaxNumberOfRemotePlayers)
      return NetLib_E_es_NoError;
 
    /* Process the number of remote players :*/
    uw_NumberOfRemotePlayer = 0;
    for(c_uwCurrentRemotePlayer=0;c_uwCurrentRemotePlayer<gs_uwMaxNumberOfRemotePlayers;c_uwCurrentRemotePlayer++)
    {
      if(gs_d_stRouteTable[c_uwCurrentRemotePlayer].ulPlayerId != C_uxNetInvalidId)
        uw_NumberOfRemotePlayer++;
    }
    if(uw_NumberOfRemotePlayer > uwNewMaxNumberOfRemotePlayers)
      /* In that case, changing anything could induce lost of data, so return an error code */
      return NetLib_E_es_AlreadyTooMuchPlayers;
 
    /* Reallocate a new array of route and copy data of the previous one */
    if((d_stNewRoute = (tdstRouteCell *)pMalloc(sizeof(tdstRouteCell)*uwNewMaxNumberOfRemotePlayers))
       ==(tdstRouteCell *)C_pNull)
     /* Not enough memory to create the new array :*/
      return NetLib_E_es_NotEnoughMemory;
 
    c_uwCurrentRemotePlayer = 0;
    c_uwNumberOfRemotePlayerProcessed = 0;
    c_uwCurrentNewFreeSlot = uw_NumberOfRemotePlayer;
 
    while ((c_uwCurrentRemotePlayer < gs_uwMaxNumberOfRemotePlayers)&&
           ((c_uwCurrentNewFreeSlot < uwNewMaxNumberOfRemotePlayers)||
            (c_uwNumberOfRemotePlayerProcessed < uw_NumberOfRemotePlayer)))
    {
      if(gs_d_stRouteTable[c_uwCurrentRemotePlayer].ulPlayerId != C_uxNetInvalidId)
      {/* A player has been found...
        So initialize the new entry with old data */
        p_stDestRoute = &d_stNewRoute[c_uwNumberOfRemotePlayerProcessed];
        c_uwNumberOfRemotePlayerProcessed++;
      }
      else
      {/* A free entry has been found in the old array : */
        if(c_uwCurrentNewFreeSlot < uwNewMaxNumberOfRemotePlayers)
        {/* Free entries remain in the new array, so initialize it with free entry of the old array*/
          p_stDestRoute = &d_stNewRoute[c_uwCurrentNewFreeSlot];
          c_uwCurrentNewFreeSlot ++;
        }
        else
        {/* No more free entry in the new array, so trash old entry */
          p_stDestRoute = (tdstRouteCell*)C_pNull;
        }
      }
 
      if(p_stDestRoute != (tdstRouteCell*)C_pNull)
      {/* Data at the current entry of the old array must be retrieved :*/
        vLevel1CopyRouteCell(p_stDestRoute,&gs_d_stRouteTable[c_uwCurrentRemotePlayer]);
      }
      c_uwCurrentRemotePlayer++;
    }/* End of while */
    if(gs_uwMaxNumberOfRemotePlayers > uwNewMaxNumberOfRemotePlayers)
    {/* Destroy the end of the old list :*/
      vFree((tdpPointer)gs_d_stRouteTable);
      /* Initialize global data with new value :*/
      gs_d_stRouteTable = d_stNewRoute;
      gs_uwMaxNumberOfRemotePlayers = uwNewMaxNumberOfRemotePlayers;
      /* returns no error :*/
      return NetLib_E_es_NoError;
    }
    else
    {
      /* Destroy the old array*/
      vFree((tdpPointer)gs_d_stRouteTable);
      gs_d_stRouteTable = d_stNewRoute;
      /* Initialise remaining free entries of the new array */
      eErrorReturned = NetLib_E_es_NoError;
      while((c_uwCurrentNewFreeSlot < uwNewMaxNumberOfRemotePlayers)&&
            (eErrorReturned == NetLib_E_es_NoError))
      {
        d_stNewRoute[c_uwCurrentNewFreeSlot].ulPlayerId = C_uxNetInvalidId;
        d_stNewRoute[c_uwCurrentNewFreeSlot].eProtocol = E_Net_pr_InvalidProtocol;
        d_stNewRoute[c_uwCurrentNewFreeSlot].uwChannel = 0;
        c_uwCurrentNewFreeSlot++;
      }/* End of while */
      gs_uwMaxNumberOfRemotePlayers = c_uwCurrentNewFreeSlot;
      return eErrorReturned;
    }
  }
  else
  {/* First call */
    gs_uwMaxNumberOfRemotePlayers = uwNewMaxNumberOfRemotePlayers;
    gs_d_stRouteTable = (tdstRouteCell *)pMalloc(sizeof(tdstRouteCell)*uwNewMaxNumberOfRemotePlayers);
    if(gs_d_stRouteTable != (tdstRouteCell *)C_pNull)
    {
      eErrorReturned = NetLib_E_es_NoError;
      c_uwCurrentRemotePlayer = 0;
      /* Init the array : */
      while((c_uwCurrentRemotePlayer < uwNewMaxNumberOfRemotePlayers)&&
            (eErrorReturned == NetLib_E_es_NoError))
      {
        gs_d_stRouteTable[c_uwCurrentRemotePlayer].ulPlayerId = C_uxNetInvalidId;
        gs_d_stRouteTable[c_uwCurrentRemotePlayer].eProtocol = E_Net_pr_InvalidProtocol;
        gs_d_stRouteTable[c_uwCurrentRemotePlayer].uwChannel = 0;
        c_uwCurrentRemotePlayer ++;
      }/* end of while */
      if(eErrorReturned != NetLib_E_es_NoError)
        /* An error occured when initializing the c_uwCurrentRemotePlayer-1 entry*/
        gs_uwMaxNumberOfRemotePlayers = c_uwCurrentRemotePlayer - 2;
      return eErrorReturned;
    }
    else
    {
      gs_uwMaxNumberOfRemotePlayers = 0;
      return NetLib_E_es_NotEnoughMemory;
    }
  }
}
 
unsigned short uwLevel1GetNumberOfRemotePlayers(void)
{
  unsigned short c_uwCount;
  unsigned short uwNbrOfPlayers;
  c_uwCount = 0;
  uwNbrOfPlayers = 0;
  while(c_uwCount <gs_uwMaxNumberOfRemotePlayers)
  {
    if(gs_d_stRouteTable[c_uwCount].ulPlayerId!=C_uxNetInvalidId)
      uwNbrOfPlayers++;
    c_uwCount++;
  }
  return uwNbrOfPlayers;
}
 
/*//////////////////////////////////////////////////////////////////////////////
Description : unsigned short uwLevel1GetMaxNumberOfRemotePlayers(void)
 Retrieves the max number of players in level 1
 (does not include the local player)
////////////////////////////////////////////////////////////////////////////////
Input : None
////////////////////////////////////////////////////////////////////////////////
Output : An unsigned short representing the number of players, that is to say the number
         of entries in the route table
////////////////////////////////////////////////////////////////////////////////
Creation date : April 16, 96
Author : Albert Pais
//////////////////////////////////////////////////////////////////////////////*/
unsigned short uwLevel1GetMaxNumberOfRemotePlayers(void)
{
  return gs_uwMaxNumberOfRemotePlayers;
}
 
/*//////////////////////////////////////////////////////////////////////////////
Description : void vLevel1CloseLibrary(void)
Level 1 closing function
////////////////////////////////////////////////////////////////////////////////
Input : none
////////////////////////////////////////////////////////////////////////////////
Output : none
////////////////////////////////////////////////////////////////////////////////
Creation date : May 6,96
Author : Albert Pais
//////////////////////////////////////////////////////////////////////////////*/
void vLevel1CloseLibrary(void)
{
  unsigned short c_uwCount;
 
  vLevel1KillBroadcastTab();
  if (gs_dcRndList) vFree((tdpPointer)gs_dcRndList);
 
  vFree((tdpPointer)gs_d_stRouteTable);
  gs_d_stRouteTable = (tdstRouteCell *)C_pNull;
  gs_uwMaxNumberOfRemotePlayers = 0;
  gs_ulLocalPlayerId = C_uxNetInvalidId;
  /* Close list :*/
  vNetList2Kill(&gs_stIncomingSysMsgList);
  vNetList2Kill(&gs_stIncomingEngineMsgList);
  vNetList2Kill(&gs_stOutgoingMsgList);
 
  if(gs_p_stSessionCell) vLevel1DestroySessionCellList();
  gs_p_stSessionCell = (tdstSessionCell *)C_pNull;
 
  /* Closing functions for different protocols used :*/
  for(c_uwCount = 0; c_uwCount < gs_uwNumberOfProtocol;c_uwCount++) {
    if(gs_d_stAvailableProtocolInterfaces[c_uwCount].fn_vLevel0CloseProtocol)
      gs_d_stAvailableProtocolInterfaces[c_uwCount].fn_vLevel0CloseProtocol();
  }
 
  vLevel1DeleteProtocolTable();
}
 
/*//////////////////////////////////////////////////////////////////////////////
Description : vLevel1NetCloseChannel
Check if there are others players using the same protocol and channel, and if not,
close the channel
////////////////////////////////////////////////////////////////////////////////
Input : the protocol
        the channel
        the player Id
////////////////////////////////////////////////////////////////////////////////
Output : None
////////////////////////////////////////////////////////////////////////////////
Creation date : June 26,96
Author : Albert Pais
//////////////////////////////////////////////////////////////////////////////*/
void vLevel1NetCloseChannel(tdeNetProtocol eProtocol,tduwNetChannel uwChannel,NetLib_tduxPlayerId ulOldPlayer)
{
  unsigned short c_uwCount;
  tdstNetProtocolInterface *p_stProtInter;
 
  c_uwCount = 0;
  while ((c_uwCount<gs_uwMaxNumberOfRemotePlayers)&&
         ((gs_d_stRouteTable[c_uwCount].ulPlayerId==ulOldPlayer)||
          (gs_d_stRouteTable[c_uwCount].eProtocol != eProtocol)||/*it is not the same player*/
          (gs_d_stRouteTable[c_uwCount].uwChannel != uwChannel)))/* it has the same protocol, but should not have the same channel*/
    c_uwCount++;
 
  if(c_uwCount>=gs_uwMaxNumberOfRemotePlayers)
  {
    p_stProtInter = p_stLevel1GetInterfaceOfProtocol(eProtocol);
    if ((p_stProtInter) && (p_stProtInter->fn_vCloseChannel))
      p_stProtInter->fn_vCloseChannel(uwChannel);
  }
}
 
/*//////////////////////////////////////////////////////////////////////////////
Description : eLevel1FillMessageHeader
Fills the standart slots in a message header
////////////////////////////////////////////////////////////////////////////////
Input : A pointer to a tdstNetMessage
////////////////////////////////////////////////////////////////////////////////
Output : a NetLib_tdeErrorStatus
////////////////////////////////////////////////////////////////////////////////
Creation date : July 8,96
Author : Albert Pais
////////////////////////////////////////////////////////////////////////////////
Warning : Call this function only after having feeled the other slots, because the check
          sum is now evaluated
//////////////////////////////////////////////////////////////////////////////*/
static NetLib_tdeErrorStatus eLevel1FillMessageHeader(tdstNetMessage *p_stMessage)
{
  p_stMessage->uxSessionId = gs_ulSessionId;
  /* BigEndian : 0xij 
     i is used for little(1)/big(0) endian of the sender msg
     j is used along the route */
  if(p_stMessage->uxSenderId==gs_ulLocalPlayerId)
  {
      /*we send a msg :*/
    p_stMessage->uxHeadBigEndian = NetLib_ucGetLittleBigEndian();
    p_stMessage->uxBodyBigEndian = p_stMessage->uxHeadBigEndian;
  }
  else
  {
    /* we route a msg :*/
    p_stMessage->uxHeadBigEndian = NetLib_ucGetLittleBigEndian();
  }
  p_stMessage->ulReserved=M_ulMakeReserved(M_uwProcessCheckSum(p_stMessage),M_uwProcessCheckSumBody(p_stMessage));
  return NetLib_E_es_NoError;
}
 
/*//////////////////////////////////////////////////////////////////////////////
Description : uwNetProcessCheckSumHeader
Process the check sum on the header of a message
////////////////////////////////////////////////////////////////////////////////
Input : A pointer to a tdstNetMessage
////////////////////////////////////////////////////////////////////////////////
Output : the check sum
////////////////////////////////////////////////////////////////////////////////
Creation date : August,96
Author : Albert Pais
//////////////////////////////////////////////////////////////////////////////*/
unsigned short Net_uwSumLongByte(unsigned long l)
{
  unsigned short chk;
  chk=M_NET_ucLOBYTE(M_NET_uwLOWORD(l))
     +M_NET_ucLOBYTE(M_NET_uwHIWORD(l))
     +M_NET_ucHIBYTE(M_NET_uwLOWORD(l))
     +M_NET_ucHIBYTE(M_NET_uwHIWORD(l));
  return chk;
}
 
unsigned short uwNetProcessCheckSumHeader(tdstNetMessage *p_stMsg)
{
  unsigned short uwCheckSum;
  /* Header check sum :*/
  uwCheckSum = (unsigned short)
   (
    Net_uwSumLongByte(p_stMsg->uxSessionId)
   +Net_uwSumLongByte(p_stMsg->uxSenderId)
   +Net_uwSumLongByte(p_stMsg->uxRecipientId)
   +Net_uwSumLongByte(p_stMsg->uwMessageSizeInBytes)
   +Net_uwSumLongByte(p_stMsg->eMessageType)
   +Net_uwSumLongByte(p_stMsg->uxHeadBigEndian)
   +Net_uwSumLongByte(p_stMsg->uxBodyBigEndian)
   +Net_uwSumLongByte(p_stMsg->uxPriority)
   +Net_uwSumLongByte(p_stMsg->uxReplaceType)
   +Net_uwSumLongByte(p_stMsg->uxReplace)
   );
 
  return uwCheckSum;
}
 
/*//////////////////////////////////////////////////////////////////////////////
Description : uwNetProcessCheckSumBody
Process the check sum on the body of a message
////////////////////////////////////////////////////////////////////////////////
Input : A pointer to a tdstNetMessage
////////////////////////////////////////////////////////////////////////////////
Output : the check sum
////////////////////////////////////////////////////////////////////////////////
Creation date : August,96
Author : Albert Pais
//////////////////////////////////////////////////////////////////////////////*/
unsigned short uwNetProcessCheckSumBody(tdstNetMessage *p_stMsg)
{
  unsigned long ulCount;
  unsigned long ulMax;
  unsigned short uwCheckSum;
  unsigned short *p_uwCurrentWord;
 
  p_uwCurrentWord = (unsigned short*)(p_stMsg+1);
  ulMax = p_stMsg->uwMessageSizeInBytes/sizeof(unsigned short);
  uwCheckSum = 0;
  for(ulCount = 0;ulCount < ulMax; ulCount++)
  {
    uwCheckSum += *p_uwCurrentWord;
    p_uwCurrentWord++;
  }
  return uwCheckSum;
}
 
/*//////////////////////////////////////////////////////////////////////////////
Description : eNetCheckMessageBody
Check if the message is correct
////////////////////////////////////////////////////////////////////////////////
Input : A pointer to a tdstNetMessage
////////////////////////////////////////////////////////////////////////////////
Output : NetLib_E_es_NoError if no error occured
         NetLib_E_es_InvalidMessage otherwise
////////////////////////////////////////////////////////////////////////////////
Creation date : August,96
Author : Albert Pais
//////////////////////////////////////////////////////////////////////////////*/
NetLib_tdeErrorStatus eNetCheckMessageBody(tdstNetMessage *p_stMessage)
{
  if(M_uwProcessCheckSumBody(p_stMessage)==M_uwCheckSumBodySlot(p_stMessage))
    return NetLib_E_es_NoError;
  else
    return NetLib_E_es_InvalidMessage;
}
 
NetLib_tdeErrorStatus eLevel1NetSetMaxNbrOfMsgPerList(unsigned short uwNewNbrMsgPerList)
{
  gs_uwMaxNbrOfMsgPerList = uwNewNbrMsgPerList;
  return NetLib_E_es_NoError;
}
 
/*//////////////////////////////////////////////////////////////////////////////
Description : eNetLevel1EraseAllMessages
Erases all the message for which the callback returns true
////////////////////////////////////////////////////////////////////////////////
Input : a player id
        a flag for either incoming or outgoing data
        a pointer to a callback function
        a pointer parameter to this function
////////////////////////////////////////////////////////////////////////////////
Output : NetLib_E_es_NoError if no error occured
////////////////////////////////////////////////////////////////////////////////
Creation date : August 22,96
Author : Albert Pais
//////////////////////////////////////////////////////////////////////////////*/
NetLib_tdeErrorStatus eNetLevel1EraseAllMessages(NetLib_tduxPlayerId ulPlayerId,unsigned char ucIncomingOrOutgoingFlag,NetLib_tdeErrorStatus (_NET_CALLING_CONV_*pfn_eEraseMessage)(tdstNetMessage*,void*),void *p_vCallBackParam)
{
  tdstNetIter stIter;
  tdstNetL1Message *pstL1Msg;
 
  pstL1Msg=(tdstNetL1Message *)pNetList2IterInit(&stIter,&gs_stOutgoingMsgList);
  while (pstL1Msg)
  {
    if (pfn_eEraseMessage(pstL1Msg->pMessage,p_vCallBackParam)==NetLib_E_es_True)
    {
      vFree(pstL1Msg->pMessage);
      vFree(pstL1Msg->dstTabDest);
      pstL1Msg=(tdstNetL1Message *)pNetList2DeleteElem(&stIter);
    }
    else pstL1Msg=(tdstNetL1Message *)pNetList2Next(&stIter);
  }
  return NetLib_E_es_NoError;
}
 
/*//////////////////////////////////////////////////////////////////////////////
Description : eLevel1AddNewPlayer
Add a new player 
////////////////////////////////////////////////////////////////////////////////
Input : a pointer to a tdstAddPlayerL1Desc
////////////////////////////////////////////////////////////////////////////////
Output : NetLib_E_es_NoError if no error occured
////////////////////////////////////////////////////////////////////////////////
Creation date : October 21,96
Author : Albert Pais
//////////////////////////////////////////////////////////////////////////////*/
NetLib_tdeErrorStatus eLevel1AddNewPlayer(NetLib_tduxPlayerId ulNewPlayerId,tdstAddPlayerL1Desc *p_stAddPlayerDesc)
{
  tduwNetChannel uwChannelReturned;
  short wFreeAccess;
  NetLib_tdeErrorStatus eErrorReturned;
  tdstNetProtocolInterface *p_stProtocolInterface;
 
  wFreeAccess = wLevel1IndexInRouteTable(C_uxNetInvalidId);
  if(wFreeAccess == -1) return NetLib_E_es_AlreadyTooMuchPlayers;
 
  p_stProtocolInterface = p_stLevel1GetInterfaceOfProtocol(p_stAddPlayerDesc->m_eProtocol);
  if(!p_stProtocolInterface) return NetLib_E_es_InvalidProtocol;
 
  if (!(p_stProtocolInterface->fn_eAddNewPlayer)) return NetLib_E_es_ServiceNotYetProvided;
 
  if((eErrorReturned = p_stProtocolInterface->fn_eAddNewPlayer(&uwChannelReturned,p_stAddPlayerDesc->m_vL0Param))
     !=NetLib_E_es_NoError) return eErrorReturned;
 
  if((eErrorReturned=eLevel1FillNewRouteCell(&gs_d_stRouteTable[wFreeAccess],ulNewPlayerId,
                                             p_stAddPlayerDesc->m_eProtocol,
                                             uwChannelReturned))!=0)
  {
    p_stProtocolInterface->fn_vCloseChannel(uwChannelReturned);
    return eErrorReturned;
  }
 
  vLevel1AddBroadcastPath(gs_ulLocalPlayerId,ulNewPlayerId);
 
  return NetLib_E_es_NoError;
}
