/*
 * universal multiplayer library level 0 implementation file
 * transport layer: Game Service UBI Soft PC-windows 95
 */
 
/*
 *      Author: David Fournier
 *
 *      Creation Date:      2/1/97
 *
 */
 
 
#include "warnings.h"
 
#include <stdlib.h>
/*
 * we need this one to fill the interface structure
 * with pointers to the correct functions
 */
#include "PrivL0GSUBI.h"
#include "PrivL0TCP.h"
#include "PrivNetDef.h"
#include "L0GlDef.h"
#include "NetMemCo.h"
#include "NetSock.h"
#include "NetEnd.h"
#include "PrivNetSer.h"
#include <stdio.h>
#include <gameserv.h>
#include <NET\Netdebug.h>
 
#define GSUBI_MAXBUF 250
 
typedef short tdPlayerId;         /* Id for the game service. */
 
static char gs_bL0PCWin95GSUBIInitState; /* State of the level 0 game service module. */
 
#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 NetLib_tduxPlayerId gs_IdMaster;       /* Master Id. */
 
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 long SendBufSize;                    /* Virtual buffer size. */
static unsigned long LastTime;              /* Time for the last eL0PCWin95TENEngine. */
static unsigned long SendMaxRate;           /* Maximum rate. */
 
typedef struct
{
  NetLib_uxReadReceiptId m_uxReadReceiptId;
  unsigned long uxPriority:7;
  unsigned long uxReplaceType:7;
  unsigned long uxReplace:1;
  unsigned long eMessageType:5;
  unsigned long uxHeadBigEndian:1;
  unsigned long uxBodyBigEndian:1;
}GSUBIHead;
 
#pragma pack()
 
/*****************************************************************************
 * Description: vL0PCWin95GSUBICloseChannel
 * Not use for this protocol.
 *****************************************************************************
 * Input:     identifier of the channel
 * Output:        none
 *****************************************************************************/
void vL0PCWin95GSUBICloseChannel(tduwNetChannel uwChannel)
{
}
 
/*****************************************************************************
 * Description: uwL0PCWin95GSUBIStartChannelScan
 * returns the identification number of the first channel for the protocol
 *****************************************************************************
 * Input:   none
 * Output:  0
 *****************************************************************************/
tduwNetChannel uwL0PCWin95GSUBIStartChannelScan(void)
{
  if(!(gs_bL0PCWin95GSUBIInitState&0x02))
    return C_uwNetInvalidChannel;
 
  return 0;
}
 
/*****************************************************************************
 * Description: uwL0PCWin95GSUBINextChannel
 * returns the identification number of the channel following the last
 * return always C_uwNetInvalidChannel because there is one channel.
 *****************************************************************************
 * Input:   uwLastScannedChannel, index returned by the previous call
 * Output:  C_uwNetInvalidChannel
 *****************************************************************************/
tduwNetChannel uwL0PCWin95GSUBINextChannel(tduwNetChannel uwLastScannedChannel)
{
  return C_uwNetInvalidChannel;
}
 
/*****************************************************************************
 * Description: uwL0PCWin95GSUBIStartBroadcastChannelScan
 * returns the identification number of the first broadcast channel
 * for the protocol
 *****************************************************************************
 * Input:   none
 * Output:  0
 *****************************************************************************/
tduwNetChannel uwL0PCWin95GSUBIStartBroadcastChannelScan(void)
{
  if(!(gs_bL0PCWin95GSUBIInitState&0x02))
    return C_uwNetInvalidChannel;
 
  return 1;
}
 
/*****************************************************************************
 * Description: uwL0PCWin95GSUBINextBroadcastChannel
 * returns the identification number of the broadcast channel following
 * the specified one
 * return always C_uwNetInvalidChannel because there is one channel broadcast.
 *****************************************************************************
 * Input:   uwLastScannedChannel, index returned by the previous call
 * Output:  identification of the channel following the specified one
 *****************************************************************************/
tduwNetChannel uwL0PCWin95GSUBINextBroadcastChannel(tduwNetChannel uwLastScannedChannel)
{
  return C_uwNetInvalidChannel;
}
 
/*****************************************************************************
 * Description: L0PCWin95GSUBIReadCallBack
 * This function is call by the game service API when a message arrived.
 * The message is store in the incoming message list until the level one read it.
 *****************************************************************************
 * Input:   fromPid, id of the sender.
 *          buf, adress of the buffer message.
 *          size, size of the message.
 * Output:  none.
 *****************************************************************************/
static void GS_FUNCTIONCALL L0PCWin95GSUBIReadCallBack(tdPlayerId fromPid, char *buf, short size)
{
  FIFO *cour;
#if defined(NET_USE_DEBUG)
  GSUBIHead *pSend=(GSUBIHead *)buf;
vDebugSISISI(Net_C_Debug_GSUbi,"Receive Type",pSend->eMessageType,"Size",size-sizeof(GSUBIHead),"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: wL0PCWin95GSUBIGetMessage
 * Get a message if it's possible.
 *****************************************************************************/
short wL0PCWin95GSUBIGetMessage(tdstNetMessage **ppMes)
{
  FIFO *cour;
  tdstNetMessage *pMes;
  GSUBIHead *pSend;
  if (FIFObegin==NULL)
  {
    *ppMes=NULL;
    return 0;
  }
  else
  {
    pMes=(tdstNetMessage *)pMalloc(FIFObegin->size+sizeof(tdstNetMessage)-sizeof(GSUBIHead));
    if (pMes==NULL)
    {
      *ppMes=NULL;
      return -1;
    }
    pSend=(GSUBIHead *)(FIFObegin+1);
    memcpy(pMes+1,pSend+1,FIFObegin->size-sizeof(GSUBIHead));
    pMes->m_uxReadReceiptId=pSend->m_uxReadReceiptId;
    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(GSUBIHead));
    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: eL0PCWin95GSUBIReadData
 *****************************************************************************
 * 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 eL0PCWin95GSUBIReadData(tduwNetChannel *p_uwChannel, tdpPointer *ppData)
{
  short r;
 
 /* no : try to get a message from the FIFO */
  r=wL0PCWin95GSUBIGetMessage((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: eL0PCWin95GSUBISendData
 *****************************************************************************
 * Input:   uwChannel, channel to send the message into.
 *          pData, pointer on the block to send
 * Output:  an error condition.
 *****************************************************************************/
NetLib_tdeErrorStatus eL0PCWin95GSUBISendData(tduwNetChannel uwChannel, tdpPointer pData)
{
  unsigned long ulBytestoSend;
  tdstNetMessage *pMes=(tdstNetMessage *)pData;
  int NumErr=0;
  short RepFlag;
  GSUBIHead *pSend;
  unsigned long NewTime;
  NetLib_tduxPlayerId uxRecipient;
  if(!(gs_bL0PCWin95GSUBIInitState&0x02))
    return NetLib_E_es_InvalidChannel;
 
  uxRecipient=pMes->uxRecipientId;
  /* verify the validity of the Channel */
  if(uwChannel>1)
  {
    uxRecipient=uwChannel-2;
    uwChannel=0;
  }
  else if (uxRecipient==C_uxNetBroadcastId) uwChannel=1;
 
 
  ulBytestoSend= pMes->uwMessageSizeInBytes + sizeof(GSUBIHead);
 
  NewTime=GetTickCount();
  SendBufSize-=((NewTime-LastTime)*(float)SendMaxRate)/1000;
  LastTime=NewTime;
  if (SendBufSize<0) SendBufSize=0;
  if (SendBufSize>=GSUBI_MAXBUF)
  {
    #if defined(NET_USE_DEBUG)
      vDebugS(Net_C_Debug_GSUbi,"Controle de flot.");
    #endif
    return NetLib_E_es_BufferIsFull;
  }
  SendBufSize+=ulBytestoSend;
 
  pSend=(GSUBIHead *)pMalloc(ulBytestoSend);
  if (!pSend)
  {
    #if defined(NET_USE_DEBUG)
      vDebugS(Net_C_Debug_GSUbi,"Manque de mémoire.");
    #endif
    return NetLib_E_es_NotEnoughMemory;
  }
 
  pSend->m_uxReadReceiptId=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;
 
  memcpy(pSend+1,pMes+1,pMes->uwMessageSizeInBytes);
 
  if (pMes->uxReplace) RepFlag=0x80 | pMes->uxPriority;
  else RepFlag=0;
 
  if (uwChannel!=1)
  {
    #if defined(NET_USE_DEBUG)
      vDebugSISISI(Net_C_Debug_GSUbi,"Send to one player. Type",pMes->eMessageType,"Size",pMes->uwMessageSizeInBytes,"Recipient",uxRecipient);
    #endif
    NumErr=GS_vSendToOnePlayer((short)(uxRecipient),(char *)pSend,(short)ulBytestoSend,RepFlag);
  }
  else
  {
    #if defined(NET_USE_DEBUG)
      vDebugSISISI(Net_C_Debug_GSUbi,"Send to all players. Type",pMes->eMessageType,"Size",pMes->uwMessageSizeInBytes,"Recipient",uxRecipient);
    #endif
    NumErr=GS_vSendToAllPlayers((char *)pSend,(short)ulBytestoSend,0,RepFlag);
  }
 
  vFree((char *)pSend);
  if (NumErr)
  {
    #if defined(NET_USE_DEBUG)
      vDebugSI(Net_C_Debug_GSUbi,"Emission en attente",NumErr);
    #endif
    return NetLib_E_es_BufferIsFull;
  }
 
  /* free the message buffer */
  vFree(pData);
 
  return NetLib_E_es_NoError;
}
 
/*****************************************************************************
 * Description: eL0PCWin95GSUBIQueryChannelStatus
 * returns the current status of the channel
 *****************************************************************************
 * Input:   uwChannel, channel to query
 * Output:  an error condition.
 *****************************************************************************/
NetLib_tdeErrorStatus eL0PCWin95GSUBIQueryChannelStatus(tduwNetChannel uwChannel)
{
  return NetLib_E_es_NoError;
}
 
/*****************************************************************************
 * Description: eL0PCWin95GSUBIEngine
 *****************************************************************************
 * Input:   none
 * Output:  none
 *****************************************************************************/
void eL0PCWin95GSUBIEngine(void)
{
  char szBuffer[128];
  if(gs_bL0PCWin95GSUBIInitState&0x02) GS_lEngine(szBuffer);
}
 
/*****************************************************************************
 * Description : vL0PCWin95GSUBICloseProtocol(void)
 * GSUBI-Level 0 closing function
 *****************************************************************************
 * Input : none
 * Output : none
 *****************************************************************************/
void vL0PCWin95GSUBICloseProtocol(void)
{
  FIFO *p;
  while (FIFObegin)
  {
    p=FIFObegin->next;
    vFree((char*)FIFObegin);
    FIFObegin=p;
  }
  FIFObegin=NULL;
  FIFOend=&FIFObegin;
 
  vLevel1RemoveProtocol(E_Net_pr_Windows95GSUBIProtocol);
}
 
/*****************************************************************************
 * Description: L0PCWin95GSUBINewPlayerCallBack
 * This function is call by the game service API when a new player join the session.
 *****************************************************************************
 * Input:   pid = id of the player.
 *          name = name of the player for the game service.
 *          options = options player for the game service.
 *          size = size of the options.
 *          isYou = true if the player is the local player.
 *          isMaster = true if the player is the master.
 * Output:  none
 *****************************************************************************/
void GS_FUNCTIONCALL L0PCWin95GSUBINewPlayerCallBack(tdPlayerId pid,char *name,char *options,short size,int isYou,int isMaster)
{
  NetLib_tdstAddPlayerDesc PlDesc;
  short j=NetLib_Join_Join;
  char nameopt[500];
#if defined(NET_USE_DEBUG)
vDebugSIS(Net_C_Debug_GSUbi,"NewPlayerCallBack Pid",pid,name);
#endif
 
  if (isYou)
  {
#if defined(NET_USE_DEBUG)
vDebugS(Net_C_Debug_GSUbi,"Is me");
#endif
    gs_InfoLocal.pid = pid;
    gs_InfoLocal.joinType = j;
  }
  if (isMaster)
  {
    gs_IdMaster=pid;
  }
  PlDesc.m_tduxPlayerId=pid;
/*  PlDesc.m_pPlayerDescriptionData=options;*/
/*  PlDesc.m_ucPlayerDesciptionLength=size;*/
  PlDesc.m_pPlayerDescriptionData=NULL;
  PlDesc.m_uxPlayerDesciptionLength=0;
  PlDesc.m_stL1AddPlayerDesc.m_eProtocol=E_Net_pr_Windows95GSUBIProtocol;
  PlDesc.m_stL1AddPlayerDesc.m_vL0Param=NULL;
  PlDesc.IsYou=isYou;
  PlDesc.JoinType=j;
  sprintf(nameopt,"name '%s'",name);
  PlDesc.Options=nameopt;
  NetLib_eAddNewPlayer(&PlDesc);
}
 
 
/*****************************************************************************
 * Description: L0PCWin95GSUBIDoPlayerLeft
 * A user has left the game. This function is call by the game service API.
 *****************************************************************************
 * Input:   pid : ID of player who left the game.
 * Output:  none
 *****************************************************************************/
static void GS_FUNCTIONCALL L0PCWin95GSUBIDoPlayerLeft(tdPlayerId pid, int isme)
{
#if defined(NET_USE_DEBUG)
vDebugSI(Net_C_Debug_GSUbi,"DoPlayerLeft Pid",pid);
#endif
  if (!isme) eNetDisconnectPlayer((NetLib_tduxPlayerId)(pid),1);
}
 
/*****************************************************************************
 * Description: vL0PCWin95GSUBIAddNewPlayer
 *****************************************************************************
 * Input:   pChanel : adress of returned chanel.
 *          par     : extern parameter.
 * Output:  error code.
 *****************************************************************************/
NetLib_tdeErrorStatus vL0PCWin95GSUBIAddNewPlayer(tduwNetChannel *pChanel,void *par)
{
  *pChanel=0;
  return NetLib_E_es_NoError;
}
 
/*****************************************************************************
 * Description: eL0PCWin95GSUBIConnectionResult
 * Return connection result from tcp to game service.
 * Level 0 TCP call this function when the result of connection is know.
 *****************************************************************************
 * Input:   Result
 * Output:  none
 *****************************************************************************/
void vL0PCWin95GSUBIConnectionResult(int iRes)
{
  GS_vNetLibConnectionResult(iRes);
}
 
/*****************************************************************************
 * Description: L0PCWin95GSUBIRateCallBack
 * This callback is call by the game service API for control the sending rate.
 *****************************************************************************
 * Input:   ulRate, maximum rate of emission.
 *****************************************************************************/
static void GS_FUNCTIONCALL L0PCWin95GSUBIRateCallBack(unsigned long ulRate)
{
  #if defined(GSUBI_TRACE)
  DebugSI("Maximum rate",ulRate);
  #endif
  SendMaxRate=ulRate;
}
 
/*****************************************************************************
 * Description: vL0PCWin95GSUBIOpenProtocol
 * 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_ vL0PCWin95GSUBIOpenProtocol(void)
{
  tdstNetProtocolInterface *p_stProtocolInterface;
 
  p_stProtocolInterface=pstLevel1AddProtocol();
 
  FIFObegin=NULL;
  FIFOend=&FIFObegin;
 
    /* logical identification of the protocol */
  p_stProtocolInterface->eProtocol = E_Net_pr_Windows95GSUBIProtocol;
 
    /* setup the function pointers */
  p_stProtocolInterface->fn_uwStartChannelScan = uwL0PCWin95GSUBIStartChannelScan;
  p_stProtocolInterface->fn_uwStartBroadcastChannelScan= uwL0PCWin95GSUBIStartBroadcastChannelScan;
  p_stProtocolInterface->fn_uwNextChannel= uwL0PCWin95GSUBINextChannel;
  p_stProtocolInterface->fn_uwNextBroadcastChannel= uwL0PCWin95GSUBINextBroadcastChannel;
  p_stProtocolInterface->fn_eReadData= eL0PCWin95GSUBIReadData;
  p_stProtocolInterface->fn_eSendData= eL0PCWin95GSUBISendData;
  p_stProtocolInterface->fn_eQueryChannelStatus= eL0PCWin95GSUBIQueryChannelStatus;
  p_stProtocolInterface->fn_vLevel0NetEngine = eL0PCWin95GSUBIEngine;
  p_stProtocolInterface->fn_vCloseChannel = vL0PCWin95GSUBICloseChannel;
  p_stProtocolInterface->fn_vLevel0CloseProtocol = vL0PCWin95GSUBICloseProtocol;
  p_stProtocolInterface->fn_eAddNewPlayer = vL0PCWin95GSUBIAddNewPlayer;
 
  gs_bL0PCWin95GSUBIInitState = 0x01;
#if defined(NET_USE_DEBUG)
vDebugClear(Net_C_Debug_GSUbi);
vDebugS(Net_C_Debug_GSUbi,"SetupInterface");
#endif
 
  vL0PCWin95TCPGSSetCallBackResult(vL0PCWin95GSUBIConnectionResult);
 
  GS_vSetRmPlayerCallBack(L0PCWin95GSUBIDoPlayerLeft);
  GS_vSetNewPlayerCallBack(L0PCWin95GSUBINewPlayerCallBack);
  GS_vSetMsgReceivedCallBack(L0PCWin95GSUBIReadCallBack);
  GS_vSetRateCtrlCallBack(L0PCWin95GSUBIRateCallBack);
 
  GS_vSetSetPlayerCallBack(vL0PCWin95TCPGSSetPLayer);
  GS_vSetSetChannelCallBack(vL0PCWin95TCPGSSetChannel);
  GS_vSetInitiateConnectionCallBack(vL0PCWin95TCPGSInitiateConnection);
  SendBufSize=0;
  LastTime=GetTickCount();
  SendMaxRate=1000000L;
}
 
/*****************************************************************************
 * Description: eL0PCWin95GSUBIStart
 * Start a game with GSUBI.
 * Initialise the incoming message list. State flag are set for call to GS_lEngine.
 *****************************************************************************
 * Input:   none
 * Output:  none
 *****************************************************************************/
void _NET_CALLING_CONV_ vL0PCWin95GSUBIStart(void)
{
  FIFO *p;
#if defined(NET_USE_DEBUG)
vDebugS(Net_C_Debug_GSUbi,"Start");
#endif
  while (FIFObegin)
  {
    p=FIFObegin->next;
    vFree((char*)FIFObegin);
    FIFObegin=p;
  }
  FIFObegin=NULL;
  FIFOend=&FIFObegin;
  gs_bL0PCWin95GSUBIInitState|=0x02;
  SendBufSize=0;
  LastTime=GetTickCount();
  SendMaxRate=1000000L;
}
 
/*****************************************************************************
 * Description: vL0PCWin95GSUBIFinish
 * Finish a game with GSUBI.
 * This function erase the list of incoming message, disconnect all remote players,
 * close all TCP port, and send message to game service for quit the game mode.
 *****************************************************************************
 * Input:   none
 * Output:  none
 *****************************************************************************/
void _NET_CALLING_CONV_ vL0PCWin95GSUBIFinish(void)
{
  FIFO *p;
#if defined(NET_USE_DEBUG)
vDebugS(Net_C_Debug_GSUbi,"Finish");
#endif
  while (FIFObegin)
  {
    p=FIFObegin->next;
    vFree((char*)FIFObegin);
    FIFObegin=p;
  }
  FIFObegin=NULL;
  FIFOend=&FIFObegin;
  GS_vQuitSession();
  gs_bL0PCWin95GSUBIInitState&=~0x02;
  SendBufSize=0;
  LastTime=GetTickCount();
  SendMaxRate=1000000L;
  NetLib_vDisconnectAllRemotePlayers();
  vL0PCWin95TCPCloseListenPort();
}
 
/*****************************************************************************
 * Description: eL0PCWin95GSUBIMasterId
 * Return the master Id. Master is the player who created the session for the
 * game service.
 *****************************************************************************/
NetLib_tduxPlayerId _NET_CALLING_CONV_ eL0PCWin95GSUBIMasterId(void)
{
  return gs_IdMaster;
}
 
/*****************************************************************************
 * Description : eL0PCWin95GSUBIIsProtocolSet
 * Check if the protocol is correctly initialize or not
 *****************************************************************************
 * Input : none
 * Output :
 * NetLib_E_es_ProtocolNotInitialized : the vLevel0SetupWin95PCGSUBIInterface has not
 * been called yet or it has failed, the application should call the level 2
 * eInitializeGlobalData function.
 *****************************************************************************/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95GSUBIIsProtocolSet(void)
{
  if(!(gs_bL0PCWin95GSUBIInitState&0x01)) return NetLib_E_es_ProtocolNotInitialized;
  return NetLib_E_es_True;
}
 
/*****************************************************************************
 * Description : eL0PCWin95GSUBIIsProtocolAvailable
 * Not use for this protocol.
 *****************************************************************************
 * Output : NetLib_E_es_True if the port is avaible
 * An error code otherwise
 *****************************************************************************/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95GSUBIIsProtocolAvailable(void)
{
  return NetLib_E_es_True;
}
