/*
 * L0DPlay.c
 *
 * universal multiplayer library level 0 implementation file
 * transport layer: DirectPlay lobby aware PC-windows 95
 */
 
/*
 *      Author: Christophe Roguet
 *
 *      Creation Date:      04/08/97
 *
 */
 
 
#include "warnings.h"
 
#include <stdlib.h>
/* 
 * we need this one to fill the interface structure 
 * with pointers to the correct functions 
 */
#include "PrivNetDef.h"
#include "l0GlDef.h"
#include "NetMemCo.h"
#include "NetEnd.h"
#include "PrivNetSer.h"
#include <NET\Netdebug.h>
#include <NET\l0DPlay.h>
 
#define IDIRECTPLAY2_OR_GREATER
#include <dplobby.h>
 
#define DPLAY_MAXBUF 250
 
static char gs_bL0PCWin95DPlayInitState;
 
#pragma pack(push)
#pragma pack(1)
 
typedef struct
{
  DPID DPlayPlayerId;                                           /* DPlay ID of the local player*/
  NetLib_tduxPlayerId uxNetLibPlayerId;         /* NetLib ID of the local 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 eL0PCWin95DPlayEngine. */
static unsigned long SendMaxRate;           /* Maximum rate. */
static unsigned short NbrPlayer;            /* Number of player. */
static NetLib_tdstInfoPlayer *TabPlayer;    /* Table of player. */
 
typedef struct {
        NetLib_tduxPlayerId uxNetLibPlayerId;
        DPID DPlayPlayerId;
        NetLib_tdstAddPlayerDesc *pstPlDesc;
        char *pcDPlayPlayerName;
} tdstConvLine;
 
typedef struct {
        unsigned short uwMaxSize;
        unsigned short uwSize;
        tdstConvLine *dastConv;
} tdstConvTable;
 
static tdstConvTable gs_stConvTable;
 
typedef struct stFIFO
{
  struct stFIFO *next;                 /* Next message of FIFO. */
  unsigned long size;                  /* Size of message. */
  DPID DPlayIdFrom;                            /* Direct Play ID of sender. */
  NetLib_tduxPlayerId uxSenderId;          /* NetLib 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;
        unsigned long uxDPlaySpecific:2;
} DPlayHead;
 
typedef enum {
        NetLib_ePureNetLib, NetLib_eNewPlayerId, NetLib_eIdTable
} tdeDPlayMsgType;
 
typedef struct {
        DPID DPlayPlayerId;
        NetLib_tduxPlayerId uxNetLibPlayerId;
} tdstConversionMsg;
 
enum { NetLib_DPlayWaiting, NetLib_DPlayGo };
 
#pragma pack(pop)
 
static LPDIRECTPLAY3                    glpDP;                  /* directplay object pointer*/
 
static LPDPLCONNECTION          glpdplConnection;       /* connection settings*/
 
static LPDIRECTPLAYLOBBY2       glpDPL;                 /* lobby object pointer*/
 
static DPID                                             pidLocalPlayer;                 /* Id of local player*/
 
static NetLib_tduxPlayerId              gs_uxPlayerIdGen;               /* used to generate player ids */
 
BOOL FAR PASCAL NewPlayerCallback(DPID PlayerId, DWORD dwPlayerType, LPCDPNAME lpName, DWORD dwFlags, LPVOID lpContext);
 
void NetLib_RegisterNewPlayer1(DPID PlayerId, char *lpName);
 
void NetLib_RegisterNewPlayer2(DPID DPlayPlayerId, NetLib_tduxPlayerId uxNetLibPlayerId);
 
void NetLib_InitConversionTable(unsigned short uwMaxSize);
 
void NetLib_DeinitConversionTable();
 
void NetLib_AddConversionEntry(DPID DPlayPlayerId, NetLib_tdstAddPlayerDesc *pstPlDesc, char *);
 
void NetLib_SuppressConversionEntry(DPID DPlayerId);
 
DPlayHead *NetLib_BuildConversionMsg(DPID IdTo, unsigned short *);
 
DPID NetLib_NetLibToDPlay(NetLib_tduxPlayerId uxNetLibPlayerId);
NetLib_tduxPlayerId NetLib_DPlayToNetLib(DPID DPlayPlayerId);
NetLib_tduxPlayerId GeneratePlayerId(void);
 
void NetLib_DPlayHandleNewMsg(FIFO *pstFIFOCell, DPID IdTo);
void NetLib_DPlayHandleNewPlayerMsg(FIFO *pstFIFOCell, DPID IdTo);
void NetLib_DPlayHandlePlayerLeftMsg(FIFO *pstFIFOCell, DPID IdTo);
void NetLib_DPlayHandleSpecificMsg(FIFO *pstFIFOCell, DPID IdTo);
 
 
/******************************************************************************************/
 
/*
 * DPLobbyCreate
 *
 * Wrapper for DirectPlayLobby DirectPlayLobbyCreate API.
 */
HRESULT DPLobbyCreate(void)
{
    HRESULT hr=E_FAIL;
 
    LPDIRECTPLAYLOBBY local_lpDPL;                  /* directplay object pointer*/
 
    hr = DirectPlayLobbyCreate(NULL, &local_lpDPL, NULL, NULL, 0);
 
        if(hr != DP_OK) {
#ifdef NET_USE_DEBUG
                vDebugFormat(Net_C_Debug_DPlay, "DPLobbyCreate : DPLCreate : error %lx", hr);
#endif
                return hr;
        }
 
        hr = IDirectPlayLobby_QueryInterface(local_lpDPL, &IID_IDirectPlayLobby2A, (LPVOID *)&glpDPL);
 
        if(hr != DP_OK) {
#ifdef NET_USE_DEBUG
                vDebugFormat(Net_C_Debug_DPlay, "DPLobbyCreate : DPLQueryInterface : error %lx", hr);
#endif
                return hr;
        }
 
        hr = IDirectPlayLobby_Release(local_lpDPL);
 
        if(hr != DP_OK) {
#ifdef NET_USE_DEBUG
                vDebugFormat(Net_C_Debug_DPlay, "DPLobbyCreate : DPLRelease : error %lx", hr);
#endif
        }
 
    return hr;
}
 
/******************************************************************************************/
 
/*
 * DPClose
 *
 * Wrapper for DirectPlay Close API
 */
HRESULT DPClose(void)
{
    HRESULT hr=E_FAIL;
 
    /* release the direct play object*/
    if (glpDP)
    {
        hr = IDirectPlay3_Close(glpDP);
 
                if(hr != DP_OK) {
#ifdef NET_USE_DEBUG
                        vDebugFormat(Net_C_Debug_DPlay, "DPClose : error %lx", hr);
#endif
                }
    }
    return hr;
}
 
/******************************************************************************************/
 
/*
 * DPRelease
 *
 * Wrapper for DirectPlay Release API
 */
HRESULT DPRelease(void)
{
    HRESULT hr=E_FAIL;
 
    /* release the lobby object*/
    if (glpDP)
    {
        hr = IDirectPlay3_Release(glpDP);
 
                if(hr != DP_OK) {
#ifdef NET_USE_DEBUG
                        vDebugFormat(Net_C_Debug_DPlay, "DPRelease : error %lx", hr);
#endif
                }
 
        glpDP = NULL;
    }
    return hr;
}
 
/******************************************************************************************/
 
/*
 * DPLobbyRelease
 *
 * Wrapper for DirectPlayLobby Release API
 */
HRESULT DPLobbyRelease(void)
{
    HRESULT hr=E_FAIL;
 
    /* free our connection settings*/
    if (glpdplConnection)
    {
        vFree(glpdplConnection);
        glpdplConnection = NULL;
    }
 
    /* release the lobby object*/
    if (glpDPL)
    {
        hr = IDirectPlayLobby_Release(glpDPL);
 
                if(hr != DP_OK) {
#ifdef NET_USE_DEBUG
                        vDebugFormat(Net_C_Debug_DPlay, "DPLobbyRelease : DPLRelease : error %lx", hr);
#endif
                }
 
        glpDPL = NULL;
    }
    return hr;
}
 
/******************************************************************************************/
/*
 * DPLobbyGetConnectionSettings
 *
 * Wrapper for DirectPlayLobby GetConnectionSettings API
 */
 
HRESULT DPLobbyGetConnectionSettings(void)
{
    HRESULT hr=E_FAIL;
    DWORD dwSize;
 
    if (glpDPL)
    {
        /* get size for the connection settings structure*/
        hr = IDirectPlayLobby_GetConnectionSettings(glpDPL, 0, NULL, &dwSize);
 
        if (DPERR_BUFFERTOOSMALL == hr)
        { 
            /* if we already have one, free it*/
            if (glpdplConnection)
            {
                vFree(glpdplConnection);
                glpdplConnection = NULL;
            }
 
            /* allocate memory for the new one*/
            glpdplConnection = (LPDPLCONNECTION) pMalloc(dwSize);
 
            /* get the connection settings*/
            if (glpdplConnection)
                hr = IDirectPlayLobby_GetConnectionSettings(glpDPL, 0, glpdplConnection, &dwSize);
 
                        if(hr != DP_OK) {
#ifdef NET_USE_DEBUG
                                vDebugFormat(Net_C_Debug_DPlay, "DPLobbyGetConnectionSettings : DPLGetConnectionSettings : error %lx", hr);
#endif
                        }
        } else if(hr != DP_OK) {
#ifdef NET_USE_DEBUG
                        vDebugFormat(Net_C_Debug_DPlay, "DPLobbyGetConnectionSettings : DPLGetConnectionSettings : error %lx", hr);
#endif
                }
    }
 
    return hr;
}
 
/******************************************************************************************/
 
/*
 * DPLobbyConnect
 *
 * Wrapper for DirectPlayLobby Connect API.
 */
 
HRESULT DPLobbyConnect(void)
{
    HRESULT hr=E_FAIL;
        static LPDIRECTPLAY2                    local_lpDP;                  /* directplay object pointer*/
 
        if(glpDPL) {
                /* connect and retrieve a DirectPlay2 interface */
 
                hr = IDirectPlayLobby_Connect(glpDPL, 0, &local_lpDP, NULL) ;    
 
                if(hr != DP_OK) {
#ifdef NET_USE_DEBUG
                        vDebugFormat(Net_C_Debug_DPlay, "DPLobbyConnect : DPLConnect : error %lx", hr);
#endif
                }
 
                hr = IDirectPlayLobby_Release(glpDPL);
                glpDPL=NULL;
 
                if(hr != DP_OK) {
#ifdef NET_USE_DEBUG
                        vDebugFormat(Net_C_Debug_DPlay, "DPLConnect : DPLRelease : error %lx", hr);
#endif
                }
 
                /* retrieve a DirectPlay3 interface from the DirectPlay2 interface */
 
                if(local_lpDP) {
 
                        hr = IDirectPlay2_QueryInterface(local_lpDP, &IID_IDirectPlay3A, (LPVOID *)&glpDP);
 
                        if(hr != DP_OK) {
 
#ifdef NET_USE_DEBUG
                                vDebugFormat(Net_C_Debug_DPlay, "DPLConnect : DP2QueryInterface : error %lx", hr);
#endif
                        }
 
                        hr = IDirectPlay2_Release(local_lpDP);
 
                        if(hr != DP_OK) {
 
#ifdef NET_USE_DEBUG
                                vDebugFormat(Net_C_Debug_DPlay, "DPLConnect : DP2Release: error %lx", hr);
#endif
                        }
                }
        }
 
    return hr;
}
 
/******************************************************************************************/
 
/*
 * DPCreatePlayer
 * wrapper for CreatePlayer
 */
 
HRESULT DPCreatePlayer(void)
{
    HRESULT hr=E_FAIL;
 
        if(glpDP) {
#ifdef NET_USE_DEBUG
                vDebugFormat(Net_C_Debug_DPlay, "DPCreatePlayer : ConnectionSettings");
                vDebugFormat(Net_C_Debug_DPlay, "dwFlags: %lx", glpdplConnection->dwFlags);
                if(glpdplConnection->dwFlags==DPLCONNECTION_CREATESESSION)
                        vDebugFormat(Net_C_Debug_DPlay, "DPLCONNECTION_CREATESESSION");
                if(glpdplConnection->dwFlags==DPLCONNECTION_JOINSESSION)
                        vDebugFormat(Net_C_Debug_DPlay, "DPLCONNECTION_JOINSESSION");
                vDebugFormat(Net_C_Debug_DPlay, "PlayerName: %s", glpdplConnection->lpPlayerName->lpszShortNameA);
#endif
 
                hr = IDirectPlay3_CreatePlayer(glpDP, &pidLocalPlayer, glpdplConnection->lpPlayerName, NULL, NULL, 0, 0);
 
                if(hr != DP_OK) {
#ifdef NET_USE_DEBUG
                                vDebugFormat(Net_C_Debug_DPlay, "DPCreatePlayer : error %lu", hr);
#endif
                }
 
                if(glpdplConnection->dwFlags==DPLCONNECTION_CREATESESSION)
                        gs_JoinType=NetLib_Join_Create;
                else if(glpdplConnection->dwFlags==DPLCONNECTION_JOINSESSION)
                        gs_JoinType=NetLib_Join_Join;
        }
 
    return hr;
}
 
 
/******************************************************************************************/
 
/*
 * DPEnumPlayers
 * wrapper for DPEnumPlayers
 */
 
HRESULT DPEnumPlayers(void)
{
    HRESULT hr=E_FAIL;
 
        if(glpDP) {
 
                hr = IDirectPlay3_EnumPlayers(glpDP, NULL, (LPDPENUMPLAYERSCALLBACK2)NewPlayerCallback, 0, 0);
 
                if(hr != DP_OK) {
#ifdef NET_USE_DEBUG
                                vDebugFormat(Net_C_Debug_DPlay, "DPEnumPlayers : DP3EnumPLayers : error %lx", hr);
#endif
                }
        }
 
        return hr;
}
 
/******************************************************************************************/
 
BOOL FAR PASCAL NewPlayerCallback(DPID PlayerId, DWORD dwPlayerType, LPCDPNAME lpName, DWORD dwFlags, LPVOID lpContext)
 
{
#if defined(NET_USE_DEBUG)
        vDebugFormat(Net_C_Debug_DPlay,"NewPlayerCallBack : %lx", PlayerId);
#endif
 
        if (PlayerId == pidLocalPlayer) {
#if defined(NET_USE_DEBUG)
vDebugS(Net_C_Debug_DPlay,"Is me");
#endif
                gs_InfoLocal.DPlayPlayerId = PlayerId;
                gs_InfoLocal.joinType = gs_JoinType;
                gs_uwCallBack|=1;
        }
 
        NetLib_RegisterNewPlayer1(PlayerId, lpName->lpszShortNameA);
 
        /* if we are the session host, we can finish registering ourself */
 
        if (gs_JoinType==NetLib_Join_Create)
                NetLib_RegisterNewPlayer2(PlayerId, GeneratePlayerId());
 
        return TRUE; /* request next player */
}
 
/******************************************************************************/
 
void NetLib_RegisterNewPlayer1(DPID PlayerId, char *lpName)
 
{
        NetLib_tdstAddPlayerDesc *pstPlDesc;
        char *acDPlayPlayerName;
 
        /* register a new player: first part */
        /* add an entry to the conversion table */
        /* and wait for the NetLib Player Id */
 
#if defined(NET_USE_DEBUG)
        vDebugFormat(Net_C_Debug_DPlay,"Register New Player 1: %lx / %s", PlayerId, lpName);
#endif
 
        pstPlDesc=(NetLib_tdstAddPlayerDesc *)pMalloc(sizeof(NetLib_tdstAddPlayerDesc));
 
        pstPlDesc->m_tduxPlayerId=C_uxNetInvalidId;
        pstPlDesc->m_pPlayerDescriptionData=C_pNull; /* at first, let's not deal with player description */
        pstPlDesc->m_uxPlayerDesciptionLength=0;
        pstPlDesc->m_stL1AddPlayerDesc.m_eProtocol=E_Net_pr_Windows95DPlayProtocol;
        pstPlDesc->m_stL1AddPlayerDesc.m_vL0Param=NULL;
        pstPlDesc->IsYou=PlayerId == pidLocalPlayer;
 
        pstPlDesc->JoinType=gs_JoinType;
 
        pstPlDesc->Options=NULL;
 
        acDPlayPlayerName=strdup(lpName);
 
        NetLib_AddConversionEntry(PlayerId, pstPlDesc, acDPlayPlayerName);
}
 
/******************************************************************************/
 
void NetLib_RegisterNewPlayer2(DPID DPlayPlayerId, NetLib_tduxPlayerId uxNetLibPlayerId)
 
{
        /* register a new player: part 2 */
        /* complete the conversion table entry */
        /* and transmit the information to higher levels */
 
        unsigned short i;
 
#if defined(NET_USE_DEBUG)
        vDebugFormat(Net_C_Debug_DPlay,"Register New Player 2: %lx / %lu", DPlayPlayerId, uxNetLibPlayerId);
#endif
 
        if(DPlayPlayerId==gs_InfoLocal.DPlayPlayerId)
                gs_InfoLocal.uxNetLibPlayerId = uxNetLibPlayerId;
 
        for(i=0; i<gs_stConvTable.uwSize; i++)
                if(gs_stConvTable.dastConv[i].DPlayPlayerId == DPlayPlayerId) {
                        gs_stConvTable.dastConv[i].uxNetLibPlayerId=uxNetLibPlayerId;
                        if(gs_stConvTable.dastConv[i].pstPlDesc) {
                                gs_stConvTable.dastConv[i].pstPlDesc->m_tduxPlayerId=uxNetLibPlayerId;
                                NetLib_eAddNewPlayer(gs_stConvTable.dastConv[i].pstPlDesc);
                                vFree(gs_stConvTable.dastConv[i].pstPlDesc);
                                gs_stConvTable.dastConv[i].pstPlDesc=(NetLib_tdstAddPlayerDesc *)C_pNull;
                                NbrPlayer++;
                        } else {
#if defined(NET_USE_DEBUG)
                                vDebugFormat(Net_C_Debug_DPlay,"Register New Player 2: no description for %lx / (i=%hu) ???", DPlayPlayerId, i);
#endif
                        }
 
                }
 
}
 
/******************************************************************************/
 
void NetLib_InitConversionTable(unsigned short uwMaxSize)
 
{
        gs_stConvTable.uwSize=0;
        gs_stConvTable.uwMaxSize=uwMaxSize;
        gs_stConvTable.dastConv=(tdstConvLine *)pMalloc(gs_stConvTable.uwMaxSize*sizeof(tdstConvLine));
}
 
/******************************************************************************/
 
void NetLib_DeinitConversionTable()
 
{
        unsigned short i;
 
        for(i=0; i<gs_stConvTable.uwSize; i++)
                if(gs_stConvTable.dastConv[i].pstPlDesc != (NetLib_tdstAddPlayerDesc*)C_pNull) {
                        vFree(gs_stConvTable.dastConv[i].pstPlDesc);
                        gs_stConvTable.dastConv[i].pstPlDesc=(NetLib_tdstAddPlayerDesc*)C_pNull;
                }
 
        vFree(gs_stConvTable.dastConv);
        gs_stConvTable.dastConv=(tdstConvLine *)C_pNull;
 
        gs_stConvTable.uwSize=gs_stConvTable.uwMaxSize=0;
}
 
/******************************************************************************/
 
void NetLib_AddConversionEntry(DPID DPlayPlayerId, NetLib_tdstAddPlayerDesc *pstPlDesc, char *pcDPlayPlayerName)
 
{
        tdstConvLine *pstConvLine;
        tdstConvLine *pstConvTable;
 
        if(gs_stConvTable.uwSize==gs_stConvTable.uwMaxSize) {
                /* reallocate table */
                pstConvTable=(tdstConvLine *)pRealloc(gs_stConvTable.dastConv, (gs_stConvTable.uwSize+1)*sizeof(tdstConvLine));
 
                if(pstConvTable != (tdstConvLine *)C_pNull) {
                        gs_stConvTable.uwMaxSize++;
                        gs_stConvTable.dastConv=pstConvTable;
                } else
                        return;
        }
 
        pstConvLine=&gs_stConvTable.dastConv[gs_stConvTable.uwSize];
 
        pstConvLine->uxNetLibPlayerId=C_uxNetInvalidId;
        pstConvLine->DPlayPlayerId=DPlayPlayerId;
        pstConvLine->pstPlDesc=pstPlDesc;
        pstConvLine->pcDPlayPlayerName=pcDPlayPlayerName;
 
        gs_stConvTable.uwSize++;
}
 
/******************************************************************************/
 
void NetLib_SuppressConversionEntry(DPID DPlayPlayerId)
 
{
        unsigned short i;
 
        for(i=0; i<gs_stConvTable.uwSize; i++)
                if(gs_stConvTable.dastConv[i].DPlayPlayerId == DPlayPlayerId) {
                        if(gs_stConvTable.dastConv[i].pstPlDesc != (NetLib_tdstAddPlayerDesc *)C_pNull)
                                vFree(gs_stConvTable.dastConv[i].pstPlDesc);
                        vFree(gs_stConvTable.dastConv[i].pcDPlayPlayerName);
                        memmove(&gs_stConvTable.dastConv[i], 
                                        &gs_stConvTable.dastConv[i+1], 
                                         (gs_stConvTable.uwMaxSize-gs_stConvTable.uwSize)*sizeof(tdstConvLine));
                        gs_stConvTable.uwSize--;
                }
}
 
/*****************************************************************************/
 
DPlayHead *NetLib_BuildConversionMsg(DPID IdTo, unsigned short *p_uxSize)
 
{
 
        tdstConversionMsg *ConversionInfo;
        DPlayHead *pstSpecificMsg;
        unsigned short i;
 
        /* build a message that contains the conversion info for all players except the adressee */
        /* the adressee is normally the last entry in the conversion table */
 
        *p_uxSize=sizeof(DPlayHead)+sizeof(tdstConversionMsg)*(gs_stConvTable.uwSize-1);
 
        pstSpecificMsg=(DPlayHead *)pMalloc(*p_uxSize);
 
        if(pstSpecificMsg != (DPlayHead *)C_pNull) {
 
                pstSpecificMsg->uxDPlaySpecific=NetLib_eIdTable;
 
                ConversionInfo=(tdstConversionMsg *)(pstSpecificMsg+1);
 
                for(i=0; i<gs_stConvTable.uwSize-1; i++) /*if(gs_stConvTable.dastConv[i].DPlayPlayerId != IdTo)*/ {
                        ConversionInfo->DPlayPlayerId=gs_stConvTable.dastConv[i].DPlayPlayerId;
                        ConversionInfo->uxNetLibPlayerId=gs_stConvTable.dastConv[i].uxNetLibPlayerId;
                        ConversionInfo++;
                }
        }
 
        return pstSpecificMsg;
}
 
/******************************************************************************/
 
DPID NetLib_NetLibToDPlay(NetLib_tduxPlayerId uxNetLibPlayerId)
 
{
        unsigned short i;
 
        for(i=0; i<gs_stConvTable.uwSize; i++)
                if(gs_stConvTable.dastConv[i].uxNetLibPlayerId == uxNetLibPlayerId)
                        return gs_stConvTable.dastConv[i].DPlayPlayerId;
 
        return DPID_UNKNOWN;
}  
 
/******************************************************************************/
 
NetLib_tduxPlayerId NetLib_DPlayToNetLib(DPID DPlayPlayerId)
 
{
        unsigned short i;
 
        for(i=0; i<gs_stConvTable.uwSize; i++)
                if(gs_stConvTable.dastConv[i].DPlayPlayerId == DPlayPlayerId)
                        return gs_stConvTable.dastConv[i].uxNetLibPlayerId;
 
        return C_uxNetInvalidId;
}  
 
/******************************************************************************/
 
NetLib_tduxPlayerId GeneratePlayerId(void)
 
{
        if(gs_uxPlayerIdGen==127) { /* hope this never happens ... */
                        gs_uxPlayerIdGen=0;
                        return 0;
        }
 
        return gs_uxPlayerIdGen++;
}
 
/*****************************************************************************/
 
int CheckPlayerNbr()
 
{
        if(NbrPlayer < glpdplConnection->lpSessionDesc->dwMaxPlayers)
                return NetLib_DPlayWaiting;
        else
                return NetLib_DPlayGo;
}
 
/*****************************************************************************/
 
/*****************************************************************************
 * Description: vL0PCWin95DPlayCloseChannel
 * frees a slot in the channel table
 *****************************************************************************
 * Input:     identifier of the channel
 * Output:        none
 *****************************************************************************/
void vL0PCWin95DPlayCloseChannel(tduwNetChannel uwChannel)
{
}
 
/*****************************************************************************
 * Description: uwL0PCWin95DPlayStartChannelScan
 * returns the identification number of the first channel for the protocol
 *****************************************************************************
 * Input:   none
 * Output:  0
 *****************************************************************************/
tduwNetChannel uwL0PCWin95DPlayStartChannelScan(void)
{
  if(!(gs_bL0PCWin95DPlayInitState&0x02))
    return C_uwNetInvalidChannel;
    
  return 0;
}
 
/*****************************************************************************
 * Description: uwL0PCWin95DPlayNextChannel
 * returns the identification number of the channel following the last
 *****************************************************************************
 * Input:   uwLastScannedChannel, index returned by the previous call
 * Output:  C_uwNetInvalidChannel
 *****************************************************************************/
tduwNetChannel uwL0PCWin95DPlayNextChannel(tduwNetChannel uwLastScannedChannel)
{
 return C_uwNetInvalidChannel;
}
 
/*****************************************************************************
 * Description: uwL0PCWin95DPlayStartBroadcastChannelScan
 * returns the identification number of the first broadcast channel
 * for the protocol
 *****************************************************************************
 * Input:   none
 * Output:  0
 *****************************************************************************/
tduwNetChannel uwL0PCWin95DPlayStartBroadcastChannelScan(void)
{
  if(!(gs_bL0PCWin95DPlayInitState&0x02))
    return C_uwNetInvalidChannel;
 
  return 1;
}
 
/*****************************************************************************
 * Description: uwL0PCWin95DPlayNextBroadcastChannel
 * 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 uwL0PCWin95DPlayNextBroadcastChannel(tduwNetChannel uwLastScannedChannel)
{
  return C_uwNetInvalidChannel;
}
 
/*****************************************************************************
 * Description: wL0PCWin95DPlayGetMessage
 *****************************************************************************
 * Input:   ppData, address of the pointer to retrieve a full message if
 *          necessary.
 * Output:  return 1 if getting a message and 0 otherwise.
 *****************************************************************************/
short wL0PCWin95DPlayGetMessage(tdstNetMessage **ppMes)
{
  FIFO *currentFIFOCell;
  tdstNetMessage *pstMessage;
  DPlayHead *pstDPlayHead;
  if (FIFObegin==NULL)
  {
    *ppMes=NULL;
    return 0;
  }
  else
  {
        /* avant tout, tester si l'expediteur est connu pour NetLib */
        /* (et si on connait son propre id NetLib en local)         */
    pstMessage=(tdstNetMessage *)pMalloc(FIFObegin->size+sizeof(tdstNetMessage)-sizeof(DPlayHead));
 
        pstDPlayHead=(DPlayHead *)(FIFObegin+1);
 
    if (pstMessage==NULL) {
      *ppMes=NULL;
      return -1;
    }
 
        pstMessage->m_uxReadReceiptId=pstDPlayHead->m_ulReadReceiptId;
        pstMessage->uxPriority=pstDPlayHead->uxPriority;
        pstMessage->uxReplace=pstDPlayHead->uxReplace;
        pstMessage->uxReplaceType=pstDPlayHead->uxReplaceType;
        pstMessage->eMessageType=pstDPlayHead->eMessageType;
        pstMessage->uxHeadBigEndian=pstDPlayHead->uxHeadBigEndian;
        pstMessage->uxBodyBigEndian=pstDPlayHead->uxBodyBigEndian;
        pstMessage->uwMessageSizeInBytes=(unsigned short)(FIFObegin->size-sizeof(DPlayHead));
        pstMessage->uxSessionId=0;
        pstMessage->uxSenderId=FIFObegin->uxSenderId;
        pstMessage->uxRecipientId=gs_InfoLocal.uxNetLibPlayerId;
        memcpy(pstMessage+1, pstDPlayHead+1, pstMessage->uwMessageSizeInBytes);
        pstMessage->ulReserved=M_ulMakeReserved(M_uwProcessCheckSum(pstMessage),
                                      M_uwProcessCheckSumBody(pstMessage));
 
    currentFIFOCell=FIFObegin->next;
    vFree((char *)FIFObegin);
    FIFObegin=currentFIFOCell;
 
    if (currentFIFOCell==NULL) FIFOend=&FIFObegin;
    *ppMes=pstMessage;
 
#ifdef NET_USE_DEBUG
        vDebugFormat(Net_C_Debug_DPlay, "GetMessage(): Type %d, Size %d, Recipient %d", 
                pstMessage->eMessageType, 
                pstMessage->uwMessageSizeInBytes, 
                pstMessage->uxRecipientId);
#endif /* NET_USE_DEBUG */
    return 1;
  }
}
 
/*****************************************************************************
 * Description: eL0PCWin95DPlayReadData
 *****************************************************************************
 * 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 eL0PCWin95DPlayReadData(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=wL0PCWin95DPlayGetMessage((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: eL0PCWin95DPlaySendData
 *****************************************************************************
 * Input:   uwChannel, channel to send the message into.
 *          pData, pointer on the block to send
 * Output:  an error condition.
 *****************************************************************************/
NetLib_tdeErrorStatus eL0PCWin95DPlaySendData(tduwNetChannel uwChannel, tdpPointer pData)
{
  unsigned long ulBytestoSend;
  tdstNetMessage *pMes=(tdstNetMessage *)pData;
  DPlayHead *pSend;
  unsigned long NewTime;
  DPID DPlayDestinationId;
  DWORD dwFlags;
  HRESULT hr;
 
  /* verify that the system has been initialized */
 
  if(!(gs_bL0PCWin95DPlayInitState&0x02))
          return NetLib_E_es_ProtocolNotInitialized;
 
  /* verify the validity of the Channel */
  if(uwChannel>1)
  {
    #if defined(NET_USE_DEBUG)
      vDebugFormat(Net_C_Debug_DPlay,"Invalid channel %ud", uwChannel);
    #endif
    return NetLib_E_es_InvalidChannel;
  }
 
  if (pMes->uxRecipientId==C_uxNetBroadcastId) uwChannel=1;
 
  ulBytestoSend= pMes->uwMessageSizeInBytes + sizeof(DPlayHead);
 
  NewTime=GetTickCount();
  SendBufSize-=((NewTime-LastTime)*(float)SendMaxRate)/1000;
  LastTime=NewTime;
  if (SendBufSize<0) SendBufSize=0;
  if (SendBufSize>=DPLAY_MAXBUF) {
    #if defined(NET_USE_DEBUG)
      vDebugS(Net_C_Debug_DPlay,"limiting throughput");
    #endif
    return NetLib_E_es_BufferIsFull;
  }
 
  SendBufSize+=ulBytestoSend+20;
 
  pSend=(DPlayHead *)pMalloc(ulBytestoSend);
  if (!pSend) {
    #if defined(NET_USE_DEBUG)
      vDebugS(Net_C_Debug_DPlay,"out of memory");
    #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;
 
  pSend->uxDPlaySpecific=NetLib_ePureNetLib;
 
  if (uwChannel!=1) {
          DPlayDestinationId=NetLib_NetLibToDPlay(pMes->uxRecipientId);
    #if defined(NET_USE_DEBUG)
      vDebugFormat(Net_C_Debug_DPlay,"Send to one player. Type %d, Size %d, Recipient %d / %lx", pMes->eMessageType, pMes->uwMessageSizeInBytes, pMes->uxRecipientId, DPlayDestinationId);
    #endif
          dwFlags=DPSEND_GUARANTEED | DPSEND_OPENSTREAM;
  } else  {
 
    #if defined(NET_USE_DEBUG)
      vDebugFormat(Net_C_Debug_DPlay,"Send to all players. Type %d, Size %d, Recipient %d", pMes->eMessageType, pMes->uwMessageSizeInBytes, pMes->uxRecipientId);
    #endif
          DPlayDestinationId=DPID_ALLPLAYERS;
          dwFlags=DPSEND_GUARANTEED;
  }
 
  hr=E_FAIL;
 
  if(glpDP) {
          hr=IDirectPlay3_Send( glpDP, 
                                                gs_InfoLocal.DPlayPlayerId,
                                                DPlayDestinationId,
                                                dwFlags,
                                                (LPVOID)pSend,
                                                ulBytestoSend);
          if(hr != DP_OK) {
#ifdef NET_USE_DEBUG
                        vDebugFormat(Net_C_Debug_DPlay, "SendData : DP3Send : error %lx", hr);
#endif
                }
  }
 
  vFree((char *)pSend);
 
  if (hr==DPERR_BUSY)
  {
    #if defined(NET_USE_DEBUG)
      vDebugFormat(Net_C_Debug_DPlay,"emission failure: send queue is full");
    #endif
    return NetLib_E_es_BufferIsFull;
  } else if(hr<0) {
    #if defined(NET_USE_DEBUG)
      vDebugFormat(Net_C_Debug_DPlay,"emission failure: %lx", hr);
    #endif
    return NetLib_E_es_UnknownError;
  }
 
  /* free the message buffer */
  vFree(pData);
 
  return NetLib_E_es_NoError;
}
 
/*****************************************************************************
 * Description: eL0PCWin95DPlayQueryChannelStatus
 * returns the current status of the channel
 *****************************************************************************
 * Input:   uwChannel, channel to query
 * Output:  an error condition.
 *****************************************************************************/
NetLib_tdeErrorStatus eL0PCWin95DPlayQueryChannelStatus(tduwNetChannel uwChannel)
{
  return NetLib_E_es_NoError;
}
 
/*****************************************************************************
 * Description: eL0PCWin95DPlayEngine
 *****************************************************************************
 * Input:   none
 * Output:  none
 *****************************************************************************/
void eL0PCWin95DPlayEngine(void)
{
 
        DPID IdFrom, IdTo;
        unsigned long ulBytesToRead;
        FIFO *pstFIFOCell;
        HRESULT hr;
 
        if(!(gs_bL0PCWin95DPlayInitState&0x02))
                return;
 
        if(!glpDP)
                return;
 
        do {
                hr=IDirectPlay3_Receive(glpDP, 
                                                                &IdFrom, &IdTo,
                                                                DPRECEIVE_ALL | DPRECEIVE_PEEK,
                                                                NULL,
                                                                &ulBytesToRead);
                if(hr==DPERR_BUFFERTOOSMALL) {
                        pstFIFOCell=(FIFO *)pMalloc(ulBytesToRead+sizeof(FIFO));
 
                        if(pstFIFOCell==(FIFO *)C_pNull)
                                return;
 
                        hr=IDirectPlay3_Receive(glpDP, 
                                                                &IdFrom, &IdTo,
                                                                DPRECEIVE_ALL,
                                                                pstFIFOCell+1,
                                                                &ulBytesToRead);
 
                        pstFIFOCell->size=ulBytesToRead;
                        pstFIFOCell->DPlayIdFrom=IdFrom;
                        pstFIFOCell->next=(FIFO *)C_pNull;
 
                        NetLib_DPlayHandleNewMsg(pstFIFOCell, IdTo);
                } else if(hr != DPERR_NOMESSAGES) {
#ifdef NET_USE_DEBUG
                        vDebugFormat(Net_C_Debug_DPlay,"reception failure: %lx", hr);
#endif /* NET_USE_DEBUG */
                }
 
        } while(hr==DP_OK); /* introduire une limite sur le nombre de messages lus par appel */
 
}
 
/*****************************************************************************/
 
void NetLib_DPlayHandleNewMsg(FIFO *pstFIFOCell, DPID IdTo)
 
{
        DPlayHead *pstDPlayMsg;
 
#if defined(NET_USE_DEBUG)
                vDebugFormat(Net_C_Debug_DPlay,"HandleNewMsg()");
#endif
 
        /* first, handle Direct Play system messages */
 
        if(pstFIFOCell->DPlayIdFrom==DPID_SYSMSG) {
                switch(((LPDPMSG_GENERIC)(pstFIFOCell+1))->dwType) {
                        case DPSYS_CREATEPLAYERORGROUP:
                                NetLib_DPlayHandleNewPlayerMsg(pstFIFOCell, IdTo);
                                break;
                        case DPSYS_DESTROYPLAYERORGROUP:
                                NetLib_DPlayHandlePlayerLeftMsg(pstFIFOCell, IdTo);
                                break;
                        default:
#if defined(NET_USE_DEBUG)
                                vDebugFormat(Net_C_Debug_DPlay,"HandleNewMsg() : unhandled system message %lx", ((LPDPMSG_GENERIC)(pstDPlayMsg+1))->dwType);
#endif
 
                                ;
                }
                vFree(pstFIFOCell);
 
        } else {
                
                pstDPlayMsg=(DPlayHead *)(pstFIFOCell+1);
                
                if(pstDPlayMsg->uxDPlaySpecific != NetLib_ePureNetLib) {
                        /* this is a message specific to the Direct Play subsystem: interpret it */
                        NetLib_DPlayHandleSpecificMsg(pstFIFOCell, IdTo);
                        vFree(pstFIFOCell);
                } else {
                        /* this is a normal NetLib message: */
                        /* translate the player Id and enqueue the message */
#if defined(NET_USE_DEBUG)
                        vDebugFormat(Net_C_Debug_DPlay,"Received: Type %lu, Size %lu, Source %lx", 
                                pstDPlayMsg->eMessageType,
                                pstFIFOCell->size-sizeof(DPlayHead),
                                pstFIFOCell->DPlayIdFrom);
#endif
                        pstFIFOCell->uxSenderId=NetLib_DPlayToNetLib(pstFIFOCell->DPlayIdFrom);
                        *FIFOend=pstFIFOCell;
 
                        FIFOend=&(pstFIFOCell->next);
                }
 
        }
}
 
/*****************************************************************************/
 
void NetLib_DPlayHandleNewPlayerMsg(FIFO *pstFIFOCell, DPID IdTo)
 
{
        HRESULT hr;
        LPDPMSG_CREATEPLAYERORGROUP     pSysMsg;
        NetLib_tduxPlayerId uxNetLibPlayerId;
        tdstConversionMsg *ConversionInfo;
        DPlayHead *pstSpecificMsg;
        unsigned short uxSize;
 
#if defined(NET_USE_DEBUG)
                vDebugFormat(Net_C_Debug_DPlay,"HandleNewPlayerMsg()");
#endif
 
        pSysMsg = (LPDPMSG_CREATEPLAYERORGROUP) (pstFIFOCell+1);
 
        /* if we host the session, then we have to generate a NetLib identifier for this player */
        /* send this identifier to all other players */
        /* and then send the list of all identifiers to this new player */
        /* if we don't host the session, we remember that a new play has come */
        /* and wait for a message from the host to know its NetLib identifier */
 
        NetLib_RegisterNewPlayer1(pSysMsg->dpId, pSysMsg->dpnName.lpszShortNameA);
 
        if(gs_InfoLocal.joinType==NetLib_Join_Create) {
                /* we host the session: generate a new identifier for this player */
                uxNetLibPlayerId=GeneratePlayerId();
 
                NetLib_RegisterNewPlayer2(pSysMsg->dpId, uxNetLibPlayerId);
 
                /* send the new NetLib id to everyone */
 
                pstSpecificMsg=(DPlayHead *)pMalloc(sizeof(DPlayHead)+sizeof(tdstConversionMsg));
 
                if(pstSpecificMsg) {
                        pstSpecificMsg->uxDPlaySpecific=NetLib_eNewPlayerId;
 
                        ConversionInfo=(tdstConversionMsg *)(pstSpecificMsg+1);
 
                        ConversionInfo->DPlayPlayerId=pSysMsg->dpId;
                        ConversionInfo->uxNetLibPlayerId=uxNetLibPlayerId;
 
 
                        hr=IDirectPlay3_Send(   glpDP, 
                                gs_InfoLocal.DPlayPlayerId,
                                DPID_ALLPLAYERS,
                                DPSEND_GUARANTEED,
                                (LPVOID)pstSpecificMsg,
                                sizeof(DPlayHead)+sizeof(tdstConversionMsg));
                
                        if(hr != DP_OK) {
#ifdef NET_USE_DEBUG
                                vDebugFormat(Net_C_Debug_DPlay, "HandleNewPlayerMsg : DP3Send : error %lx", hr);
#endif
                        }
                }
 
                /* send all NetLib ids to the new player */
 
                pstSpecificMsg=NetLib_BuildConversionMsg(pSysMsg->dpId, &uxSize);
 
                if(pstSpecificMsg) {
                        hr=IDirectPlay3_Send(   glpDP, 
                                gs_InfoLocal.DPlayPlayerId,
                                pSysMsg->dpId,
                                DPSEND_GUARANTEED,
                                (LPVOID)pstSpecificMsg,
                                uxSize);
 
                        if(hr != DP_OK) {
#ifdef NET_USE_DEBUG
                                vDebugFormat(Net_C_Debug_DPlay, "HandleNewPlayerMsg : DP3Send : error %lx", hr);
#endif
                        }
 
                }
        }
}
 
/*****************************************************************************/
 
void NetLib_DPlayHandlePlayerLeftMsg(FIFO *pstFIFOCell, DPID IdTo)
 
{
        LPDPMSG_DELETEPLAYERFROMGROUP pSysMsg;
 
        pSysMsg=(LPDPMSG_DELETEPLAYERFROMGROUP)(pstFIFOCell+1);
 
#if defined(NET_USE_DEBUG)
        vDebugFormat(Net_C_Debug_DPlay,"HandlePlayerLeftMsg(): Player %lx", pSysMsg->dpIdPlayer);
#endif
 
        NetLib_SuppressConversionEntry(pSysMsg->dpIdPlayer);
 
        NbrPlayer--;
 
        /* in normal conditions (i.e. the remote player made a call to DisconnectFromSession() */
        /* the higher levels already have suppressed this player upon reception of the         */
        /* disconnection message */
}
 
/*****************************************************************************/
 
void NetLib_DPlayHandleSpecificMsg(FIFO *pstFIFOCell, DPID IdTo)
 
{
        DPlayHead *pstMsg;
        tdstConversionMsg *pstConversionMsg;
        unsigned int i, uwNbrOfLines;
 
#if defined(NET_USE_DEBUG)
                vDebugFormat(Net_C_Debug_DPlay,"HandleSpecificMsg()");
#endif
 
        pstMsg=(DPlayHead *)(pstFIFOCell+1);
 
        switch(pstMsg->uxDPlaySpecific) {
 
        case NetLib_eNewPlayerId:
                pstConversionMsg=(tdstConversionMsg *)(pstMsg+1);
                NetLib_RegisterNewPlayer2(pstConversionMsg->DPlayPlayerId, 
                                                                  pstConversionMsg->uxNetLibPlayerId);
                break;
                /* the message contains the NetLib ID of a new player */
                
        case NetLib_eIdTable:
                pstConversionMsg=(tdstConversionMsg *)(pstMsg+1);
                uwNbrOfLines=(pstFIFOCell->size - sizeof(DPlayHead)) / sizeof(tdstConversionMsg);
                for(i=0; i<uwNbrOfLines; i++)
                        NetLib_RegisterNewPlayer2(pstConversionMsg[i].DPlayPlayerId, 
                                                                          pstConversionMsg[i].uxNetLibPlayerId);
 
                break;
                /* the message contains a table of the NetLib IDs for all players except the local player */
        }
}
 
/*****************************************************************************
 * Description : vL0PCWin95DPlayCloseProtocol(void)
 * DPlay-Level 0 closing function
 *****************************************************************************
 * Input : none
 * Output : none
 *****************************************************************************/
void vL0PCWin95DPlayCloseProtocol(void)
{
  FIFO *p;
  while (FIFObegin)
  {
    p=FIFObegin->next;
    vFree((char*)FIFObegin);
    FIFObegin=p;
  }
  FIFObegin=NULL;
  FIFOend=&FIFObegin;
 
  vLevel1RemoveProtocol(E_Net_pr_Windows95DPlayProtocol);
 
}
 
/*****************************************************************************
 * Description: vL0PCWin95DPlayAddNewPlayer
 *****************************************************************************
 * Input:   pChanel : adress of returned chanel.
 *          par     : extern parameter.
 * Output:  error code.
 *****************************************************************************/
NetLib_tdeErrorStatus vL0PCWin95DPlayAddNewPlayer(tduwNetChannel *pChannel,void *par)
{
  *pChannel=0;
  return NetLib_E_es_NoError;
}
 
/*****************************************************************************
 * Description: vL0PCWin95DPlayOpenProtocol
 * 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_ vL0PCWin95DPlayOpenProtocol(void)
{
  tdstNetProtocolInterface *p_stProtocolInterface;
 
#if defined(NET_USE_DEBUG)
vDebugClear(Net_C_Debug_DPlay);
vDebugS(Net_C_Debug_DPlay,"SetupInterface");
#endif
 
  p_stProtocolInterface=pstLevel1AddProtocol();
 
  FIFObegin=NULL;
  FIFOend=&FIFObegin;
 
  /* logical identification of the protocol */
  p_stProtocolInterface->eProtocol = E_Net_pr_Windows95DPlayProtocol;
 
    /* setup the function pointers */
  p_stProtocolInterface->fn_uwStartChannelScan = uwL0PCWin95DPlayStartChannelScan;
  p_stProtocolInterface->fn_uwStartBroadcastChannelScan= uwL0PCWin95DPlayStartBroadcastChannelScan;
  p_stProtocolInterface->fn_uwNextChannel= uwL0PCWin95DPlayNextChannel;
  p_stProtocolInterface->fn_uwNextBroadcastChannel= uwL0PCWin95DPlayNextBroadcastChannel;
  p_stProtocolInterface->fn_eReadData= eL0PCWin95DPlayReadData;
  p_stProtocolInterface->fn_eSendData= eL0PCWin95DPlaySendData;
  p_stProtocolInterface->fn_eQueryChannelStatus= eL0PCWin95DPlayQueryChannelStatus;
  p_stProtocolInterface->fn_vLevel0NetEngine = eL0PCWin95DPlayEngine;
  p_stProtocolInterface->fn_vCloseChannel = vL0PCWin95DPlayCloseChannel;
  p_stProtocolInterface->fn_vLevel0CloseProtocol = vL0PCWin95DPlayCloseProtocol;
  p_stProtocolInterface->fn_eAddNewPlayer = vL0PCWin95DPlayAddNewPlayer;
 
  gs_bL0PCWin95DPlayInitState = 0x01;
 
  gs_uxPlayerIdGen=0;
  NetLib_InitConversionTable(8);
}
 
/*****************************************************************************
 * Description: eL0PCWin95DPlayStart
 * Start a game with DPlay
 *****************************************************************************
 * Input:   ppGameOptions : Adresse of a pointer wich receive the adresse of
 *          game options.
 * Output:  join type.
 *****************************************************************************/
_NET_EXPORT_ NetLib_tduwJoinType _NET_CALLING_CONV_ eL0PCWin95DPlayStart(char cMode)
{
        int Status;
 
#if defined(NET_USE_DEBUG)
vDebugS(Net_C_Debug_DPlay,"Start");
#endif
 
  SendMaxRate=1000L;
  NbrPlayer=0;
  SendBufSize=0;
  TabPlayer=NULL;
  LastTime=GetTickCount();
  FIFObegin=NULL;
  FIFOend=&FIFObegin;
 
  /* obtain a directplay lobby interface*/
 
  if(DPLobbyCreate() != DP_OK)
          return -1;
 
#ifdef NET_USE_DEBUG
  vDebugFormat(Net_C_Debug_DPlay, "DPLobbyCreate: OK");
#endif
 
  /* retrieve the session information */
 
  if(DPLobbyGetConnectionSettings() != DP_OK)
          return -1;
 
#ifdef NET_USE_DEBUG
  vDebugFormat(Net_C_Debug_DPlay, "DPLobbyGetConnectionSettings: OK");
#endif
 
#ifdef NET_USE_DEBUG
  vDebugFormat(Net_C_Debug_DPlay, "session name : %s", glpdplConnection->lpSessionDesc->lpszSessionNameA);
  vDebugFormat(Net_C_Debug_DPlay, "max # players : %ld", glpdplConnection->lpSessionDesc->dwMaxPlayers);
  vDebugFormat(Net_C_Debug_DPlay, "current # players : %ld", glpdplConnection->lpSessionDesc->dwCurrentPlayers);
#endif
 
  /* connect to the session */
 
  if(DPLobbyConnect() != DP_OK)
          return -1;
 
#ifdef NET_USE_DEBUG
  vDebugFormat(Net_C_Debug_DPlay, "DPLobbyConnect: OK");
#endif
 
  /* create the local player */
 
  if(DPCreatePlayer() != DP_OK)
                  return -1;
 
#ifdef NET_USE_DEBUG
  vDebugFormat(Net_C_Debug_DPlay, "DPCreatePlayer: OK");
#endif
 
  /* wait for other players ? or not ? */
 
  if(DPEnumPlayers() != DP_OK)
          return -1;
 
#ifdef NET_USE_DEBUG
  vDebugFormat(Net_C_Debug_DPlay, "DPEnumPlayers: OK");
#endif
 
  if(cMode==NetLib_DPlayClosed)
          /* wait for all players to arrive*/
 
          do {
                  eL0PCWin95DPlayEngine();
                  Status=CheckPlayerNbr();
          } while(Status==NetLib_DPlayWaiting);
 
  gs_bL0PCWin95DPlayInitState|=0x02;
  
#if defined(NET_USE_DEBUG)
vDebugS(Net_C_Debug_DPlay,"Start: returning");
#endif
 
  return gs_JoinType;
}
 
/*****************************************************************************
 * Description: eL0PCWin95DPlayFinish
 * Finish a game with DPlay.
 *****************************************************************************
 * Input:   none
 * Output:  none
 *****************************************************************************/
_NET_EXPORT_ void _NET_CALLING_CONV_ eL0PCWin95DPlayFinish(void)
{
  FIFO *p;
#if defined(NET_USE_DEBUG)
vDebugS(Net_C_Debug_DPlay,"Finish");
#endif
  vFree((char *)TabPlayer);
  while (FIFObegin)
  {
    p=FIFObegin->next;
    vFree((char*)FIFObegin);
    FIFObegin=p;
  }
  FIFObegin=NULL;
  FIFOend=&FIFObegin;
  gs_bL0PCWin95DPlayInitState&=~0x02;
  NetLib_eFlushSendingList(500);
  NetLib_vDisconnectAllRemotePlayers();
 
  DPClose();
  DPRelease();
  DPLobbyRelease();
 
}
 
/*****************************************************************************/
 
NetLib_tdeErrorStatus eL0PCWin95DPlayGetPlayerName(NetLib_tduxPlayerId uxPlayerId, char *pcString, unsigned short uwSize)
 
{
        unsigned short i;
 
        for(i=0; i<gs_stConvTable.uwSize; i++)
                if(gs_stConvTable.dastConv[i].uxNetLibPlayerId == uxPlayerId) {
                        strncpy(pcString, gs_stConvTable.dastConv[i].pcDPlayPlayerName, uwSize-1);
                        pcString[uwSize]='\0';
                        return NetLib_E_es_NoError;
                }
 
        return NetLib_E_es_UnknownPlayerId;
}
 
/*****************************************************************************
 * Description : eL0PCWin95DPlayIsProtocolSet
 * Check if the protocol is correctly initialize or not
 *****************************************************************************
 * Input : none
 * Output : NetLib_E_es_True
 *****************************************************************************/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eL0PCWin95DPlayIsProtocolSet(void)
{
  if(!(gs_bL0PCWin95DPlayInitState&0x01)) return NetLib_E_es_ProtocolNotInitialized;
  return NetLib_E_es_True;
}
 
/*****************************************************************************
 * Description : eL0PCWin95DPlayIsProtocolAvailable
 * 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_ eL0PCWin95DPlayIsProtocolAvailable(void)
{
  if(!(gs_bL0PCWin95DPlayInitState&0x01)) return NetLib_E_es_ProtocolNotInitialized;
  return NetLib_E_es_True;
}
