CCDDE.CPP (21712B)
1 // 2 // Copyright 2020 Electronic Arts Inc. 3 // 4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 5 // software: you can redistribute it and/or modify it under the terms of 6 // the GNU General Public License as published by the Free Software Foundation, 7 // either version 3 of the License, or (at your option) any later version. 8 9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 16 17 /*********************************************************************************************** 18 *** 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 *** 19 *********************************************************************************************** 20 * * 21 * Project Name : Command & Conquer - Red Alert * 22 * * 23 * File Name : CCDDE.CPP * 24 * * 25 * Programmer : Steve Tall * 26 * * 27 * Start Date : 10/04/95 * 28 * * 29 * Last Update : August 5th, 1996 [ST] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Overview: * 33 * C&C's interface to the DDE class * 34 * * 35 *---------------------------------------------------------------------------------------------* 36 * * 37 * Functions: * 38 * DDE_Callback -- DDE server callback function * 39 * DDEServerClass::DDEServerClass -- class constructor * 40 * DDEServerClass::Enable -- Enables the DDE callback * 41 * DDEServerClass::Disable -- Disables the DDE callback * 42 * DDEServerClass::~DDEServerClass -- class destructor * 43 * DDESC::Callback -- callback function. Called from the DDE_Callback wrapper function * 44 * DDESC::Get_MPlayer_Game_Info -- returns a pointer to the multiplayer setup info from wchat * 45 * DDESC::Delete_MPlayer_Game_Info -- clears out multi player game setup info * 46 * DDESC::Time_Since_Heartbeat -- returns the time in ticks since the last heartbeat from wchat* 47 * * 48 * Send_Data_To_DDE_Server -- sends a packet to WChat * 49 * * 50 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 51 52 53 #include <WINDOWS.H> 54 #include "ccdde.h" 55 #include <stdio.h> 56 #include <timer.h> 57 58 DDEServerClass DDEServer; //Instance of the DDE Server class 59 60 Instance_Class *DDE_Class = NULL; // pointer for client callback 61 // this *must* be called DDE_Class 62 63 BOOL CC95AlreadyRunning = FALSE; //Was there an instance of C&C 95 already running when we started? 64 65 extern HWND MainWindow; 66 extern TimerClass GameTimer; 67 extern bool GameTimerInUse; 68 extern void CCDebugString (char *string); 69 70 71 /*********************************************************************************************** 72 * DDE_Callback -- DDE server callback function * 73 * * 74 * Just acts as a wrapper for the DDEServerClass callback function * 75 * * 76 * INPUT: ptr to data from client * 77 * length of data * 78 * * 79 * OUTPUT: Nothing * 80 * * 81 * WARNINGS: None * 82 * * 83 * HISTORY: * 84 * 6/8/96 3:19PM ST : Created * 85 *=============================================================================================*/ 86 BOOL CALLBACK DDE_Callback (unsigned char *data, long length) 87 { 88 return (DDEServer.Callback(data, length)); 89 } 90 91 92 93 94 /*********************************************************************************************** 95 * DDEServerClass::DDEServerClass -- class constructor * 96 * * 97 * * 98 * * 99 * INPUT: Nothing * 100 * * 101 * OUTPUT: Nothing * 102 * * 103 * WARNINGS: None * 104 * * 105 * HISTORY: * 106 * 6/8/96 3:20PM ST : Created * 107 *=============================================================================================*/ 108 DDEServerClass::DDEServerClass(void) 109 { 110 MPlayerGameInfo = NULL; //Flag that we havnt received a start game info packet yet 111 112 DDE_Class = new Instance_Class ("CONQUER", "WCHAT"); 113 114 DDE_Class->Enable_Callback( TRUE ); 115 IsEnabled = TRUE; 116 117 if (DDE_Class->Test_Server_Running(DDE_Class->local_name)){ 118 CC95AlreadyRunning = TRUE; 119 }else{ 120 121 //ST - 12/20/2018 2:27PM 122 //DDE_Class->Register_Server( DDE_Callback ); 123 } 124 } 125 126 127 128 void DDEServerClass::Enable(void) 129 { 130 if (!IsEnabled){ 131 DDE_Class->Enable_Callback( TRUE ); 132 IsEnabled = TRUE; 133 } 134 } 135 136 137 138 /*********************************************************************************************** 139 * DDEServerClass::Disable -- Disables the DDE callback * 140 * * 141 * * 142 * * 143 * INPUT: Nothing * 144 * * 145 * OUTPUT: Nothing * 146 * * 147 * WARNINGS: None * 148 * * 149 * HISTORY: * 150 * 8/5/96 9:44PM ST : Created * 151 *=============================================================================================*/ 152 void DDEServerClass::Disable(void) 153 { 154 if (IsEnabled){ 155 DDE_Class->Enable_Callback( FALSE ); 156 IsEnabled = FALSE; 157 } 158 } 159 160 161 162 163 164 165 /*********************************************************************************************** 166 * DDEServerClass::~DDEServerClass -- class destructor * 167 * * 168 * * 169 * * 170 * INPUT: Nothing * 171 * * 172 * OUTPUT: Nothing * 173 * * 174 * WARNINGS: None * 175 * * 176 * HISTORY: * 177 * 6/8/96 3:20PM ST : Created * 178 *=============================================================================================*/ 179 DDEServerClass::~DDEServerClass(void) 180 { 181 Delete_MPlayer_Game_Info(); 182 delete( DDE_Class ); 183 } 184 185 186 187 /*********************************************************************************************** 188 * DDESC::Callback -- callback function. Called from the DDE_Callback wrapper function * 189 * * 190 * * 191 * * 192 * INPUT: data from DDE client * 193 * length of data * 194 * * 195 * OUTPUT: Nothing * 196 * * 197 * WARNINGS: Data has length and type as first 2 ints * 198 * * 199 * HISTORY: * 200 * 6/8/96 3:21PM ST : Created * 201 *=============================================================================================*/ 202 BOOL DDEServerClass::Callback(unsigned char *data, long length) 203 { 204 205 /* 206 ** If the packet length < 0 then this is a special advisory packet 207 */ 208 if ( length<0 ) { 209 210 switch( length ) { 211 212 case DDE_ADVISE_CONNECT: 213 CCDebugString("C&C95 - DDE advisory: client connect detected."); 214 return TRUE; 215 216 case DDE_ADVISE_DISCONNECT: 217 CCDebugString("C&C95 - DDE advisory: client disconnect detected."); 218 return TRUE; 219 220 default: 221 CCDebugString("C&C95 - DDE advisory: Unknown DDE advise type."); 222 return FALSE; 223 } 224 225 }else{ 226 227 /* 228 ** Packet must be at least the length of the packet type & size fields to be valid 229 */ 230 if (length < 2*sizeof(int)) { 231 CCDebugString ("C&C95 - Received invalid packet."); 232 return (FALSE); 233 } 234 235 /* 236 ** Find out what kind of packet this is and its length. 237 */ 238 int *packet_pointer = (int *)data; 239 int actual_length = ntohl(*packet_pointer++); 240 int packet_type = ntohl(*packet_pointer++); 241 242 /* 243 ** Strip the ID int from the start of the packet 244 */ 245 data += 2*sizeof (int); 246 length -= 2*sizeof (int); 247 actual_length -= 2*sizeof (int); 248 249 /* 250 ** Take the appropriate action for the packet type 251 */ 252 switch ( packet_type ){ 253 254 /* 255 ** This is a packet with the info required for starting a new internet game. This is really 256 * just C&CSPAWN.INI sent from WChat instead of read from disk. 257 */ 258 case DDE_PACKET_START_MPLAYER_GAME: 259 CCDebugString("C&C95 - Received start game packet."); 260 Delete_MPlayer_Game_Info(); 261 MPlayerGameInfo = new char [actual_length + 1]; 262 memcpy (MPlayerGameInfo, data, actual_length); 263 *(MPlayerGameInfo + actual_length) = 0; //Terminator in case we treat it as a string 264 MPlayerGameInfoLength = actual_length; 265 LastHeartbeat = 0; 266 break; 267 268 case DDE_TICKLE: 269 CCDebugString("C&C95 - Received 'tickle' packet."); 270 //SetForegroundWindow ( MainWindow ); 271 //ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); 272 break; 273 274 case DDE_PACKET_HEART_BEAT: 275 CCDebugString("C&C95 - Received heart beat packet."); 276 if (GameTimerInUse){ 277 LastHeartbeat = GameTimer.Time(); 278 }else{ 279 LastHeartbeat = 0; 280 } 281 break; 282 283 default: 284 CCDebugString("C&C95 - Received unrecognised packet."); 285 break; 286 287 } 288 } 289 290 return (TRUE); 291 292 } 293 294 295 296 /*********************************************************************************************** 297 * DDESC::Get_MPlayer_Game_Info -- returns a pointer to the multiplayer setup info from wchat * 298 * * 299 * * 300 * * 301 * INPUT: Nothing * 302 * * 303 * OUTPUT: ptr to data in .INI file format * 304 * * 305 * WARNINGS: None * 306 * * 307 * HISTORY: * 308 * 6/8/96 3:23PM ST : Created * 309 *=============================================================================================*/ 310 char *DDEServerClass::Get_MPlayer_Game_Info (void) 311 { 312 return (MPlayerGameInfo); 313 } 314 315 316 317 /*********************************************************************************************** 318 * DDESC::Delete_MPlayer_Game_Info -- clears out multi player game setup info * 319 * * 320 * * 321 * * 322 * INPUT: Nothing * 323 * * 324 * OUTPUT: Nothing * 325 * * 326 * WARNINGS: None * 327 * * 328 * HISTORY: * 329 * 6/8/96 3:24PM ST : Created * 330 *=============================================================================================*/ 331 void DDEServerClass::Delete_MPlayer_Game_Info(void) 332 { 333 if (MPlayerGameInfo){ 334 delete [] MPlayerGameInfo; 335 MPlayerGameInfo = NULL; 336 } 337 } 338 339 340 341 /*********************************************************************************************** 342 * DDESC::Time_Since_Heartbeat -- returns the time in ticks since the last heartbeat from wchat* 343 * * 344 * * 345 * * 346 * INPUT: Nothing * 347 * * 348 * OUTPUT: time since heartbeat * 349 * * 350 * WARNINGS: None * 351 * * 352 * HISTORY: * 353 * 6/9/96 11:05PM ST : Created * 354 *=============================================================================================*/ 355 int DDEServerClass::Time_Since_Heartbeat(void) 356 { 357 return (GameTimer.Time() - LastHeartbeat); 358 } 359 360 361 362 363 /*********************************************************************************************** 364 * Send_Data_To_DDE_Server -- sends a packet to WChat * 365 * * 366 * * 367 * * 368 * INPUT: ptr to the data to send * 369 * length of data * 370 * packet type identifier * 371 * * 372 * OUTPUT: true if packet successfully sent * 373 * * 374 * WARNINGS: None * 375 * * 376 * HISTORY: * 377 * 6/9/96 11:07PM ST : Created * 378 *=============================================================================================*/ 379 BOOL Send_Data_To_DDE_Server (char *data, int length, int packet_type) 380 { 381 #if (0) 382 BOOL app_exists; 383 384 app_exists = DDE_Class->Test_Server_Running(DDE_Class->remote_name); 385 386 if (app_exists != TRUE) { 387 CCDebugString("Connection to server failed!"); 388 return(FALSE); 389 } 390 #endif //(0) 391 392 if( DDE_Class->Open_Poke_Connection(DDE_Class->remote_name) == FALSE) { 393 CCDebugString("C&C95 - Failed to connect for POKE!"); 394 return (FALSE); 395 } 396 397 char *poke_data = new char [length + 2*sizeof(int)]; 398 399 int *poke_data_int = (int*)poke_data; 400 401 *poke_data_int = htonl (length + 2*sizeof(int)); 402 *(poke_data_int+1)= htonl (packet_type); 403 404 memcpy (poke_data + 8, data, length); 405 406 407 if(DDE_Class->Poke_Server( (LPBYTE) poke_data, ntohl(*poke_data_int) ) == FALSE) { 408 CCDebugString("C&C95 - POKE failed!\n"); 409 DDE_Class->Close_Poke_Connection(); // close down the link 410 delete poke_data; 411 return (FALSE); 412 } 413 414 DDE_Class->Close_Poke_Connection(); // close down the link 415 416 delete poke_data; 417 418 return (TRUE); 419 }