//
// 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 : Dynamic Data Encapsulation               *
 *                                                                         *
 *                    File Name : DDE.CPP		                              *
 *                                                                         *
 *                   Programmer : Steve Wetherill                          *
 *                                                                         *
 *                   Start Date : June 1, 1996                             *
 *                                                                         *
 *                  Last Update : June 8, 1996 [SW]                        *
 *                                                                         *
 *-------------------------------------------------------------------------*
 * Functions:                                                              *
 *   Instance_Class::InstanceClass -- class constructor                    *
 *   Instance_Class::InstanceClass -- class destructor                     *
 *   Instance_Class::Enable_Callback -- enables local processing of pokes  *
 *   Instance_Class::Register_Servers -- registers a local DDE DNS service *
 *   Instance_Class::Cleanup_App -- currently does nothing                 *
 *   Instance_Class::Test_Server_Running -- does a trial connect to remote *
 *	  Instance_Class::Open_Poke_Connection -- pokes some data to server		*
 *   Instance_Class::Close_Poke_Connectionp -- closes connection to remote *
 *   Instance_Class::Poke_Server -- sends a chunk of data to remote        *
 *   Instance_Class::dde_callback -- processes DDE transactions            *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


#include	<windows.h>
#include	"dde.h"

/***************************************************************************
 * These are static members of Instance_Class
 *=========================================================================*/

DWORD	Instance_Class::id_inst;			// instance identifier set by DdeInitialize
BOOL		Instance_Class::process_pokes;	// controls response to pokes
char 	Instance_Class::ascii_name[32];	// name of server

//PG_TO_FIX  ST - 12/20/2018 2:17PM
#if (0)
static BOOL CALLBACK	(*Instance_Class::callback) (
			LPBYTE pointer,		// pointer to received data
			long	length			// length of received data or advisory flag
			) = NULL;
#endif

/***************************************************************************
 * Instance_Class::InstanceClass -- class constructor              			*
 *                                                                         *
 * INPUT:                                                                  *
 *		name1		null terminated ASCII client name									*
 *		name1		null terminated ASCII server name									*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		dde_error = TRUE if error occurs when initializing DDE					*
 *                                                                         *
 * WARNINGS:                                                               *
 *		none.																						*
 *                                                                         *
 * HISTORY:                                                                *
 *   6/1/1996 SW : Created.                                                *
 *=========================================================================*/

Instance_Class::Instance_Class( LPSTR name1, LPSTR name2 )
{
	name1;
	name2;
	return;
			
#if (0)	//ST - 1/2/2019 5:35PM
	dde_error = FALSE;		// no errors
	process_pokes = FALSE;	// disable pokes in callback

	id_inst = 0;				// set to 0 for first time through
	conv_handle = 0;   		// conversation handle reset

	lstrcpy( ascii_name, name1 );	// keep a record of ASCII name

	if	( DdeInitialize(
		(LPDWORD) &id_inst,			// instance identifier
		dde_callback,
		APPCLASS_STANDARD | 			// filter server messages
		CBF_FAIL_SELFCONNECTIONS,	// prevent from connecting with self
		0) != DMLERR_NO_ERROR) {   // reserved
			dde_error = TRUE;			// flag an error
	}

	local_name = DdeCreateStringHandle(
		id_inst,         		// instance identifier
    	name1,     				// string to register
    	CP_WINANSI);    		// Windows ANSI code page

	remote_name = DdeCreateStringHandle(
		id_inst,         		// instance identifier
    	name2,     				// string to register
    	CP_WINANSI);    		// Windows ANSI code page

	poke_topic = DdeCreateStringHandle(
		id_inst,         		// instance identifier
		"POKE TOPIC", 			// System topic
		CP_WINANSI);    		// Windows ANSI code page

	poke_item = DdeCreateStringHandle(
		id_inst,         		// instance identifier
		"POKE ITEM", 			// System topic
		CP_WINANSI);    		// Windows ANSI code page

	system_topic = DdeCreateStringHandle(
		id_inst,         		// instance identifier
		SZDDESYS_TOPIC, 		// System topic
		CP_WINANSI);    		// Windows ANSI code page
#endif
}

/***************************************************************************
 * Instance_Class::~Instance_Class -- class destructor	              		*
 *                                                                         *
 * INPUT:                                                                  *
 *    none.                                                                *
 *                                                                         *
 * OUTPUT:                                                                 *
 *		none.																						*
 *                                                                         *
 * WARNINGS:                                                               *
 *		none.																						*
 *                                                                         *
 * HISTORY:                                                                *
 *   6/1/1996 SW : Created.                                                *
 *=========================================================================*/

Instance_Class::~Instance_Class()
{
	DdeUninitialize( id_inst );
}

/***************************************************************************
 * Instance_Class::Enable_Callback -- enables user callback						*
 *                                                                         *
 * INPUT:                                                                  *
 *    TRUE = enable poke processing														*
 *    FALSE = disable poke processing													*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		echos the input																		*
 *                                                                         *
 * WARNINGS:                                                               *
 *		user callback must be explicitly enabled. Disbabled by default.		*
 *                                                                         *
 * HISTORY:                                                                *
 *   6/1/1996 SW : Created.                                                *
 *=========================================================================*/

BOOL	Instance_Class::Enable_Callback( BOOL flag )		// enable or disable callback
{
	return (process_pokes = flag);

}

/***************************************************************************
 * Instance_Class::Register_Server -- registers a local DDE DNS service    *
 *                                                                         *
 * INPUT:                                                                  *
 *    BOOL CALLBACK ( *callback_fnc) ( LPBYTE, DWORD) = user poke callbacl *
 *                                                                         *
 * OUTPUT:                                                                 *
 *		TRUE == success																		*
 *		FALSE == failed																		*
 *                                                                         *
 * WARNINGS:                                                               *
 *		none.																						*
 *                                                                         *
 * HISTORY:                                                                *
 *   6/1/1996 SW : Created.                                                *
 *=========================================================================*/
#if (0)
BOOL	Instance_Class::Register_Server( BOOL (CALLBACK *)(LPBYTE, long));

//BOOL	Instance_Class::Register_Server( BOOL CALLBACK ( *callback_fnc) (LPBYTE, long) )
{

	if (DdeNameService( id_inst, local_name,	0L, DNS_REGISTER ) != 0L) {
		callback = callback_fnc;
		return ( TRUE );
	} else {
		return ( FALSE );
	}
}
#endif
/***************************************************************************
 * Instance_Class::Test_Server_Running -- does a trial connect to remote   *
 *                                                                         *
 * INPUT:                                                                  *
 *    name = HSZ string handle of server name.										*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		TRUE == successfully connected to remote										*
 *		FALSE == failed to connect															*
 *                                                                         *
 * WARNINGS:                                                               *
 *		- Can be called for local or remote server but of course will 			*
 *      fail if a called for local and local server is not "up".				*
 *		- Disconects before exiting.														*
 *                                                                         *
 * HISTORY:                                                                *
 *   6/1/1996 SW : Created.                                                *
 *=========================================================================*/

BOOL	Instance_Class::Test_Server_Running( HSZ name )
{

	if( Open_Poke_Connection( name ) == TRUE) {
		Close_Poke_Connection();
		return( TRUE );
	} else {
		return( FALSE );
	}
}

/***************************************************************************
 *	Instance_Class::Open_Poke_Connection -- open a connection to server		*
 *                                                                         *
 * INPUT:                                                                  *
 *    name = HSZ server name.																*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		TRUE == successfully opened connection											*
 *		FALSE == failed to connect															*
 *                                                                         *
 * WARNINGS:                                                               *
 *		Can be called for local or remote server but of course will 			*
 *    fail if a called for local and local server is not "up".					*
 *                                                                         *
 * HISTORY:                                                                *
 *   6/1/1996 SW : Created.                                                *
 *=========================================================================*/

BOOL	Instance_Class::Open_Poke_Connection( HSZ name )
{
	conv_handle = DdeConnect(
		id_inst,           	// instance identifier
		name,         			// service name string handle
		poke_topic,        	// topic string handle
		(PCONVCONTEXT) NULL);// use default context

	if (conv_handle == NULL) {
		return FALSE;
	} else {
		return TRUE;
	}
}

/***************************************************************************
 *	Instance_Class::Close_Poke_Connection -- closes poke connection			*
 *                                                                         *
 * INPUT:                                                                  *
 *    none.																						*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		TRUE == successfully closed connection											*
 *		FALSE == failed to close connection for some reason						*
 *                                                                         *
 * WARNINGS:                                                               *
 *		none.																						*
 *                                                                         *
 * HISTORY:                                                                *
 *   6/1/1996 SW : Created.                                                *
 *=========================================================================*/

BOOL	Instance_Class::Close_Poke_Connection( void )
{
	if( conv_handle ) {
		HCONV	temp_handle = conv_handle;
		conv_handle = NULL;
		return( DdeDisconnect( temp_handle ));
	} else {
		return( TRUE );
	}
}

/***************************************************************************
 *	Instance_Class::Poke_Server -- pokes some data to server						*
 *                                                                         *
 * INPUT:                                                                  *
 *    poke_data	points to data to send to remote									*
 *    poke_length	length of buffer to send											*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		TRUE == successfully poked the data												*
 *		FALSE == failed to connect															*
 *                                                                         *
 * WARNINGS:                                                               *
 *		has a 3 second timeout (change POKE_TIMEOUT, in milliseconds)			*
 *                                                                         *
 * HISTORY:                                                                *
 *   6/1/1996 SW : Created.                                                *
 *=========================================================================*/

#define	POKE_TIMEOUT	60*1000	      // 60 sec timeout

BOOL	Instance_Class::Poke_Server( LPBYTE poke_data, DWORD poke_length )
{

	if( DdeClientTransaction(

		poke_data,					// address of data to pass to server
	  	poke_length,				// length of data
		conv_handle,				// handle of conversation
		poke_topic,					// handle of item name string
		CF_TEXT,						// no special clipboard data format
		XTYP_POKE,					// transaction type
		POKE_TIMEOUT,							// time-out duration (millisecs)
		(LPDWORD) NULL		 		// address of transaction result (don't check)
	   ) == 0) {

		return( FALSE);
	} else {
		return( TRUE );
	}
}

/***************************************************************************
 *	Instance_Class::dde_callback -- callback dde event handler					*
 *                                                                         *
 * INPUT:                                                                  *
 *		dde_event	transaction type														*
 *		uFmt			clipboard data format												*
 *		hconv			handle of the conversation											*
 *		hsz1			handle of a string													*
 *		hsz2			handle of a string													*
 *		hdata			handle of a global memory object									*
 *		dwData1		transaction-specific data											*
 *		dwData2 		transaction-specific data											*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		context specific HDDEDATA object													*
 *                                                                         *
 * WARNINGS:                                                               *
 *	  	NOTE: declared as HDDEDATA CALLBACK which means PASCAL parameters		*
 *                                                                         *
 * HISTORY:                                                                *
 *   6/1/1996 SW : Created.                                                *
 *=========================================================================*/

HDDEDATA CALLBACK Instance_Class::dde_callback(

	UINT  dde_event,	// transaction type
	UINT  uFmt,			// clipboard data format
	HCONV  ,				// handle of the conversation
	HSZ  hsz1,			// handle of a string
	HSZ  hsz2,			// handle of a string
	HDDEDATA  hdata,	// handle of a global memory object
	DWORD  			,	// transaction-specific data
	DWORD  			 	// transaction-specific data
	)
{

	dde_event;
	uFmt;
	hsz1;
	hsz2;
	hdata;
		return (HDDEDATA) NULL;

	
#if (0)	//ST - 1/2/2019 5:38PM
	if (!Instance_Class::callback){
		return (HDDEDATA) NULL;
	}

	switch ( dde_event ) {

		case XTYP_REGISTER:
      case XTYP_UNREGISTER:

			return (HDDEDATA) NULL;

		case XTYP_ADVDATA:
			return (HDDEDATA) DDE_FACK;

		case XTYP_XACT_COMPLETE:

			return (HDDEDATA) NULL;

		case XTYP_DISCONNECT:

			Instance_Class::callback( NULL, DDE_ADVISE_DISCONNECT);
			return (HDDEDATA) NULL;

		case	XTYP_CONNECT: {

			char	buffer[32];

			DdeQueryString (Instance_Class::id_inst, hsz2, buffer, sizeof (buffer), 0) ;

			if (0 != strcmp (buffer, Instance_Class::ascii_name)) {
				return (HDDEDATA) NULL;
			}

			DdeQueryString (Instance_Class::id_inst, hsz1, buffer, sizeof (buffer), 0) ;

			if (0 != strcmp (buffer, "POKE TOPIC")) {
				return (HDDEDATA) NULL;
			}

			Instance_Class::callback( NULL, DDE_ADVISE_CONNECT);
			return (HDDEDATA) TRUE;
		}

		case	XTYP_POKE:

			if	(Instance_Class::process_pokes == FALSE ) {
				return (HDDEDATA) DDE_FNOTPROCESSED;	// processing disabled
			} else {

				char	buffer[32];

				DdeQueryString (Instance_Class::id_inst, hsz1, buffer, sizeof (buffer), 0) ;

				if (0 != strcmp (buffer, "POKE TOPIC")) {
					return (HDDEDATA) DDE_FNOTPROCESSED;
				} else if (uFmt == CF_TEXT) {					// make sure it's CF_TEXT

					BOOL processed;
					BYTE FAR *pdata;
					DWORD dw_length;

					if ( (pdata = DdeAccessData( hdata, &dw_length)) == NULL ) {
						return (HDDEDATA) DDE_FNOTPROCESSED;
					}

					processed = Instance_Class::callback((LPBYTE) pdata, dw_length);

					DdeUnaccessData( hdata );

					if (processed == TRUE) {
						return (HDDEDATA) DDE_FACK;
					} else {
						return (HDDEDATA) NULL;
					}

				}
			}

		default:
      	return (HDDEDATA) NULL;
   }
#endif
}