#include "pch.hpp"
//
//============================================================
//
// Contents:  Network routines
//
// Owner   :  AndyG
// Created :  Aug 9 1999
//
// All Rights reserved worldwide (c) Microsoft
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               
//============================================================
//
#include "Net_Header.hpp"

extern HGOSHEAP DefaultHeap;


#include "mreplay.h" // jcem mission replay



//
// Used to decode IP address when lobby launched
//
char* _stdcall DecodeIPAddress( DPLCONNECTION* pConnection )
{
	DPCOMPOUNDADDRESSELEMENT* ptr=(DPCOMPOUNDADDRESSELEMENT*)pConnection->lpAddress;
	DPCOMPOUNDADDRESSELEMENT* EndPtr=(DPCOMPOUNDADDRESSELEMENT*)((BYTE*)ptr+pConnection->dwAddressSize);
	while( ptr<EndPtr )
	{
		if( ptr->guidDataType==DPAID_INet )
		{
			return (char*)&ptr->lpData;
		}
		ptr=(DPCOMPOUNDADDRESSELEMENT*)((BYTE*)ptr+ptr->dwDataSize+sizeof(DPCOMPOUNDADDRESSELEMENT)-4);
	}
	return 0;
}

//
// Used to decode port number when lobby launched
//
WORD _stdcall DecodePORTAddress( DPLCONNECTION* pConnection )
{
	DPCOMPOUNDADDRESSELEMENT* ptr=(DPCOMPOUNDADDRESSELEMENT*)pConnection->lpAddress;
	DPCOMPOUNDADDRESSELEMENT* EndPtr=(DPCOMPOUNDADDRESSELEMENT*)((BYTE*)ptr+pConnection->dwAddressSize);
	while( ptr<EndPtr )
	{
		if( ptr->guidDataType==DPAID_INetPort )
		{
			return *(WORD*)&ptr->lpData;
		}
		ptr=(DPCOMPOUNDADDRESSELEMENT*)((BYTE*)ptr+ptr->dwDataSize+sizeof(DPCOMPOUNDADDRESSELEMENT)-4);
	}
	return 0;
}




//
// TCPIP enumeration callback
//
BOOL FAR PASCAL TCPIPCallback( REFGUID guidDataType, DWORD dwDataSize, LPCVOID lpData, void* lpContext )
{
	if( guidDataType==DPAID_INet )			// Internet address or name
	{
		if( MyIPAddress )
		{
			gos_Free( MyIPAddress );
		}
		MyIPAddress=(char*)gos_Malloc(dwDataSize,DefaultHeap);
		memcpy( MyIPAddress, lpData, dwDataSize );
	}
	return TRUE;
}



//
// These are the invliad modem names found in Win9x and Win2000
// NOTE: Valid modems starting with these strings will be ignored.
//
char* IgnoreList[]={ "RAS VPN Line", "WAN Miniport", "LPT", "IPCONF LINE", "H323 Line", "Microsoft VPN" };

//
// Modem enumeration callback
//
BOOL FAR PASCAL ModemCallback( REFGUID guidDataType, DWORD dwDataSize, LPCVOID lpData, void* lpContext )
{
	DWORD NumberModems=0;

	if( guidDataType==DPAID_Modem )
	{
		DWORD counter = 0;
		char *pData=(char*)lpData;

		while( counter<dwDataSize )
		{
//
// Check for known invalid modems (TAPI returns some bad devices)
//
			bool BadName=0;
			for( int t0=0; t0<sizeof(IgnoreList)/sizeof(IgnoreList[0]); t0++ )
			{
				if( 0==strnicmp( IgnoreList[t0], pData, strlen(IgnoreList[t0]) ) )
					BadName=1;
			}

			if( !BadName )
			{
				char Buffer[128];
				sprintf( Buffer, "%s",pData );

				ListOfNames* pTemp=(ListOfNames*)gos_Malloc( sizeof(ListOfNames)+strlen(Buffer), Heap_Network );
				pTemp->pNext=0;
				
				strcpy( pTemp->Name, Buffer );
				if( ModemNames )
				{
					ListOfNames* pLast=ModemNames;
					while( pLast->pNext )									// Add to end of list
						pLast=pLast->pNext;
					pLast->pNext=pTemp;
				}
				else
				{
					ModemNames=pTemp;
				}
				hasModem=1;

				NumberModems++;
				if( NumberModems>7 )										// Allow a maximum of 8 modems
					return FALSE;
			}

			counter += strlen(pData)+1;
			pData += strlen(pData)+1;
			if( *pData==0 )
				return FALSE;
		}
		return FALSE;
	}
	return TRUE;
}



//
// Used to enumerate details about a session before it is joined
//
BOOL PASCAL EnumJoinSessionCallback( LPCDPSESSIONDESC2 lpSessionDesc, LPDWORD lpdwTimeOut, DWORD dwFlags, void* lpContext )
{
	if( (dwFlags&DPESC_TIMEDOUT) || 0==lpSessionDesc || 0==lpSessionDesc->lpszSessionNameA )
		return false;
//
// Store the session information
//
	DPSESSIONDESC2** pSession=(DPSESSIONDESC2**)lpContext;
	*pSession=(DPSESSIONDESC2*)malloc( lpSessionDesc->dwSize+strlen(lpSessionDesc->lpszSessionNameA)+1 );
	memcpy( *pSession, lpSessionDesc, lpSessionDesc->dwSize );
	strcpy((char*)(*pSession+1), lpSessionDesc->lpszSessionNameA );
	(*pSession)->lpszSessionNameA=(char*)(*pSession+1);
	return false;
}





//
// Called while games are being enumerated.
//
BOOL PASCAL EnumSessionsCallback( LPCDPSESSIONDESC2 lpSessionDesc, LPDWORD lpdwTimeOut, DWORD dwFlags, void* lpContext )
{
	if( (dwFlags&DPESC_TIMEDOUT) || 0==lpSessionDesc || 0==lpSessionDesc->lpszSessionNameA )
		return false;
//
// Create a new GameName entry
//
	ListOfGames* pThis=(ListOfGames*)malloc( sizeof(ListOfGames)+strlen(lpSessionDesc->lpszSessionNameA) );
	memcpy( &pThis->SessionDescriptor, lpSessionDesc, sizeof(pThis->SessionDescriptor) );
	strcpy( pThis->Name, lpSessionDesc->lpszSessionNameA );
	pThis->pPlayers=0;
	pThis->pNext=0;
//
// Add it to the end of the current list
//
	ListOfGames* pCurrent=EnumNames;
	if( pCurrent )
	{
		while( pCurrent->pNext )
		{
			pCurrent=pCurrent->pNext;
		}
		pCurrent->pNext=pThis;
	}
	else
	{
		EnumNames=pThis;
	}
	return true;
}






//
// Used to remove players from a game
//
void _stdcall gos_RealRemovePlayerFromGame( ListOfNames** pListOfPlayers, char* Name, DPID dpId )
{
	ListOfNames* pLast=0;
	ListOfNames* pTemp=*pListOfPlayers;
	while( pTemp )
	{
		if( 0==stricmp( Name, pTemp->Name ) && pTemp->Data==dpId )
		{
			if( pLast )
			{
				pLast->pNext=pTemp->pNext;
				free( pTemp );
			}
			else
			{
				free( pTemp );
				*pListOfPlayers=0;			// Removed last player on list
			}
			return;
		}
		pLast=pTemp;
		pTemp=pTemp->pNext;
	}
	SPEW(( GROUP_NETWORK,"Tried to remove player %s, does not exist",Name ));
}

void _stdcall RemovePlayerFromGame( ListOfNames** pListOfPlayers, char* Name, DPID dpId )
{
    gos_RealRemovePlayerFromGame( pListOfPlayers, Name, dpId );
    // jcem mission replay
    if (g_pfn_REMOVEPLAYERFROMGAME) {
        (*g_pfn_REMOVEPLAYERFROMGAME)((void**)pListOfPlayers, Name, dpId);
    }
    // jcem mission replay
}


//
// Used to add players to a game
//
void _stdcall gos_RealAddPlayerToGame( ListOfNames** pListOfPlayers, char* Name, DPID dpId )
{
	ListOfNames* pCurrent=(ListOfNames*)malloc( sizeof(ListOfNames)+strlen(Name) );

	pCurrent->Data=dpId;
	pCurrent->LastTime=0;
	pCurrent->PacketsReceived=0;
	strcpy( pCurrent->Name, Name );
	pCurrent->pNext=0;

	if( *pListOfPlayers )
	{
		ListOfNames* pTemp=*pListOfPlayers;
		while( pTemp->pNext )
		{
			pTemp=pTemp->pNext;
		}
		pTemp->pNext=pCurrent;
	}
	else
	{
		*pListOfPlayers=pCurrent;
	}
}

void _stdcall AddPlayerToGame( ListOfNames** pListOfPlayers, char* Name, DPID dpId )
{
    // jcem mission replay
    if (g_pfn_ADDPLAYERTOGAME) {
        (*g_pfn_ADDPLAYERTOGAME)((void**)pListOfPlayers, Name, dpId);
    }
    // jcem mission replay
    gos_RealAddPlayerToGame( pListOfPlayers, Name, dpId );
}

//
// Called to enumerate all the players in a game.
//
BOOL PASCAL EnumPlayersCallback( DPID dpId, DWORD dwPlayerType, LPCDPNAME lpName, DWORD dwFlags, LPVOID lpContext )
{
	if( dwPlayerType==DPPLAYERTYPE_PLAYER && lpName && lpName->lpszShortNameA )
	{
		AddPlayerToGame( (ListOfNames**)lpContext, lpName->lpszShortNameA, dpId );
	}

	return true;
}

//
// When a game is created or joined, find current players
//
void _stdcall GetCurrentPlayers()
{
	wEnumPlayers( dplay4, NULL, EnumPlayersCallback, &CurrentPlayers, DPENUMPLAYERS_ALL );
}


//
// Return up to 10 bytes of the players name
//
char* _stdcall GetName10( DWORD Id )
{
	static char Buffer[64];

	if( Id==(DWORD)gos_NetInformation(gos_ServerID) )
	{
		return "Server    ";
	}

	if( Id==(DWORD)gos_NetInformation(gos_AllID) )
	{
		return "Everyone  ";
	}

	if( Id==(DWORD)gos_NetInformation(gos_MyID) )
	{
		return "Yourself  ";
	}
	strcpy( Buffer, (char*)gos_NetInformation( gos_PlayerName, (DWORD)gos_NetInformation( gos_PlayerNumber, Id )) );
	strcat( Buffer, "           " );
	Buffer[10]=0;
	return Buffer;
}



//
// Waits until all DirectPlay messages are flushed, before DPlay object can be closed
//
void _stdcall WaitTillQueueEmpty()
{
	DWORD Messages=1;
	while( Messages )		// Wait until all messages are sent
	{
		AllowFail=1;
		HRESULT hr=wGetMessageQueue( dplay4, 0, 0, DPMESSAGEQUEUE_SEND, &Messages, 0 );
		AllowFail=0;
		if( FAILED(hr) )
			Messages=0;
	}
	Sleep(200);				// Pause for 1 second!  (DPLAY recommend)
}


