/*
 * universal multiplayer library level 0 implementation file
 * transport layer: TEN arena PC-windows 95
 */
 
/*
 *      Author: David Fournier
 *
 *      Creation Date:      13/12/96
 *
 */
 
 
#include "warnings.h"
 
#include <stdlib.h>
/* 
 * we need this one to fill the interface structure 
 * with pointers to the correct functions 
 */
#include "Privl0ten.h"
#include "PrivNetDef.h"
#include "l0GlDef.h"
#include "NetMemCo.h"
#include "NetSock.h"
#include "NetEnd.h"
#include "PrivNetSer.h"
#include <NET\Netdebug.h>
 
#include <arena.h>
#include <button.h>
#include <debuglib.h>
#include <environ.h>
#include <tenutils.h>
 
#define TEN_MAXBUF 250
 
static char gs_bL0PCWin95TENInitState;
 
#pragma pack(1)
 
typedef struct
{
  NetLib_tduxPlayerId pid;         /* ID of the player. */
  NetLib_tduwJoinType joinType;    /* Create, Join or Watch. */
}NetLib_tdstInfoPlayer;
 
static NetLib_tdstInfoPlayer gs_InfoLocal;  /* Informations of the local player. */
static long SendBufSize;                    /* Virtual buffer size. */
static unsigned long LastTime;              /* Time for the last eL0PCWin95TENEngine. */
static unsigned long SendMaxRate;           /* Maximum rate. */
static unsigned short NbrPlayer;            /* Number of player. */
static NetLib_tdstInfoPlayer *TabPlayer;    /* Table of player. */
 
typedef struct stFIFO
{
  struct stFIFO *next;                 /* Next message of FIFO. */
  unsigned long size;                  /* Size of message. */
  NetLib_tduxPlayerId id;              /* ID of sender. */
}FIFO;
 
static FIFO *FIFObegin;                /* First message of file. */
static FIFO **FIFOend;                 /* Last pointer of the file. */
 
static unsigned short gs_uwCallBack;
static NetLib_tduwJoinType gs_JoinType;
 
typedef struct
{
  NetLib_uxReadReceiptId m_ulReadReceiptId;
  unsigned long uxPriority:7;
  unsigned long uxReplaceType:7;
  unsigned long uxReplace:1;
  unsigned long eMessageType:5;
  unsigned long uxHeadBigEndian:1;
  unsigned long uxBodyBigEndian:1;
}TENHead;
 
#pragma pack()
 
  /* Convert a bloc memory into a hexa string. */
void memtohexa(void *mem,unsigned short sizemem,char *hexa)
{
  unsigned char *p=(unsigned char *)mem;
  unsigned short c,i;
  for (i=0;i<sizemem;i++)
  {
    c=((p[i]&0xF0)>>4);
    if (c<10) hexa[i*2]=c+'0';
    else hexa[i*2]=(c-10)+'A';
    c=p[i]&0x0F;
    if (c<10) hexa[i*2+1]=c+'0';
    else hexa[i*2+1]=(c-10)+'A';
  }
  hexa[i*2]=0;
}
 
  /* Convert a hexa string into a bloc memory. */
void hexatomem(void *mem,char *hexa)
{
  unsigned char *p=(unsigned char *)mem;
  unsigned short c,i,sizehexa;
  sizehexa=strlen(hexa)/2;
  for (i=0;i<sizehexa;i++)
  {
    p[i]=0;
    c=hexa[i*2];
    if ((c>='0') && (c<='9')) p[i]+=((c-'0')<<4);
    else  p[i]+=(((c-'A')+10)<<4);
    c=hexa[i*2+1];
    if ((c>='0') && (c<='9')) p[i]+=(c-'0');
    else  p[i]+=(c-'A')+10;
  }
}
 
  /* Process the maximum send rate. */
static void ProcMaxRate(void)
{
  unsigned short i,nbrp=0;
  unsigned long max=10000000L;
  unsigned long rate;
  for (i=0;i<NbrPlayer;i++)
  {
    if (TabPlayer[i].pid!=C_uxNetInvalidId)
    {
      size_t size=sizeof(long);
      tenArGetPlayerInfo(TabPlayer[i].pid,kTenArPIBandwidth,&rate,&size);
      if ((rate) && (max>rate)) max=rate;
      nbrp++;
    }
  }
  if (nbrp>1) SendMaxRate=max/(nbrp-1);
  else SendMaxRate=10000000L;
  SendMaxRate=5000;
}
 
  /* Add a player info. */
static void AddPlayerInfo(NetLib_tduxPlayerId pid,NetLib_tduwJoinType joinType)
{
  NetLib_tdstInfoPlayer *nTab;
  unsigned short i;
  if (pid+1>NbrPlayer)
  {
    nTab=(NetLib_tdstInfoPlayer *)pMalloc(sizeof(NetLib_tdstInfoPlayer)*(pid+1));
    memcpy(nTab,TabPlayer,sizeof(NetLib_tdstInfoPlayer)*NbrPlayer);
    for (i=NbrPlayer;i<pid;i++) TabPlayer[i].pid=C_uxNetInvalidId;
    vFree((char *)TabPlayer);
    TabPlayer=nTab;
    NbrPlayer=pid+1;
  }
  TabPlayer[pid].pid=pid;
  TabPlayer[pid].joinType=joinType;
  ProcMaxRate();
}
 
  /* Supprim a player info. */
static void SupPlayerInfo(NetLib_tduxPlayerId pid)
{
  TabPlayer[pid].pid=C_uxNetInvalidId;
  ProcMaxRate();
}
 
/*****************************************************************************
 * Description: vL0PCWin95TENCloseChannel
 * frees a slot in the channel table
 *****************************************************************************
 * Input:     identifier of the channel
 * Output:        none
 *****************************************************************************/
void vL0PCWin95TENCloseChannel(tduwNetChannel uwChannel)
{
}
 
/*****************************************************************************
 * Description: uwL0PCWin95TENStartChannelScan
 * returns the identification number of the first channel for the protocol
 *****************************************************************************
 * Input:   none
 * Output:  0
 *****************************************************************************/
tduwNetChannel uwL0PCWin95TENStartChannelScan(void)
{
  if(!(gs_bL0PCWin95TENInitState&0x02))
    return C_uwNetInvalidChannel;
    
  return 0;
}
 
/*****************************************************************************
 * Description: uwL0PCWin95TENNextChannel
 * returns the identification number of the channel following the last
 *****************************************************************************
 * Input:   uwLastScannedChannel, index returned by the previous call
 * Output:  C_uwNetInvalidChannel
 *****************************************************************************/
tduwNetChannel uwL0PCWin95TENNextChannel(tduwNetChannel uwLastScannedChannel)
{
 return C_uwNetInvalidChannel;
}
 
/*****************************************************************************
 * Description: uwL0PCWin95TENStartBroadcastChannelScan
 * returns the identification number of the first broadcast channel
 * for the protocol
 *****************************************************************************
 * Input:   none
 * Output:  0
 *****************************************************************************/
tduwNetChannel uwL0PCWin95TENStartBroadcastChannelScan(void)
{
  if(!(gs_bL0PCWin95TENInitState&0x02))
    return C_uwNetInvalidChannel;
 
  return 1;
}
 
/*****************************************************************************
 * Description: uwL0PCWin95TENNextBroadcastChannel
 * returns the identification number of the broadcast channel following
 * the specified one
 *****************************************************************************
 * Input:   uwLastScannedChannel, index returned by the previous call
 * Output:  identification of the channel following the specified one
 *****************************************************************************/
tduwNetChannel uwL0PCWin95TENNextBroadcastChannel(tduwNetChannel uwLastScannedChannel)
{
  return C_uwNetInvalidChannel;
}
 
/*****************************************************************************
 * Description: L0PCWin95TENReadCallBack
 *****************************************************************************
 * Input:   p_uwChannel, pointer on a field to store the identification of
 *          a new channel in case of access to a newly created broacast channel
 *          (irrelevant for the serial layer)
 *          ppData, address of the pointer to retrieve a full message if
 *          necessary.
 * Output:  an error condition.
 *****************************************************************************/
static void L0PCWin95TENReadCallBack(int fromPid, void *buf, size_t size)
{
  FIFO *cour;
#if defined(NET_USE_DEBUG)
  TENHead *pSend=(TENHead *)buf;
vDebugSISISI(Net_C_Debug_Ten,"Receive Type",pSend->eMessageType,"Size",size-sizeof(TENHead),"Source",fromPid);
#endif
  cour=(FIFO *)pMalloc(sizeof(FIFO)+size);
  if (cour)
  {
    *FIFOend=cour;
    cour->next=NULL;
    cour->size=size;
    cour->id=fromPid;
    memcpy(cour+1,buf,size);
    FIFOend=&(cour->next);
  }
}
 
/*****************************************************************************
 * Description: wL0PCWin95TENGetMessage
 *****************************************************************************
 * Input:   ppData, address of the pointer to retrieve a full message if
 *          necessary.
 * Output:  return 1 if getting a message and 0 otherwise.
 *****************************************************************************/
short wL0PCWin95TENGetMessage(tdstNetMessage **ppMes)
{
  FIFO *cour;
  tdstNetMessage *pMes;
  TENHead *pSend;
  if (FIFObegin==NULL)
  {
    *ppMes=NULL;
    return 0;
  }
  else
  {
    pMes=(tdstNetMessage *)pMalloc(FIFObegin->size+sizeof(tdstNetMessage)-sizeof(TENHead));
    if (pMes==NULL)
    {
      *ppMes=NULL;
      return -1;
    }
    pSend=(TENHead *)(FIFObegin+1);
    memcpy(pMes+1,pSend+1,FIFObegin->size-sizeof(TENHead));
    pMes->m_uxReadReceiptId=pSend->m_ulReadReceiptId;
    pMes->uxPriority=pSend->uxPriority;
    pMes->uxReplace=pSend->uxReplace;
    pMes->uxReplaceType=pSend->uxReplaceType;
    pMes->eMessageType=pSend->eMessageType;
    pMes->uxHeadBigEndian=pSend->uxHeadBigEndian;
    pMes->uxBodyBigEndian=pSend->uxBodyBigEndian;
    pMes->uxSessionId=0;
    pMes->uxSenderId=FIFObegin->id;
    pMes->uxRecipientId=gs_InfoLocal.pid;
    pMes->uwMessageSizeInBytes=(unsigned short)(FIFObegin->size-sizeof(TENHead));
    pMes->ulReserved=M_ulMakeReserved(M_uwProcessCheckSum(pMes),
                                      M_uwProcessCheckSumBody(pMes));
    cour=FIFObegin->next;
    vFree((char *)FIFObegin);
    FIFObegin=cour;
    if (cour==NULL) FIFOend=&FIFObegin;
    *ppMes=pMes;
    return 1;
  }
}
 
/*****************************************************************************
 * Description: eL0PCWin95TENReadData
 *****************************************************************************
 * Input:   p_uwChannel, pointer on a field to store the identification of
 *          a new channel in case of access to a newly created broacast channel
 *          (irrelevant for the serial layer)
 *          ppData, address of the pointer to retrieve a full message if
 *          necessary.
 * Output:  an error condition.
 *****************************************************************************/
NetLib_tdeErrorStatus eL0PCWin95TENReadData(tduwNetChannel *p_uwChannel, tdpPointer *ppData)
{
  short r;
 /* verify the validity of the Channel */
  if(*p_uwChannel>1) return NetLib_E_es_InvalidChannel;
 
 /* no : try to get a message from the FIFO */
  r=wL0PCWin95TENGetMessage((tdstNetMessage **)ppData);
  if (r>0) return NetLib_E_es_NoError;
  else if (r==0) return NetLib_E_es_FIFOIsEmpty;
  else return NetLib_E_es_NotEnoughMemory;
}
 
 
/*****************************************************************************
 * Description: eL0PCWin95TENSendData
 *****************************************************************************
 * Input:   uwChannel, channel to send the message into.
 *          pData, pointer on the block to send
 * Output:  an error condition.
 *****************************************************************************/
NetLib_tdeErrorStatus eL0PCWin95TENSendData(tduwNetChannel uwChannel, tdpPointer pData)
{
  unsigned long ulBytestoSend;
  tdstNetMessage *pMes=(tdstNetMessage *)pData;
  int NumErr;
  TENHead *pSend;
  unsigned long NewTime;
 
  /* verify the validity of the Channel */
  if(uwChannel>1)
  {
    #if defined(NET_USE_DEBUG)
      vDebugS(Net_C_Debug_Ten,"Invalid channel");
    #endif
    return NetLib_E_es_InvalidChannel;
  }
 
  if (pMes->uxRecipientId==C_uxNetBroadcastId) uwChannel=1;
 
  ulBytestoSend= pMes->uwMessageSizeInBytes + sizeof(TENHead);
 
  NewTime=GetTickCount();
  SendBufSize-=((NewTime-LastTime)*(float)SendMaxRate)/1000;
  LastTime=NewTime;
  if (SendBufSize<0) SendBufSize=0;
  if (SendBufSize>=TEN_MAXBUF)
  {
    #if defined(NET_USE_DEBUG)
      vDebugS(Net_C_Debug_Ten,"Controle de flot.");
    #endif
    return NetLib_E_es_BufferIsFull;
  }
  SendBufSize+=ulBytestoSend+20;
 
  pSend=(TENHead *)pMalloc(ulBytestoSend);
  if (!pSend)
  {
    #if defined(NET_USE_DEBUG)
      vDebugS(Net_C_Debug_Ten,"Manque de mémoire.");
    #endif
    return NetLib_E_es_NotEnoughMemory;
  }
  memcpy(pSend+1,pMes+1,pMes->uwMessageSizeInBytes);
 
  pSend->m_ulReadReceiptId=pMes->m_uxReadReceiptId;
  pSend->uxPriority=pMes->uxPriority;
  pSend->uxReplace=pMes->uxReplace;
  pSend->uxReplaceType=pMes->uxReplaceType;
  pSend->eMessageType=pMes->eMessageType;
  pSend->uxHeadBigEndian=pMes->uxHeadBigEndian;
  pSend->uxBodyBigEndian=pMes->uxBodyBigEndian;
 
  if (uwChannel!=1)
  {
    #if defined(NET_USE_DEBUG)
      vDebugSISISI(Net_C_Debug_Ten,"Send to one player. Type",pMes->eMessageType,"Size",pMes->uwMessageSizeInBytes,"Recipient",pMes->uxRecipientId);
    #endif
    NumErr=tenArSendToPlayer(pMes->uxRecipientId,pSend,ulBytestoSend);
  }
  else
  {
    #if defined(NET_USE_DEBUG)
      vDebugSISISI(Net_C_Debug_Ten,"Send to all players. Type",pMes->eMessageType,"Size",pMes->uwMessageSizeInBytes,"Recipient",pMes->uxRecipientId);
    #endif
    NumErr=tenArSendToOtherPlayers(pSend,ulBytestoSend);
  }
 
  vFree((char *)pSend);
  if (NumErr)
  {
    #if defined(NET_USE_DEBUG)
      vDebugSI(Net_C_Debug_Ten,"Emission en attente",NumErr);
    #endif
    return NetLib_E_es_BufferIsFull;
  }
 
  /* free the message buffer */
  vFree(pData);
 
  return NetLib_E_es_NoError;
}
 
/*****************************************************************************
 * Description: eL0PCWin95TENQueryChannelStatus
 * returns the current status of the channel
 *****************************************************************************
 * Input:   uwChannel, channel to query
 * Output:  an error condition.
 *****************************************************************************/
NetLib_tdeErrorStatus eL0PCWin95TENQueryChannelStatus(tduwNetChannel uwChannel)
{
  return NetLib_E_es_NoError;
}
 
/*****************************************************************************
 * Description: eL0PCWin95TENEngine
 *****************************************************************************
 * Input:   none
 * Output:  none
 *****************************************************************************/
void eL0PCWin95TENEngine(void)
{
  tenArIdleArena();
}
 
/*****************************************************************************
 * Description : vL0PCWin95TENCloseProtocol(void)
 * TEN-Level 0 closing function
 *****************************************************************************
 * Input : none
 * Output : none
 *****************************************************************************/
void vL0PCWin95TENCloseProtocol(void)
{
  FIFO *p;
  while (FIFObegin)
  {
    p=FIFObegin->next;
    vFree((char*)FIFObegin);
    FIFObegin=p;
  }
  FIFObegin=NULL;
  FIFOend=&FIFObegin;
 
  vLevel1RemoveProtocol(E_Net_pr_Windows95TENProtocol);
}
 
/*****************************************************************************
 * Description: L0PCWin95TENNewPlayerCallBack
 *****************************************************************************
 * Input:   none
 * Output:  none
 *****************************************************************************/
static void L0PCWin95TENNewPlayerCallBack(int pid, int isYou, char *options,
                char *termOptions, char *address, long uniqueId, char *joinType)
{
  NetLib_tdstAddPlayerDesc PlDesc;
  char typeToken[16];
  char hexa[500],mem[250];
  short j=-1;
#if defined(NET_USE_DEBUG)
vDebugSI(Net_C_Debug_Ten,"NewPlayerCallBack Pid",pid);
#endif
  sscanf(joinType, "%s", typeToken);      /* peel off the first word*/
  if (!strcmp(typeToken, "create")) j=NetLib_Join_Create;
  else if (!strcmp(typeToken, "join")) j=NetLib_Join_Join;
  else if (!strcmp(typeToken, "watch")) j=NetLib_Join_Watch;
  if (isYou)
  {
#if defined(NET_USE_DEBUG)
vDebugS(Net_C_Debug_Ten,"Is me");
#endif
    gs_InfoLocal.pid = pid;
    gs_InfoLocal.joinType = j;
    gs_uwCallBack|=1;
  }
  PlDesc.m_tduxPlayerId=pid;
  getStringParam(options,"NetlibOptions",hexa,sizeof(hexa));
  hexatomem(mem,hexa);
  PlDesc.m_pPlayerDescriptionData=mem;
  PlDesc.m_uxPlayerDesciptionLength=strlen(hexa)/2;
  PlDesc.m_stL1AddPlayerDesc.m_eProtocol=E_Net_pr_Windows95TENProtocol;
  PlDesc.m_stL1AddPlayerDesc.m_vL0Param=NULL;
  PlDesc.IsYou=isYou;
  PlDesc.JoinType=j;
  PlDesc.Options=options;
  NetLib_eAddNewPlayer(&PlDesc);
  AddPlayerInfo((NetLib_tduxPlayerId)pid,(NetLib_tduwJoinType)j);
}
 
 
/*****************************************************************************
 * Description: L0PCWin95TENDoPlayerLeft
 * A user has left the game.
 *****************************************************************************
 * Input:   pid : ID of player who left the game.
 * Output:  none
 *****************************************************************************/
static void L0PCWin95TENDoPlayerLeft(int pid)
{
#if defined(NET_USE_DEBUG)
vDebugSI(Net_C_Debug_Ten,"DoPlayerLeft Pid",pid);
#endif
  eNetDisconnectPlayer((NetLib_tduxPlayerId)pid,1);
  SupPlayerInfo((NetLib_tduxPlayerId)pid);
}
 
#include <stdio.h>
 
/*****************************************************************************
 * Description: eL0PCWin95TENDoAlert
 * Callback receive the error messages.
 *****************************************************************************/
static void eL0PCWin95TENDoAlert(int type, int err, char *msg)
{
    printf("%s\n", msg);
}
 
/*****************************************************************************
 * Description: eL0PCWin95TENDebugMessage
 * Callback for verifyNoErr.
 *****************************************************************************/
static void eL0PCWin95TENDebugMessage(int msgLevel, char *msg)
{
    fprintf(stderr, "%s", msg);
}
 
/*****************************************************************************
 * Description : eL0PCWin95TENSetGameOptions
 * Set the game options.
 *****************************************************************************
 * Input : GameOptions : the game options.
 * Output : none.
 *****************************************************************************/
_NET_EXPORT_ void _NET_CALLING_CONV_ eL0PCWin95TENSetGameOptions(char *GameName,void *GameOptions,unsigned short SizeOptions)
{
  char options[500],conv[500];
  options[0]=0;
  addStringParam(options,sizeof(options),"name",GameName);
  memtohexa(GameOptions,SizeOptions,conv);
  addStringParam(options,sizeof(options),"NetlibOptions",conv);
  tenArRegisterGame(options);
  NetLib_eSetSessionDescription((unsigned char)SizeOptions,(char *)GameOptions,0);
}
 
/*****************************************************************************
 * Description : eL0PCWin95TENSetPlayerOptions
 * Set the player options.
 *****************************************************************************
 * Input : GameOptions : the game options.
 * Output : none.
 *****************************************************************************/
_NET_EXPORT_ void _NET_CALLING_CONV_ eL0PCWin95TENSetPlayerOptions(char *PlayerName,char *PlayerOptions,unsigned short SizeOptions)
{
  char options[500],conv[500];
  options[0]=0;
  addStringParam(options,sizeof(options),"name",PlayerName);
  memtohexa(PlayerOptions,SizeOptions,conv);
  addStringParam(options,sizeof(options),"NetlibOptions",conv);
  tenArRegisterPlayer(options);
  do
  {
    tenArIdleArena();
  }while (!((gs_uwCallBack&1) && ((gs_uwCallBack&4) || (gs_JoinType==NetLib_Join_Create))));
}
 
/*****************************************************************************
 * Description: eL0PCWin95TENDoPregameHook
 * Callback for verifyNoErr.
 *****************************************************************************/
static void eL0PCWin95TENDoPregameHook(char *joinType, char *gameTermOptions,
    char *playerTermOptions, char *name, char *alias, char *address)
{
  char typeToken[16];
  sscanf(joinType, "%s", typeToken);      /* peel off the first word*/
  if (!strcmp(typeToken, "create")) gs_JoinType=NetLib_Join_Create;
  else if (!strcmp(typeToken, "join")) gs_JoinType=NetLib_Join_Join;
  else if (!strcmp(typeToken, "watch")) gs_JoinType=NetLib_Join_Watch;
  gs_uwCallBack|=2;
}
 
/*****************************************************************************
 * Description: eL0PCWin95TENDoGameOptions
 * Callback for recept change of game options.
 *****************************************************************************/
void eL0PCWin95TENDoGameOptions(char *options, char *termOptions)
{
  char hexa[500],mem[250];
  getStringParam(options,"NetlibOptions",hexa,sizeof(hexa));
  hexatomem(mem,hexa);
  NetLib_eSetSessionDescription((char)(strlen(hexa)/2),mem,0);
  gs_uwCallBack|=4;
}
 
/*****************************************************************************
 * Description: vL0PCWin95TENAddNewPlayer
 *****************************************************************************
 * Input:   pChanel : adress of returned chanel.
 *          par     : extern parameter.
 * Output:  error code.
 *****************************************************************************/
NetLib_tdeErrorStatus vL0PCWin95TENAddNewPlayer(tduwNetChannel *pChanel,void *par)
{
  *pChanel=0;
  return NetLib_E_es_NoError;
}
 
/*****************************************************************************
 * Description: vL0PCWin95TENOpenProtocol
 * initialize the specified interface structure with the correct function
 * pointers to enable a protocol-independent data transmission process.
 *****************************************************************************
 * Input:   p_stProtocolInterface, pointer to the interface structure
 *                      uwPortNumber, port number to bind the socket to
 * Output:  none
 *****************************************************************************/
void _NET_CALLING_CONV_ vL0PCWin95TENOpenProtocol(void)
{
  tdstNetProtocolInterface *p_stProtocolInterface;
 
#if defined(NET_USE_DEBUG)
vDebugClear(Net_C_Debug_Ten);
vDebugS(Net_C_Debug_Ten,"SetupInterface");
#endif
 
  p_stProtocolInterface=pstLevel1AddProtocol();
 
  FIFObegin=NULL;
  FIFOend=&FIFObegin;
 
  /* logical identification of the protocol */
  p_stProtocolInterface->eProtocol = E_Net_pr_Windows95TENProtocol;
 
    /* setup the function pointers */
  p_stProtocolInterface->fn_uwStartChannelScan = uwL0PCWin95TENStartChannelScan;
  p_stProtocolInterface->fn_uwStartBroadcastChannelScan= uwL0PCWin95TENStartBroadcastChannelScan;
  p_stProtocolInterface->fn_uwNextChannel= uwL0PCWin95TENNextChannel;
  p_stProtocolInterface->fn_uwNextBroadcastChannel= uwL0PCWin95TENNextBroadcastChannel;
  p_stProtocolInterface->fn_eReadData= eL0PCWin95TENReadData;
  p_stProtocolInterface->fn_eSendData= eL0PCWin95TENSendData;
  p_stProtocolInterface->fn_eQueryChannelStatus= eL0PCWin95TENQueryChannelStatus;
  p_stProtocolInterface->fn_vLevel0NetEngine = eL0PCWin95TENEngine;
  p_stProtocolInterface->fn_vCloseChannel = vL0PCWin95TENCloseChannel;
  p_stProtocolInterface->fn_vLevel0CloseProtocol = vL0PCWin95TENCloseProtocol;
  p_stProtocolInterface->fn_eAddNewPlayer = vL0PCWin95TENAddNewPlayer;
 
  gs_bL0PCWin95TENInitState = 0x01;
 
  setDebugMsgRoutine(eL0PCWin95TENDebugMessage);
 
  tenArSetPregameHookRoutine(eL0PCWin95TENDoPregameHook);
  tenArSetGameOptionsRoutine(eL0PCWin95TENDoGameOptions);
  tenArSetPlayerLeftRoutine(L0PCWin95TENDoPlayerLeft);
  tenArSetPlayerEnteredRoutine(L0PCWin95TENNewPlayerCallBack);
  tenArSetIncomingPacketRoutine(L0PCWin95TENReadCallBack);
  tenArSetAlertMessageRoutine(eL0PCWin95TENDoAlert);
}
 
/*****************************************************************************
 * Description: eL0PCWin95TENStart
 * Start a game with TEN
 *****************************************************************************
 * Input:   ppGameOptions : Adresse of a pointer wich receive the adresse of
 *          game options.
 * Output:  join type.
 *****************************************************************************/
_NET_EXPORT_ NetLib_tduwJoinType _NET_CALLING_CONV_ eL0PCWin95TENStart(void)
{
#if defined(NET_USE_DEBUG)
vDebugS(Net_C_Debug_Ten,"Start");
#endif
  SendMaxRate=10000000L;
  NbrPlayer=0;
  SendBufSize=0;
  TabPlayer=NULL;
  LastTime=GetTickCount();
  FIFObegin=NULL;
  FIFOend=&FIFObegin;
  verifyNoErr(tenArInitArena("Lobby"));
  do
  {
    tenArIdleArena();
  }while (!(gs_uwCallBack&2));
 
  gs_bL0PCWin95TENInitState|=0x02;
  
  return gs_JoinType;
}
 
/*****************************************************************************
 * Description: eL0PCWin95TENFinish
 * Finish a game with TEN.
 *****************************************************************************
 * Input:   none
 * Output:  none
 *****************************************************************************/
_NET_EXPORT_ void _NET_CALLING_CONV_ eL0PCWin95TENFinish(void)
{
  FIFO *p;
#if defined(NET_USE_DEBUG)
vDebugS(Net_C_Debug_Ten,"Finish");
#endif
  vFree((char *)TabPlayer);
  while (FIFObegin)
  {
    p=FIFObegin->next;
    vFree((char*)FIFObegin);
    FIFObegin=p;
  }
  FIFObegin=NULL;
  FIFOend=&FIFObegin;
  tenArExitArena();
  gs_bL0PCWin95TENInitState&=~0x02;
  NetLib_eFlushSendingList(500);
  NetLib_vDisconnectAllRemotePlayers();
}
 
/*****************************************************************************
 * Description: eL0PCWin95TENGamePrivate
 * Refuse all new players in the game.
 *****************************************************************************/
_NET_EXPORT_ void _NET_CALLING_CONV_ eL0PCWin95TENGamePrivate(void)
{
  char options[20];
  options[0]=0;
  addStringParam(options,sizeof(options),"lock","y");
  tenArSetGameState(options);
}
 
/*****************************************************************************
 * Description: eL0PCWin95TENGamePublic
 * Accept all new players in the game (if maximum number of player allow it).
 *****************************************************************************/
_NET_EXPORT_ void _NET_CALLING_CONV_ eL0PCWin95TENGamePublic(void)
{
  char options[20];
  options[0]=0;
  addStringParam(options,sizeof(options),"lock","n");
  tenArSetGameState(options);
}
 
/*****************************************************************************
 * Description : eL0PCWin95TENIsProtocolSet
 * Check if the protocol is correctly initialize or not
 *****************************************************************************
 * Input : none
 * Output :
 * NetLib_E_es_ProtocolNotInitialized : the vLevel0SetupWin95PCTENInterface has not
 * been called yet or it has failed, the application should call the level 2
 * eInitializeGlobalData function.
 * NetLib_E_es_InitialisationSocketError : the eLevel0InitWin95PCTEN function has not been
 * called yet or it has failed, the application should call the level 2 
 * eInitializeGlobalData function
 * NetLib_E_es_NoPortSelected : the eL0PCWin95TENAddPort has not been called yet. The 
 * protocol has been correctly initialised, it can be used, but it won't give any
 * results since no port is avaible.  The application should call it.
 *****************************************************************************/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95TENIsProtocolSet(void)
{
  if(!(gs_bL0PCWin95TENInitState&0x01)) return NetLib_E_es_ProtocolNotInitialized;
  return NetLib_E_es_True;
}
 
/*****************************************************************************
 * Description : eL0PCWin95TENIsProtocolAvailable
 * Check if the protocol is avaible for use or not
 *****************************************************************************
 * Input : A port number to test
 * Output : NetLib_E_es_True if the port is avaible
 * An error code otherwise
 *****************************************************************************/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95TENIsProtocolAvailable(void)
{
  if(!(gs_bL0PCWin95TENInitState&0x01)) return NetLib_E_es_ProtocolNotInitialized;
  return NetLib_E_es_True;
}
 
/*****************************************************************************
 * Description : eL0PCWin95TENPlayerInfo
 * Return player bandwith and latency.
 * Return non null value if error.
 *****************************************************************************/
int _NET_CALLING_CONV_ eL0PCWin95TENPlayerInfo(NetLib_tduxPlayerId PId,long *bandwhith,long *latency)
{
  int r;
  size_t size=sizeof(long);
  r=tenArGetPlayerInfo(PId,kTenArPIBandwidth,bandwhith,&size);
  if (r!=0) return r;
  size=sizeof(long);
  r=tenArGetPlayerInfo(PId,kTenArPIAveLatency,latency,&size);
  return r;
}
 
/*****************************************************************************
 * Description : eL0PCWin95TENLaunchTenClient
 * Input : a quit callback function.
 *****************************************************************************/
void _NET_CALLING_CONV_ eL0PCWin95TENLaunchTenClient(void (*rtn)(void))
{
  tenBnSetExitRtn(rtn);
  tenBnStart();
}
