//
// Copyright 2020 Electronic Arts Inc.
//
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 
// software: you can redistribute it and/or modify it under the terms of 
// the GNU General Public License as published by the Free Software Foundation, 
// either version 3 of the License, or (at your option) any later version.

// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 
// in the hope that it will be useful, but with permitted additional restrictions 
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 
// distributed with this program. You should have received a copy of the 
// GNU General Public License along with permitted additional restrictions 
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection

/***********************************************************************************************
 ***              C O N F I D E N T I A L  ---  W E S T W O O D  S T U D I O S               ***
 ***********************************************************************************************
 *                                                                                             *
 *                 Project Name : Command & Conquer                                            *
 *                                                                                             *
 *                     $Archive:: /Sun/WSProto.cpp                                            $*
 *                                                                                             *
 *                      $Author:: Joe_b                                                       $*
 *                                                                                             *
 *                     $Modtime:: 8/20/97 10:54a                                              $*
 *                                                                                             *
 *                    $Revision:: 5                                                           $*
 *                                                                                             *
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 *                                                                                             *
 *  WSProto.CPP WinsockInterfaceClass to provide an interface to Winsock protocols             *
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 *                                                                                             *
 * Functions:                                                                                  *
 *                                                                                             *
 * WIC::WinsockInterfaceClass -- constructor for the WinsockInterfaceClass                     *
 * WIC::~WinsockInterfaceClass -- destructor for the WinsockInterfaceClass                     *
 * WIC::Close -- Releases any currently in use Winsock resources.                              *
 * WIC::Close_Socket -- Close the communication socket if its open                             *
 * WIC::Start_Listening -- Enable callbacks for read/write events on our socket                *
 * WIC::Stop_Listening -- Disable the winsock event callback                                   *
 * WIC::Discard_In_Buffers -- Discard any packets in our incoming packet holding buffers       *
 * WIC::Discard_In_Buffers -- Discard any packets in our outgoing packet holding buffers       *
 * WIC::Init -- Initialised Winsock and this class for use.                                    *
 * WIC::Read -- read any pending input from the communications socket                          *
 * WIC::WriteTo -- Send data via the Winsock socket                                            *
 * WIC::Broadcast -- Send data via the Winsock socket                                          *
 * WIC::Clear_Socket_Error -- Clear any outstanding erros on the socket                        *
 * WIC::Set_Socket_Options -- Sets default socket options for Winsock buffer sizes             *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include	"function.h"
#include	"WSProto.h"

#include	<stdio.h>


/***********************************************************************************************
 * WIC::WinsockInterfaceClass -- constructor for the WinsockInterfaceClass                     *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Nothing                                                                           *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *    3/20/96 2:51PM ST : Created                                                              *
 *=============================================================================================*/
WinsockInterfaceClass::WinsockInterfaceClass(void)
{
	WinsockInitialised = false;
	ASync = INVALID_HANDLE_VALUE;
	Socket = INVALID_SOCKET;
}


/***********************************************************************************************
 * WIC::~WinsockInterfaceClass -- destructor for the WinsockInterfaceClass                     *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Nothing                                                                           *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *    3/20/96 2:52PM ST : Created                                                              *
 *=============================================================================================*/
WinsockInterfaceClass::~WinsockInterfaceClass(void)
{
	Close();
}


/***********************************************************************************************
 * WIC::Close -- Releases any currently in use Winsock resources.                              *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Nothing                                                                           *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *    3/20/96 2:52PM ST : Created                                                              *
 *=============================================================================================*/
void WinsockInterfaceClass::Close(void)
{
	/*
	** If we never initialised the class in the first place then just return
	*/
	if (!WinsockInitialised) return;

	/*
	** Cancel any outstaning asyncronous events
	*/
	Stop_Listening();

	/*
	** Close any open sockets
	*/
	Close_Socket();

	/*
	** Call the Winsock cleanup function to say we are finished using Winsock
	*/
	WSACleanup();

	WinsockInitialised = false;
}



/***********************************************************************************************
 * WIC::Close_Socket -- Close the communication socket if its open                             *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Nothing                                                                           *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *    8/5/97 11:53AM ST : Created                                                              *
 *=============================================================================================*/
void WinsockInterfaceClass::Close_Socket (void)
{
	if ( Socket != INVALID_SOCKET ) {
		closesocket (Socket);
		Socket = INVALID_SOCKET;
	}
}



/***********************************************************************************************
 * WIC::Start_Listening -- Enable callbacks for read/write events on our socket                *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Nothing                                                                           *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *    8/5/97 11:54AM ST : Created                                                              *
 *=============================================================================================*/
bool WinsockInterfaceClass::Start_Listening (void)
{
	/*
	** Enable asynchronous events on the socket
	*/
	if ( WSAAsyncSelect ( Socket, MainWindow, Protocol_Event_Message(), FD_READ | FD_WRITE) == SOCKET_ERROR ){
		WWDebugString ( "TS: Async select failed.\n" );
		assert (false);
		WSACancelAsyncRequest(ASync);
		ASync = INVALID_HANDLE_VALUE;
		return (false);
	}
	return (true);
}


/***********************************************************************************************
 * WIC::Stop_Listening -- Disable the winsock event callback                                   *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Nothing                                                                           *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *    8/5/97 12:06PM ST : Created                                                              *
 *=============================================================================================*/
void WinsockInterfaceClass::Stop_Listening (void)
{
	if ( ASync != INVALID_HANDLE_VALUE ) {
		WSACancelAsyncRequest ( ASync );
		ASync = INVALID_HANDLE_VALUE;
	}
}




/***********************************************************************************************
 * WIC::Discard_In_Buffers -- Discard any packets in our incoming packet holding buffers       *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Nothing                                                                           *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *    8/5/97 11:55AM ST : Created                                                              *
 *=============================================================================================*/
void WinsockInterfaceClass::Discard_In_Buffers (void)
{
	WinsockBufferType *packet;

	while ( InBuffers.Count() ) {
		packet = InBuffers [ 0 ];
		delete packet;
		InBuffers.Delete (0);
	}
}


/***********************************************************************************************
 * WIC::Discard_In_Buffers -- Discard any packets in our outgoing packet holding buffers       *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Nothing                                                                           *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *    8/5/97 11:55AM ST : Created                                                              *
 *=============================================================================================*/
void WinsockInterfaceClass::Discard_Out_Buffers (void)
{
	WinsockBufferType *packet;

	while ( OutBuffers.Count() ) {
		packet = OutBuffers [ 0 ];
		delete packet;
		OutBuffers.Delete (0);
	}
}


/***********************************************************************************************
 * WIC::Init -- Initialised Winsock and this class for use.                                    *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Nothing                                                                           *
 *                                                                                             *
 * OUTPUT:   true if Winsock is available and was initialised                                  *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *    3/20/96 2:54PM ST : Created                                                              *
 *=============================================================================================*/
bool WinsockInterfaceClass::Init(void)
{
	short version;
	int 	rc;

	/*
	** Just return true if we are already set up
	*/
	if (WinsockInitialised) return (true);

	/*
	** Create a buffer much larger than the sizeof (WSADATA) would indicate since Bounds Checker
	** says that a buffer of that size gets overrun.
	*/
	char	*buffer = new char [sizeof (WSADATA) + 1024];
	WSADATA *winsock_info = (WSADATA*) (&buffer[0]);

	/*
	** Initialise socket and event handle to null
	*/
	Socket =INVALID_SOCKET;
	ASync = INVALID_HANDLE_VALUE;
	Discard_In_Buffers();
	Discard_Out_Buffers();

	/*
	** Start WinSock, and fill in our Winsock info structure
	*/
	version = (WINSOCK_MINOR_VER << 8) | WINSOCK_MAJOR_VER;
	rc = WSAStartup(version, winsock_info);
	if (rc != 0) {
		char out[128];
		sprintf (out, "TS: Winsock failed to initialise - error code %d.\n", GetLastError() );
		OutputDebugString (out);
		delete [] buffer;
		return (false);
	}

	/*
	** Check the Winsock version number
	*/
	if ((winsock_info->wVersion & 0x00ff) != (version & 0x00ff) ||
		(winsock_info->wVersion >> 8) != (version >> 8)) {
		OutputDebugString ("TS: Winsock version is less than 1.1\n" );
		delete [] buffer;
		return (false);
	}

	/*
	** Everything is OK so return success
	*/
	WinsockInitialised = true;

	delete [] buffer;
	return (true);

}




/***********************************************************************************************
 * WIC::Read -- read any pending input from the communications socket                          *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    ptr to buffer to receive input                                                    *
 *           length of buffer                                                                  *
 *           ptr to address to fill with address that packet was sent from                     *
 *           length of address buffer                                                          *
 *                                                                                             *
 * OUTPUT:   number of bytes transfered to buffer                                              *
 *                                                                                             *
 * WARNINGS: The format of the address is dependent on the protocol in use.                    *
 *                                                                                             *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *    3/20/96 2:58PM ST : Created                                                              *
 *=============================================================================================*/
int WinsockInterfaceClass::Read(void *buffer, int &buffer_len, void *address, int &address_len)
{
	address_len = address_len;
	/*
	** Call the message loop in case there are any outstanding winsock READ messages.
	*/
	Keyboard->Check();

	/*
	** If there are no available packets then return 0
	*/
	if ( InBuffers.Count() == 0 ) return (0);

	/*
	** Get the oldest packet for reading
	*/
	int packetnum = 0;
	WinsockBufferType *packet = InBuffers [packetnum];

	assert ( buffer_len >= packet->BufferLen );
	assert ( address_len >= sizeof (packet->Address) );

	/*
	** Copy the data and the address it came from into the supplied buffers.
	*/
	memcpy ( buffer, packet->Buffer, packet->BufferLen );
	memcpy ( address, packet->Address, sizeof (packet->Address) );

	/*
	** Return the length of the packet in buffer_len.
	*/
	buffer_len = packet->BufferLen;

	/*
	** Delete the temporary storage for the packet now that it is being passed to the game.
	*/
	InBuffers.Delete ( packetnum );
	delete packet;

	return ( buffer_len );
}




/***********************************************************************************************
 * WIC::WriteTo -- Send data via the Winsock socket                                            *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    ptr to buffer containing data to send                                             *
 *           length of data to send                                                            *
 *           address to send data to.                                                          *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: The format of the address is dependent on the protocol in use.                    *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *    3/20/96 3:00PM ST : Created                                                              *
 *=============================================================================================*/
void WinsockInterfaceClass::WriteTo(void *buffer, int buffer_len, void *address)
{
	/*
	** Create a temporary holding area for the packet.
	*/
	WinsockBufferType *packet = new WinsockBufferType;

	/*
	** Copy the packet into the holding buffer.
	*/
	memcpy ( packet->Buffer, buffer, buffer_len );
	packet->BufferLen = buffer_len;
	packet->IsBroadcast = false;
//	memcpy ( packet->Address, address, sizeof (packet->Address) );
	memcpy ( packet->Address, address, sizeof( IPXAddressClass ) );		// Steve Tall has revised WriteTo due to this bug.

	/*
	** Add it to our out list.
	*/
	OutBuffers.Add ( packet );

	/*
	** Send a message to ourselves so that we can initiate a write if Winsock is idle.
	*/
	SendMessage ( MainWindow, Protocol_Event_Message(), 0, (LONG)FD_WRITE );

	/*
	** Make sure the message loop gets called.
	*/
	Keyboard->Check();
}




/***********************************************************************************************
 * WIC::Broadcast -- Send data via the Winsock socket                                          *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    ptr to buffer containing data to send                                             *
 *           length of data to send                                                            *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *    3/20/96 3:00PM ST : Created                                                              *
 *=============================================================================================*/
void WinsockInterfaceClass::Broadcast (void *buffer, int buffer_len)
{

	/*
	** Create a temporary holding area for the packet.
	*/
	WinsockBufferType *packet = new WinsockBufferType;

	/*
	** Copy the packet into the holding buffer.
	*/
	memcpy ( packet->Buffer, buffer, buffer_len );
	packet->BufferLen = buffer_len;

	/*
	** Indicate that this packet should be broadcast.
	*/
	packet->IsBroadcast = true;

	/*
	** Add it to our out list.
	*/
	OutBuffers.Add ( packet );

	/*
	** Send a message to ourselves so that we can initiate a write if Winsock is idle.
	*/
	SendMessage ( MainWindow, Protocol_Event_Message(), 0, (LONG)FD_WRITE );

	/*
	** Make sure the message loop gets called.
	*/
	Keyboard->Check();
}




/***********************************************************************************************
 * WIC::Clear_Socket_Error -- Clear any outstanding erros on the socket                        *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Socket                                                                            *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *    8/5/97 12:05PM ST : Created                                                              *
 *=============================================================================================*/
void WinsockInterfaceClass::Clear_Socket_Error(SOCKET socket)
{
	unsigned long error_code;
	int length = 4;

	getsockopt (socket, SOL_SOCKET, SO_ERROR, (char*)&error_code, &length);
	error_code = 0;
	setsockopt (socket, SOL_SOCKET, SO_ERROR, (char*)&error_code, length);
}







/***********************************************************************************************
 * WIC::Set_Socket_Options -- Sets default socket options for Winsock buffer sizes             *
 *                                                                                             *
 *                                                                                             *
 *                                                                                             *
 * INPUT:    Nothing                                                                           *
 *                                                                                             *
 * OUTPUT:   Nothing                                                                           *
 *                                                                                             *
 * WARNINGS: None                                                                              *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *    8/5/97 12:07PM ST : Created                                                              *
 *=============================================================================================*/
bool WinsockInterfaceClass::Set_Socket_Options ( void )
{
	static int		socket_transmit_buffer_size = SOCKET_BUFFER_SIZE;
	static int		socket_receive_buffer_size = SOCKET_BUFFER_SIZE;

	/*
	** Specify the size of the receive buffer.
	*/
	int err = setsockopt ( Socket, SOL_SOCKET, SO_RCVBUF, (char*)&socket_receive_buffer_size, 4);
	if ( err == INVALID_SOCKET ) {
		char out[128];
		sprintf (out, "TS: Failed to set IPX socket option SO_RCVBUF - error code %d.\n", GetLastError() );
		OutputDebugString (out);
		assert ( err != INVALID_SOCKET );
	}

	/*
	** Specify the size of the send buffer.
	*/
	err = setsockopt ( Socket, SOL_SOCKET, SO_SNDBUF, (char*)&socket_transmit_buffer_size, 4);
	if ( err == INVALID_SOCKET ) {
		char out[128];
		sprintf (out, "TS: Failed to set IPX socket option SO_SNDBUF - error code %d.\n", GetLastError() );
		OutputDebugString (out);
		assert ( err != INVALID_SOCKET );
	}

	return ( true );
}