595 lines
30 KiB
C++
595 lines
30 KiB
C++
/*
|
|
** Command & Conquer Red Alert(tm)
|
|
** Copyright 2025 Electronic Arts Inc.
|
|
**
|
|
** This program 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.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/***********************************************************************************************
|
|
*** 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 );
|
|
}
|
|
|
|
|