420 lines
21 KiB
C++
420 lines
21 KiB
C++
|
//
|
||
|
// 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 - Red Alert *
|
||
|
* *
|
||
|
* File Name : CCDDE.CPP *
|
||
|
* *
|
||
|
* Programmer : Steve Tall *
|
||
|
* *
|
||
|
* Start Date : 10/04/95 *
|
||
|
* *
|
||
|
* Last Update : August 5th, 1996 [ST] *
|
||
|
* *
|
||
|
*---------------------------------------------------------------------------------------------*
|
||
|
* Overview: *
|
||
|
* C&C's interface to the DDE class *
|
||
|
* *
|
||
|
*---------------------------------------------------------------------------------------------*
|
||
|
* *
|
||
|
* Functions: *
|
||
|
* DDE_Callback -- DDE server callback function *
|
||
|
* DDEServerClass::DDEServerClass -- class constructor *
|
||
|
* DDEServerClass::Enable -- Enables the DDE callback *
|
||
|
* DDEServerClass::Disable -- Disables the DDE callback *
|
||
|
* DDEServerClass::~DDEServerClass -- class destructor *
|
||
|
* DDESC::Callback -- callback function. Called from the DDE_Callback wrapper function *
|
||
|
* DDESC::Get_MPlayer_Game_Info -- returns a pointer to the multiplayer setup info from wchat *
|
||
|
* DDESC::Delete_MPlayer_Game_Info -- clears out multi player game setup info *
|
||
|
* DDESC::Time_Since_Heartbeat -- returns the time in ticks since the last heartbeat from wchat*
|
||
|
* *
|
||
|
* Send_Data_To_DDE_Server -- sends a packet to WChat *
|
||
|
* *
|
||
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||
|
|
||
|
|
||
|
#include <WINDOWS.H>
|
||
|
#include "ccdde.h"
|
||
|
#include <stdio.h>
|
||
|
#include <timer.h>
|
||
|
|
||
|
DDEServerClass DDEServer; //Instance of the DDE Server class
|
||
|
|
||
|
Instance_Class *DDE_Class = NULL; // pointer for client callback
|
||
|
// this *must* be called DDE_Class
|
||
|
|
||
|
BOOL CC95AlreadyRunning = FALSE; //Was there an instance of C&C 95 already running when we started?
|
||
|
|
||
|
extern HWND MainWindow;
|
||
|
extern TimerClass GameTimer;
|
||
|
extern bool GameTimerInUse;
|
||
|
extern void CCDebugString (char *string);
|
||
|
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
* DDE_Callback -- DDE server callback function *
|
||
|
* *
|
||
|
* Just acts as a wrapper for the DDEServerClass callback function *
|
||
|
* *
|
||
|
* INPUT: ptr to data from client *
|
||
|
* length of data *
|
||
|
* *
|
||
|
* OUTPUT: Nothing *
|
||
|
* *
|
||
|
* WARNINGS: None *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 6/8/96 3:19PM ST : Created *
|
||
|
*=============================================================================================*/
|
||
|
BOOL CALLBACK DDE_Callback (unsigned char *data, long length)
|
||
|
{
|
||
|
return (DDEServer.Callback(data, length));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
* DDEServerClass::DDEServerClass -- class constructor *
|
||
|
* *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: Nothing *
|
||
|
* *
|
||
|
* OUTPUT: Nothing *
|
||
|
* *
|
||
|
* WARNINGS: None *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 6/8/96 3:20PM ST : Created *
|
||
|
*=============================================================================================*/
|
||
|
DDEServerClass::DDEServerClass(void)
|
||
|
{
|
||
|
MPlayerGameInfo = NULL; //Flag that we havnt received a start game info packet yet
|
||
|
|
||
|
DDE_Class = new Instance_Class ("CONQUER", "WCHAT");
|
||
|
|
||
|
DDE_Class->Enable_Callback( TRUE );
|
||
|
IsEnabled = TRUE;
|
||
|
|
||
|
if (DDE_Class->Test_Server_Running(DDE_Class->local_name)){
|
||
|
CC95AlreadyRunning = TRUE;
|
||
|
}else{
|
||
|
|
||
|
//ST - 12/20/2018 2:27PM
|
||
|
//DDE_Class->Register_Server( DDE_Callback );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void DDEServerClass::Enable(void)
|
||
|
{
|
||
|
if (!IsEnabled){
|
||
|
DDE_Class->Enable_Callback( TRUE );
|
||
|
IsEnabled = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
* DDEServerClass::Disable -- Disables the DDE callback *
|
||
|
* *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: Nothing *
|
||
|
* *
|
||
|
* OUTPUT: Nothing *
|
||
|
* *
|
||
|
* WARNINGS: None *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 8/5/96 9:44PM ST : Created *
|
||
|
*=============================================================================================*/
|
||
|
void DDEServerClass::Disable(void)
|
||
|
{
|
||
|
if (IsEnabled){
|
||
|
DDE_Class->Enable_Callback( FALSE );
|
||
|
IsEnabled = FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
* DDEServerClass::~DDEServerClass -- class destructor *
|
||
|
* *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: Nothing *
|
||
|
* *
|
||
|
* OUTPUT: Nothing *
|
||
|
* *
|
||
|
* WARNINGS: None *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 6/8/96 3:20PM ST : Created *
|
||
|
*=============================================================================================*/
|
||
|
DDEServerClass::~DDEServerClass(void)
|
||
|
{
|
||
|
Delete_MPlayer_Game_Info();
|
||
|
delete( DDE_Class );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
* DDESC::Callback -- callback function. Called from the DDE_Callback wrapper function *
|
||
|
* *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: data from DDE client *
|
||
|
* length of data *
|
||
|
* *
|
||
|
* OUTPUT: Nothing *
|
||
|
* *
|
||
|
* WARNINGS: Data has length and type as first 2 ints *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 6/8/96 3:21PM ST : Created *
|
||
|
*=============================================================================================*/
|
||
|
BOOL DDEServerClass::Callback(unsigned char *data, long length)
|
||
|
{
|
||
|
|
||
|
/*
|
||
|
** If the packet length < 0 then this is a special advisory packet
|
||
|
*/
|
||
|
if ( length<0 ) {
|
||
|
|
||
|
switch( length ) {
|
||
|
|
||
|
case DDE_ADVISE_CONNECT:
|
||
|
CCDebugString("C&C95 - DDE advisory: client connect detected.");
|
||
|
return TRUE;
|
||
|
|
||
|
case DDE_ADVISE_DISCONNECT:
|
||
|
CCDebugString("C&C95 - DDE advisory: client disconnect detected.");
|
||
|
return TRUE;
|
||
|
|
||
|
default:
|
||
|
CCDebugString("C&C95 - DDE advisory: Unknown DDE advise type.");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
}else{
|
||
|
|
||
|
/*
|
||
|
** Packet must be at least the length of the packet type & size fields to be valid
|
||
|
*/
|
||
|
if (length < 2*sizeof(int)) {
|
||
|
CCDebugString ("C&C95 - Received invalid packet.");
|
||
|
return (FALSE);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Find out what kind of packet this is and its length.
|
||
|
*/
|
||
|
int *packet_pointer = (int *)data;
|
||
|
int actual_length = ntohl(*packet_pointer++);
|
||
|
int packet_type = ntohl(*packet_pointer++);
|
||
|
|
||
|
/*
|
||
|
** Strip the ID int from the start of the packet
|
||
|
*/
|
||
|
data += 2*sizeof (int);
|
||
|
length -= 2*sizeof (int);
|
||
|
actual_length -= 2*sizeof (int);
|
||
|
|
||
|
/*
|
||
|
** Take the appropriate action for the packet type
|
||
|
*/
|
||
|
switch ( packet_type ){
|
||
|
|
||
|
/*
|
||
|
** This is a packet with the info required for starting a new internet game. This is really
|
||
|
* just C&CSPAWN.INI sent from WChat instead of read from disk.
|
||
|
*/
|
||
|
case DDE_PACKET_START_MPLAYER_GAME:
|
||
|
CCDebugString("C&C95 - Received start game packet.");
|
||
|
Delete_MPlayer_Game_Info();
|
||
|
MPlayerGameInfo = new char [actual_length + 1];
|
||
|
memcpy (MPlayerGameInfo, data, actual_length);
|
||
|
*(MPlayerGameInfo + actual_length) = 0; //Terminator in case we treat it as a string
|
||
|
MPlayerGameInfoLength = actual_length;
|
||
|
LastHeartbeat = 0;
|
||
|
break;
|
||
|
|
||
|
case DDE_TICKLE:
|
||
|
CCDebugString("C&C95 - Received 'tickle' packet.");
|
||
|
//SetForegroundWindow ( MainWindow );
|
||
|
//ShowWindow ( MainWindow, SW_SHOWMAXIMIZED );
|
||
|
break;
|
||
|
|
||
|
case DDE_PACKET_HEART_BEAT:
|
||
|
CCDebugString("C&C95 - Received heart beat packet.");
|
||
|
if (GameTimerInUse){
|
||
|
LastHeartbeat = GameTimer.Time();
|
||
|
}else{
|
||
|
LastHeartbeat = 0;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
CCDebugString("C&C95 - Received unrecognised packet.");
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (TRUE);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
* DDESC::Get_MPlayer_Game_Info -- returns a pointer to the multiplayer setup info from wchat *
|
||
|
* *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: Nothing *
|
||
|
* *
|
||
|
* OUTPUT: ptr to data in .INI file format *
|
||
|
* *
|
||
|
* WARNINGS: None *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 6/8/96 3:23PM ST : Created *
|
||
|
*=============================================================================================*/
|
||
|
char *DDEServerClass::Get_MPlayer_Game_Info (void)
|
||
|
{
|
||
|
return (MPlayerGameInfo);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
* DDESC::Delete_MPlayer_Game_Info -- clears out multi player game setup info *
|
||
|
* *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: Nothing *
|
||
|
* *
|
||
|
* OUTPUT: Nothing *
|
||
|
* *
|
||
|
* WARNINGS: None *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 6/8/96 3:24PM ST : Created *
|
||
|
*=============================================================================================*/
|
||
|
void DDEServerClass::Delete_MPlayer_Game_Info(void)
|
||
|
{
|
||
|
if (MPlayerGameInfo){
|
||
|
delete [] MPlayerGameInfo;
|
||
|
MPlayerGameInfo = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
* DDESC::Time_Since_Heartbeat -- returns the time in ticks since the last heartbeat from wchat*
|
||
|
* *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: Nothing *
|
||
|
* *
|
||
|
* OUTPUT: time since heartbeat *
|
||
|
* *
|
||
|
* WARNINGS: None *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 6/9/96 11:05PM ST : Created *
|
||
|
*=============================================================================================*/
|
||
|
int DDEServerClass::Time_Since_Heartbeat(void)
|
||
|
{
|
||
|
return (GameTimer.Time() - LastHeartbeat);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
* Send_Data_To_DDE_Server -- sends a packet to WChat *
|
||
|
* *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: ptr to the data to send *
|
||
|
* length of data *
|
||
|
* packet type identifier *
|
||
|
* *
|
||
|
* OUTPUT: true if packet successfully sent *
|
||
|
* *
|
||
|
* WARNINGS: None *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 6/9/96 11:07PM ST : Created *
|
||
|
*=============================================================================================*/
|
||
|
BOOL Send_Data_To_DDE_Server (char *data, int length, int packet_type)
|
||
|
{
|
||
|
#if (0)
|
||
|
BOOL app_exists;
|
||
|
|
||
|
app_exists = DDE_Class->Test_Server_Running(DDE_Class->remote_name);
|
||
|
|
||
|
if (app_exists != TRUE) {
|
||
|
CCDebugString("Connection to server failed!");
|
||
|
return(FALSE);
|
||
|
}
|
||
|
#endif //(0)
|
||
|
|
||
|
if( DDE_Class->Open_Poke_Connection(DDE_Class->remote_name) == FALSE) {
|
||
|
CCDebugString("C&C95 - Failed to connect for POKE!");
|
||
|
return (FALSE);
|
||
|
}
|
||
|
|
||
|
char *poke_data = new char [length + 2*sizeof(int)];
|
||
|
|
||
|
int *poke_data_int = (int*)poke_data;
|
||
|
|
||
|
*poke_data_int = htonl (length + 2*sizeof(int));
|
||
|
*(poke_data_int+1)= htonl (packet_type);
|
||
|
|
||
|
memcpy (poke_data + 8, data, length);
|
||
|
|
||
|
|
||
|
if(DDE_Class->Poke_Server( (LPBYTE) poke_data, ntohl(*poke_data_int) ) == FALSE) {
|
||
|
CCDebugString("C&C95 - POKE failed!\n");
|
||
|
DDE_Class->Close_Poke_Connection(); // close down the link
|
||
|
delete poke_data;
|
||
|
return (FALSE);
|
||
|
}
|
||
|
|
||
|
DDE_Class->Close_Poke_Connection(); // close down the link
|
||
|
|
||
|
delete poke_data;
|
||
|
|
||
|
return (TRUE);
|
||
|
}
|