//
// 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

/* $Header: /CounterStrike/IPX.CPP 1     3/03/97 10:24a Joe_bostic $ */
/***************************************************************************
 **   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                        *
 *                                                                         *
 *                    File Name : IPX.CPP                                  *
 *                                                                         *
 *                   Programmer : Barry Nance										*
 * 										 from Client/Server LAN Programming			*
 *											 Westwood-ized by Bill Randolph				*
 *                                                                         *
 *                   Start Date : December 14, 1994                        *
 *                                                                         *
 *                  Last Update : December 15, 1994   [BR]                 *
 *                                                                         *
 *-------------------------------------------------------------------------*
 * Pitfalls:																					*
 * - Never try to use a closed socket; always check the return code from	*
 *   IPX_Open_Socket().																		*
 * - Always give IPX an outstanding ECB for listening, before you send.		*
 * - It turns out that IPX is pretty bad about saving registers, so if		*
 *   you have any register variables in your program, they may get			*
 *   trashed.  To circumvent this, all functions in this module save &		*
 *   restore the registers before invoking any IPX or NETX function.			*
 *                                                                         *
 *-------------------------------------------------------------------------*
 * Functions:                                                              *
 *   IPX_SPX_Installed -- checks for installation of IPX/SPX               *
 *   IPX_Open_Socket -- opens an IPX socket for sending or receiving       *
 *   IPX_Close_Socket -- closes an open socket                             *
 *   IPX_Get_Connection_Number -- gets local Connection Number					*
 *   IPX_Get_1st_Connection_Num -- gets 1st Connect Number for given user  *
 *   IPX_Get_Internet_Address -- gets Network Number & Node Address			*
 *   IPX_Get_User_ID -- gets user ID from Connection Number                *
 *   IPX_Listen_For_Packet -- commands IPX to listen for a packet          *
 *   IPX_Send_Packet -- commands IPX to send a packet                      *
 *   IPX_Get_Local_Target -- fills in ImmediateAddress field of ECB        *
 *   IPX_Cancel_Event -- cancels an operation in progress                  *
 *   Let_IPX_Breath -- gives IPX some CPU time                             *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include "function.h"
#include <stdio.h>
//#include <mem.h>
//#include <i86.h>
#include "ipx.h"

#ifdef WIN32
#include "ipx95.h"
#endif	//WIN32


// Turn off "expression is not meaningful".
//#pragma warning 628 9


/***************************************************************************
 * IPX_SPX_Installed -- checks for installation of IPX/SPX                 *
 *                                                                         *
 * INPUT:                                                                  *
 *		none.																						*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		0 = not installed; 1 = IPX only, 2 = IPX and SPX are installed			*
 *                                                                         *
 * WARNINGS:                                                               *
 *		none.																						*
 *                                                                         *
 * HISTORY:                                                                *
 *   12/14/1994 BR : Created.                                              *
 *=========================================================================*/
int IPX_SPX_Installed(void)
{
	return false;
#ifdef WIN32

#ifdef TIBERIAN_SUN
	return(false);
#else
#if (0)//PG
	if ( Load_IPX_Dll () ){
		return ( IPX_Initialise() );
	}else{
		return(false);
	}
#endif
#endif

#else	//WIN32

	union REGS regs;
	struct SREGS sregs;
	RMIType rmi;

	//------------------------------------------------------------------------
	//	Init all registers to 0's
	//------------------------------------------------------------------------
	memset (&regs, 0, sizeof(regs));
	segread (&sregs);
	memset (&rmi, 0, sizeof(rmi));

	//------------------------------------------------------------------------
	//	Fill in registers for the DPMI call, function 0x300
	//------------------------------------------------------------------------
	regs.w.ax = DPMI_CALL_REAL_INT;	// DPMI function to call
	regs.w.bx = 0x002f;					// interrupt # to invoke
	sregs.es = FP_SEG(&rmi);			// tell DPMI where the RMI is
	regs.x.edi = FP_OFF(&rmi);

	//------------------------------------------------------------------------
	//	Fill in registers for the real-mode interrupt handler.
	//	To test for the presence of IPX, set AH to 0x7a, AL to 0, and invoke
	//	interrupt 0x2f (the "multiplex" interrupt).  If IPX is installed,
	//	AL will be 0xff, and ES:DI will contain the IPX/SPX function address.
	//------------------------------------------------------------------------
	rmi.eax = 0x00007a00;

	//------------------------------------------------------------------------
	//	call DPMI
	//------------------------------------------------------------------------
	int386x(DPMI_INT, &regs, &regs, &sregs);

	//------------------------------------------------------------------------
	//	If IPX isn't there, return 0
	//------------------------------------------------------------------------
	if ( (rmi.eax & 0x00ff) != 0xff) {
		return(0);
	}

	//------------------------------------------------------------------------
	//	Test for SPX by invoking the IPX_SPX function with BX = 0x10, and AL = 0.
	//	If SPX is present, AL will be 0xff.
	//------------------------------------------------------------------------
	//........................................................................
	//	Fill in registers for the DPMI call
	//........................................................................
	memset (&regs, 0 ,sizeof(regs));
	segread (&sregs);
	memset (&rmi, 0 ,sizeof(rmi));
	regs.w.ax = DPMI_CALL_REAL_INT;	// DPMI function to call
	regs.w.bx = IPX_INT;					// interrupt # to invoke
	sregs.es = FP_SEG(&rmi);			// tell DPMI where the RMI is
	regs.x.edi = FP_OFF(&rmi);

	//........................................................................
	//	Fill in registers for the interrupt call
	//........................................................................
	rmi.ebx = 0x00000010;

	//........................................................................
	//	call DPMI
	//........................................................................
	int386x(DPMI_INT, &regs, &regs, &sregs);

	//------------------------------------------------------------------------
	//	SPX is installed; return '2'
	//------------------------------------------------------------------------
	if ( (rmi.eax & 0x00ff) == 0xff) {
		return(2);
	}

	//------------------------------------------------------------------------
	//	SPX is not installed; return '1'
	//------------------------------------------------------------------------
	return(1);
#endif	//WIN32

}	/* end of IPX_SPX_Installed */


/***************************************************************************
 * IPX_Open_Socket -- opens an IPX socket for sending or receiving         *
 *                                                                         *
 * INPUT:                                                                  *
 *		socket		the socket number to open											*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		0 = OK																					*
 *		-1 = IPX not installed																*
 *		0xfe = socket table is full														*
 *		0xff = socket is already open														*
 *                                                                         *
 * WARNINGS:                                                               *
 *		The application must define its socket number carefully.  Use			*
 *		values from 0x4000 to 0x8000 for custom socket numbers.  The app		*
 *		must know its own socket number as well as the socket number of		*
 *		a destination workstation.															*
 *                                                                         *
 * HISTORY:                                                                *
 *   12/15/1994 BR : Created.                                              *
 *=========================================================================*/

#ifndef WIN32		// WIN32 version is in IPX95.CPP

int IPX_Open_Socket(unsigned short socket)
{
	union REGS regs;
	struct SREGS sregs;
	RMIType rmi;
	int rc;

	//------------------------------------------------------------------------
	//	Open the socket:
	//	DX = socket number
	//	AL = 0 for short-lived socket, 0xff for long-lived socket
	//------------------------------------------------------------------------
	//........................................................................
	//	Fill in registers for the DPMI call
	//........................................................................
	memset (&regs, 0 ,sizeof(regs));
	segread (&sregs);
	memset (&rmi, 0 ,sizeof(rmi));
	regs.w.ax = DPMI_CALL_REAL_INT;	// DPMI function to call
	regs.w.bx = IPX_INT;					// interrupt # to invoke
	sregs.es = FP_SEG (&rmi);			// tell DPMI where the RMI is
	regs.x.edi = FP_OFF (&rmi);

	//........................................................................
	//	Fill in registers for the interrupt call
	//........................................................................
	rmi.ebx = IPX_OPEN_SOCKET;			// function code
	rmi.edx = socket;						// desired socket #
	rmi.eax = 0x00ff;						// make this a long-lived socket

	//........................................................................
	//	call DPMI
	//........................................................................
	int386x (DPMI_INT, &regs, &regs, &sregs);

	rc = (rmi.eax & 0xff);

	return(rc);

}	/* end of IPX_Open_Socket */
#endif	//WIN32

/***************************************************************************
 * IPX_Close_Socket -- closes an open socket                               *
 *                                                                         *
 * INPUT:                                                                  *
 *		socket		socket number to close												*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		0 = ok, -1 = error																	*
 *                                                                         *
 * WARNINGS:                                                               *
 *		none.																						*
 *                                                                         *
 * HISTORY:                                                                *
 *   12/15/1994 BR : Created.                                              *
 *=========================================================================*/
#ifndef WIN32		// WIN32 version is in IPX95.CPP

int IPX_Close_Socket(unsigned short socket)
{
	union REGS regs;
	struct SREGS sregs;
	RMIType rmi;

	//------------------------------------------------------------------------
	//	Close the socket:
	//	DX = socket number
	//------------------------------------------------------------------------
	//........................................................................
	//	Fill in registers for the DPMI call
	//........................................................................
	memset (&regs, 0 ,sizeof(regs));
	segread (&sregs);
	memset (&rmi, 0 ,sizeof(rmi));
	regs.w.ax = DPMI_CALL_REAL_INT;	// DPMI function to call
	regs.w.bx = IPX_INT;					// interrupt # to invoke
	sregs.es = FP_SEG(&rmi);			// tell DPMI where the RMI is
	regs.x.edi = FP_OFF(&rmi);

	//........................................................................
	//	Fill in registers for the interrupt call
	//........................................................................
	rmi.ebx = IPX_CLOSE_SOCKET;
	rmi.edx = socket;

	//........................................................................
	//	call DPMI
	//........................................................................
	int386x(DPMI_INT, &regs, &regs, &sregs);

	return(0);

}	/* end of IPX_Close_Socket */
#endif		//WIN32


/***************************************************************************
 * IPX_Get_Connection_Number -- gets local Connection Number					*
 *                                                                         *
 * This Novell call will the return the user's local "Connection Number".	*
 * This value will be 0 if the user isn't logged into Novell, so this		*
 * routine can be used to detect if other calls (such as Get_Local_Target)	*
 * will be OK.																					*
 *                                                                         *
 * INPUT:                                                                  *
 *		none.																						*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		Connection Number, 0 = none														*
 *                                                                         *
 * WARNINGS:                                                               *
 *		none.																						*
 *                                                                         *
 * HISTORY:                                                                *
 *   12/15/1994 BR : Created.                                              *
 *=========================================================================*/
#ifndef WIN32		// WIN32 version is in IPX95.CPP
int IPX_Get_Connection_Number(void)
{
	union REGS regs;
	struct SREGS sregs;
	RMIType rmi;
	int num;

	//------------------------------------------------------------------------
	//	Call Interrupt 0x21, with AH = 0xdc.  This tells Novell to put the local
	//	connection number into AL.
	//------------------------------------------------------------------------
	//........................................................................
	//	Fill in registers for the DPMI call
	//........................................................................
	memset (&regs, 0 ,sizeof(regs));
	segread (&sregs);
	memset (&rmi, 0 ,sizeof(rmi));
	regs.w.ax = DPMI_CALL_REAL_INT;	// DPMI function to call
	regs.w.bx = 0x21;						// interrupt # to invoke
	sregs.es = FP_SEG(&rmi);			// tell DPMI where the RMI is
	regs.x.edi = FP_OFF(&rmi);

	//........................................................................
	//	Fill in registers for the interrupt call
	//........................................................................
	rmi.eax = 0x0000dc00;

	//........................................................................
	//	call DPMI
	//........................................................................
	int386x(DPMI_INT, &regs, &regs, &sregs);

	num = rmi.eax & 0x00ff;

	return(num);

}	/* end of IPX_Get_Connection_Number */
#endif	//WIN32


/***************************************************************************
 * IPX_Get_1st_Connection_Num -- gets 1st Connect Number for given user    *
 *                                                                         *
 * This gets the Connection Number for the given User ID.  Since a user		*
 * may be logged in more than once, this just returns the first connection	*
 * found and ignores the others.															*
 *                                                                         *
 * INPUT:                                                                  *
 *		username		name of the user to get the Connection Number for			*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		first-found Connection Number for that user, 0 if user not logged in	*
 *                                                                         *
 * WARNINGS:                                                               *
 *                                                                         *
 * HISTORY:                                                                *
 *   12/15/1994 BR : Created.                                              *
 *=========================================================================*/
#ifndef WIN32		// WIN32 version is in IPX95.CPP
int IPX_Get_1st_Connection_Num (char * username)
{
	struct request_buffer {
		unsigned short len;							// username length + 5
		unsigned char buffer_type;					// ConnectionNum = 0x15
		unsigned short object_type;				// set ot 0x0100
		unsigned char name_len;						// length of username
		char name [48];								// copy of username
		unsigned short reserved;
	};
	struct reply_buffer {
		unsigned short  len;
		unsigned char   number_connections;		// will be 0 - 100
		unsigned char   connection_num [100];	// array of connection numbers
		unsigned short  reserved[2];
	};
	union REGS regs;
	struct SREGS sregs;
	RMIType rmi;
	struct request_buffer * reqbuf;
	struct reply_buffer * replybuf;
	unsigned short segment;							// for DOS allocation
	unsigned short selector;						// for DOS allocation
	int num_conns;										// # connections returned
	int conn_num;										// connection number
	int rc;

	//------------------------------------------------------------------------
	//	Allocate DOS memory to store the buffers passed to the interrupt
	//------------------------------------------------------------------------
	memset (&regs, 0 ,sizeof(regs));
	segread (&sregs);
	regs.x.eax = DPMI_ALLOC_DOS_MEM;						// DPMI function to call
	regs.x.ebx = (sizeof(struct request_buffer) + 	// # paragraphs to allocate
		sizeof(struct reply_buffer) + 15) >> 4;
	int386x (DPMI_INT, &regs, &regs, &sregs);			// allocate the memory

	//------------------------------------------------------------------------
	//	If the carry flag is set, DPMI is indicating an error.
	//------------------------------------------------------------------------
	if (regs.x.cflag) {
		return(0);
	}

	//------------------------------------------------------------------------
	//	Get pointers to allocated memory.
	//	'reqbuf' is just the returned real-mode segment, multiplied by 16.
	//	'replybuf' is an offset from 'reqbuf'.
	//------------------------------------------------------------------------
	segment = regs.w.ax;
	selector = regs.w.dx;
	reqbuf = (struct request_buffer *)(segment << 4);
	replybuf = (struct reply_buffer *)
		(((char *)reqbuf) + sizeof (struct request_buffer));

	//------------------------------------------------------------------------
	//	Init the contents of the request & reply buffers
	//------------------------------------------------------------------------
	reqbuf->len = (unsigned short)(strlen(username) + 5);
	reqbuf->buffer_type = 0x15;
	reqbuf->object_type = 0x0100;
	reqbuf->name_len    = (unsigned char) strlen(username);
	strcpy(reqbuf->name, username);
	reqbuf->reserved = reqbuf->reserved;				// prevent compiler warning
	replybuf->len = 101;
	replybuf->reserved[0] = replybuf->reserved[0];	// prevent compiler warning
	replybuf->reserved[0] = replybuf->reserved[1];	// prevent compiler warning

	//------------------------------------------------------------------------
	//	Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer
	//------------------------------------------------------------------------
	//........................................................................
	//	Fill in registers for the DPMI call
	//........................................................................
	memset (&regs, 0 ,sizeof(regs));
	segread (&sregs);
	memset (&rmi, 0 ,sizeof(rmi));
	regs.w.ax = DPMI_CALL_REAL_INT;	// DPMI function to call
	regs.w.bx = 0x21;						// interrupt # to invoke
	sregs.es = FP_SEG(&rmi);			// tell DPMI where the RMI is
	regs.x.edi = FP_OFF(&rmi);

	//........................................................................
	//	Fill in registers for the interrupt call
	//........................................................................
	rmi.eax = 0x0000e300;
	rmi.ds = segment;
	rmi.esi = 0;
	rmi.es = segment;
	rmi.edi = sizeof(struct request_buffer);

	//........................................................................
	//	call DPMI
	//........................................................................
	int386x(DPMI_INT, &regs, &regs, &sregs);

	//------------------------------------------------------------------------
	//	Stash the 1st connection number
	//------------------------------------------------------------------------
	rc = (rmi.eax & 0x00ff);								// if AL !=0, error
	num_conns = replybuf->number_connections;			// # times user is logged in
	conn_num = (int )replybuf->connection_num[0];	// 1st connection #

	//------------------------------------------------------------------------
	//	Free DOS memory
	//------------------------------------------------------------------------
	memset (&regs, 0 ,sizeof(regs));
	segread (&sregs);
	regs.x.eax = DPMI_FREE_DOS_MEM;						// DPMI function to call
	regs.x.edx = selector;									// ptr to free
	int386x (DPMI_INT, &regs, &regs, &sregs);			// allocate the memory

	//------------------------------------------------------------------------
	//	Return error if function failed, or user not logged in
	//------------------------------------------------------------------------
	if (rc != 0 || num_conns==0) {
		return(0);
	}
	else {
		return(conn_num);
	}

}	/* end of IPX_Get_1st_Connection_Num */

#endif	//WIN32

/***************************************************************************
 * IPX_Get_Internet_Address -- gets Network Number & Node Address				*
 *                                                                         *
 * Once you've obtained a Connection Number from IPX_Get_Connection_Number	*
 * or IPX_Get_1st_Connection_Num, use this function to translate it into	*
 * a Network Number and Node Address; then, place those numbers in the		*
 * IPX header for outgoing packets.														*
 *                                                                         *
 * INPUT:                                                                  *
 *		connection_number		Connection Number to translate						*
 *		network_number			ptr: will hold Network Number							*
 *		physical_node			ptr: will hold Node Address							*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		0 = OK, -1 = error																	*
 *                                                                         *
 * WARNINGS:                                                               *
 *		If connection_number is 0 and NETX isn't running, this routine			*
 *		will just put garbage into the network_number and physical_node.		*
 *                                                                         *
 * HISTORY:                                                                *
 *   12/15/1994 BR : Created.                                              *
 *=========================================================================*/
#ifndef WIN32		// WIN32 version is in IPX95.CPP
int IPX_Get_Internet_Address(int connection_number,
	unsigned char * network_number, unsigned char * physical_node)
{
	struct request_buffer {
		unsigned short  len;
		unsigned char   buffer_type;				// Internet = 0x13
		unsigned char   connection_number;		// Conn. Number to translate
	};
	struct reply_buffer {
		unsigned short  len;
		unsigned char   network_number [4];		// filled in by IPX
		unsigned char   physical_node  [6];		// filled in by IPX
		unsigned short  server_socket;			// filled in by IPX, but don't use!
	};
	union REGS regs;
	struct SREGS sregs;
	RMIType rmi;
	struct request_buffer * reqbuf;
	struct reply_buffer * replybuf;
	unsigned short segment;							// for DOS allocation
	unsigned short selector;						// for DOS allocation

	//------------------------------------------------------------------------
	//	Error if invalid connection is given
	//------------------------------------------------------------------------
	if (connection_number==0) {
		return(-1);
	}

	//------------------------------------------------------------------------
	//	Allocate DOS memory to store the buffers passed to the interrupt
	//------------------------------------------------------------------------
	memset (&regs, 0 ,sizeof(regs));
	segread (&sregs);
	regs.x.eax = DPMI_ALLOC_DOS_MEM;						// DPMI function to call
	regs.x.ebx = (sizeof(struct request_buffer) + 	// # paragraphs to allocate
		sizeof(struct reply_buffer) + 15) >> 4;
	int386x (DPMI_INT, &regs, &regs, &sregs);			// allocate the memory

	//------------------------------------------------------------------------
	//	If the carry flag is set, DPMI is indicating an error.
	//------------------------------------------------------------------------
	if (regs.x.cflag) {
		return(-1);
	}

	//------------------------------------------------------------------------
	//	Get pointers to allocated memory.
	//	'reqbuf' is just the returned real-mode segment, multiplied by 16.
	//	'replybuf' is an offset from 'reqbuf'.
	//------------------------------------------------------------------------
	segment = regs.w.ax;
	selector = regs.w.dx;
	reqbuf = (struct request_buffer *)(segment << 4);
	replybuf = (struct reply_buffer *)
		(((char *)reqbuf) + sizeof (struct request_buffer));

	//------------------------------------------------------------------------
	//	Init the contents of the request & reply buffers
	//------------------------------------------------------------------------
	reqbuf->len = 2;
	reqbuf->buffer_type = 0x13;
	reqbuf->connection_number = (unsigned char)connection_number;
	replybuf->len = 12;
	replybuf->network_number[0] = replybuf->network_number[0];	// suppress warning
	replybuf->physical_node[0] = replybuf->physical_node[0];		// suppress warning
	replybuf->server_socket = replybuf->server_socket;				// suppress warning

	//------------------------------------------------------------------------
	//	Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer
	//------------------------------------------------------------------------
	//........................................................................
	//	Fill in registers for the DPMI call
	//........................................................................
	memset (&regs, 0 ,sizeof(regs));
	segread (&sregs);
	memset (&rmi, 0 ,sizeof(rmi));
	regs.w.ax = DPMI_CALL_REAL_INT;	// DPMI function to call
	regs.w.bx = 0x21;						// interrupt # to invoke
	sregs.es = FP_SEG(&rmi);			// tell DPMI where the RMI is
	regs.x.edi = FP_OFF(&rmi);

	//........................................................................
	//	Fill in registers for the interrupt call
	//........................................................................
	rmi.eax = 0x0000e300;
	rmi.ds = segment;
	rmi.esi = 0;
	rmi.es = segment;
	rmi.edi = sizeof(struct request_buffer);

	//........................................................................
	//	call DPMI
	//........................................................................
	int386x(DPMI_INT, &regs, &regs, &sregs);

	memcpy(network_number, replybuf->network_number, 4);
	memcpy(physical_node,  replybuf->physical_node,  6);

	//------------------------------------------------------------------------
	//	Free DOS memory
	//------------------------------------------------------------------------
	memset (&regs, 0 ,sizeof(regs));
	segread (&sregs);
	regs.x.eax = DPMI_FREE_DOS_MEM;						// DPMI function to call
	regs.x.edx = selector;									// ptr to free
	int386x (DPMI_INT, &regs, &regs, &sregs);			// allocate the memory

	return(0);

}	/* end of IPX_Get_Internet_Address */
#endif	//WIN32

/***************************************************************************
 * IPX_Get_User_ID -- gets user ID from Connection Number                  *
 *                                                                         *
 * INPUT:                                                                  *
 *		connection_number		Connection Number to get User ID for				*
 *		user_id					ptr to buffer to put User ID into; 					*
 *									size must be >= 48 chars								*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		0 = OK, -1 = error																	*
 *                                                                         *
 * WARNINGS:                                                               *
 *		none.																						*
 *                                                                         *
 * HISTORY:                                                                *
 *   12/15/1994 BR : Created.                                              *
 *=========================================================================*/
#ifndef WIN32		// WIN32 version is in IPX95.CPP
int IPX_Get_User_ID(int connection_number, char * user_id)
{
	struct request_buffer {
		unsigned short  len;
		unsigned char   buffer_type;			// 0x16 = UserID buffer type
		unsigned char   connection_number;	// Connection Number to get ID for
	};
	struct reply_buffer {
		unsigned short  len;
		unsigned char   object_id[4];
		unsigned char   object_type[2];
		char            object_name[48];
		char            login_time[7];
		unsigned short  reserved;
	};
	union REGS      regs;
	struct SREGS    sregs;
	RMIType rmi;
	struct request_buffer * reqbuf;
	struct reply_buffer * replybuf;
	unsigned short segment;							// for DOS allocation
	unsigned short selector;						// for DOS allocation

	//------------------------------------------------------------------------
	//	Error if invalid connection is given
	//------------------------------------------------------------------------
	if (connection_number==0) {
		return(-1);
	}

	//------------------------------------------------------------------------
	//	Allocate DOS memory to store the buffers passed to the interrupt
	//------------------------------------------------------------------------
	memset (&regs, 0 ,sizeof(regs));
	segread (&sregs);
	regs.x.eax = DPMI_ALLOC_DOS_MEM;						// DPMI function to call
	regs.x.ebx = (sizeof(struct request_buffer) + 	// # paragraphs to allocate
		sizeof(struct reply_buffer) + 15) >> 4;
	int386x (DPMI_INT, &regs, &regs, &sregs);			// allocate the memory

	//------------------------------------------------------------------------
	//	If the carry flag is set, DPMI is indicating an error.
	//------------------------------------------------------------------------
	if (regs.x.cflag) {
		return(-1);
	}

	//------------------------------------------------------------------------
	//	Get pointers to allocated memory.
	//	'reqbuf' is just the returned real-mode segment, multiplied by 16.
	//	'replybuf' is an offset from 'reqbuf'.
	//------------------------------------------------------------------------
	segment = regs.w.ax;
	selector = regs.w.dx;
	reqbuf = (struct request_buffer *)(segment << 4);
	replybuf = (struct reply_buffer *)
		(((char *)reqbuf) + sizeof (struct request_buffer));

	//------------------------------------------------------------------------
	//	Init the contents of the request & reply buffers
	//------------------------------------------------------------------------
	reqbuf->len = 2;
	reqbuf->buffer_type = 0x16;
	reqbuf->connection_number = (unsigned char)connection_number;
	replybuf->len = sizeof(struct reply_buffer) - 2;
	replybuf->object_id[0] = replybuf->object_id[0];		// suppress warnings
	replybuf->object_type[0] = replybuf->object_type[0];	// suppress warnings
	replybuf->login_time[0] = replybuf->login_time[0];		// suppress warnings
	replybuf->reserved = replybuf->reserved;					// suppress warnings

	//------------------------------------------------------------------------
	//	Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer
	//------------------------------------------------------------------------
	//........................................................................
	//	Fill in registers for the DPMI call
	//........................................................................
	memset (&regs, 0 ,sizeof(regs));
	segread (&sregs);
	memset (&rmi, 0 ,sizeof(rmi));
	regs.w.ax = DPMI_CALL_REAL_INT;	// DPMI function to call
	regs.w.bx = 0x21;						// interrupt # to invoke
	sregs.es = FP_SEG(&rmi);			// tell DPMI where the RMI is
	regs.x.edi = FP_OFF(&rmi);

	//........................................................................
	//	Fill in registers for the interrupt call
	//........................................................................
	rmi.eax = 0x0000e300;
	rmi.ds = segment;
	rmi.esi = 0;
	rmi.es = segment;
	rmi.edi = sizeof(struct request_buffer);

	//........................................................................
	//	call DPMI
	//........................................................................
	int386x(DPMI_INT, &regs, &regs, &sregs);

	//------------------------------------------------------------------------
	//	Fill in the caller's buffer with the user name
	//------------------------------------------------------------------------
	strncpy(user_id, replybuf->object_name, 48);

	//------------------------------------------------------------------------
	//	Free DOS memory
	//------------------------------------------------------------------------
	memset (&regs, 0 ,sizeof(regs));
	segread (&sregs);
	regs.x.eax = DPMI_FREE_DOS_MEM;						// DPMI function to call
	regs.x.edx = selector;									// ptr to free
	int386x (DPMI_INT, &regs, &regs, &sregs);			// allocate the memory

	return(0);

}	/* end of IPX_Get_User_ID */
#endif	//WIN32

/***************************************************************************
 * IPX_Listen_For_Packet -- commands IPX to listen for a packet            *
 *                                                                         *
 * Before calling this function, you must fill in an ECB:						*
 *		SocketNumber:				must contain the socket you've opened,			*
 *										and are "listening" on								*
 *		Event_Service_Routine:	optionally points to a callback routine		*
 *		PacketCount:				set to 2, to tell IPX there are 2 areas to	*
 *										store the incoming data in							*
 *		Packet[0].Address:		set to the address of an IPXHeaderType			*
 *		Packet[0].Length:			sizeof(IPXHeaderType)								*
 *		Packet[1].Address:		address of data buffer, for the packet			*
 *		Packet[1].Length:			size of the data buffer								*
 *                                                                         *
 * When the packet is received, ECBType.CompletionCode will be 0 if 			*
 * successful.  Otherwise, some error occurred.										*
 *                                                                         *
 * You should initialize the ECB to 0's before filling it in.					*
 *                                                                         *
 * INPUT:                                                                  *
 *		ecb_ptr		pointer to a filled-in ECB; MUST be real-mode memory		*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		0 = OK, IPX error otherwise														*
 *                                                                         *
 * WARNINGS:                                                               *
 *		The ECB must be located in real-mode memory, as well as the values	*
 *		pointed to in the ECB (the IPX Header, the buffer to send, etc.)		*
 *                                                                         *
 * HISTORY:                                                                *
 *   12/15/1994 BR : Created.                                              *
 *=========================================================================*/
#ifndef WIN32		// WIN32 version is in IPX95.CPP
int IPX_Listen_For_Packet(struct ECB *ecb_ptr)
{
	union REGS      regs;
	struct SREGS    sregs;
	RMIType rmi;

	//------------------------------------------------------------------------
	//	Call IPX with ES:SI=ecb_ptr
	//------------------------------------------------------------------------
	//........................................................................
	//	Fill in registers for the DPMI call
	//........................................................................
	memset (&regs, 0 ,sizeof(regs));
	segread (&sregs);
	memset (&rmi, 0 ,sizeof(rmi));
	regs.w.ax = DPMI_CALL_REAL_INT;	// DPMI function to call
	regs.w.bx = IPX_INT;					// interrupt # to invoke
	sregs.es = FP_SEG(&rmi);			// tell DPMI where the RMI is
	regs.x.edi = FP_OFF(&rmi);

	//........................................................................
	//	Fill in registers for the interrupt call
	//........................................................................
	rmi.ebx = IPX_LISTEN_FOR_PACKET;
	rmi.es = (short)((long)ecb_ptr >> 4);
	rmi.esi = (long)((long)ecb_ptr & 0x000f);

	//........................................................................
	//	call DPMI
	//........................................................................
	int386x(DPMI_INT, &regs, &regs, &sregs);

	return(rmi.eax & 0x00ff);

}	/* end of IPX_Listen_For_Packet */
#endif	//WIN32

/***************************************************************************
 * IPX_Send_Packet -- commands IPX to send a packet                        *
 *                                                                         *
 * Before calling this function, you must fill in an ECB:						*
 *		SocketNumber:				must contain the socket you've opened,			*
 *										and are sending on									*
 *		Event_Service_Routine:	optionally points to a callback routine		*
 *		PacketCount:				set to 2, to tell IPX there are 2 areas the	*
 *										outgoing data is stored in							*
 *		Packet[0].Address:		set to the address of an IPXHeaderType			*
 *		Packet[0].Length:			sizeof(IPXHeaderType)								*
 *		Packet[1].Address:		address of buffer containing data to send		*
 *		Packet[1].Length:			size of the data buffer								*
 *		ImmediateAddress:			must be filled in with the node address of	*
 *										the bridge that will route the message;		*
 *										fill this in by calling IPX_Get_Local_Target	*
 *                                                                         *
 * Also, you must fill in the IPXHeaderType with certain values:				*
 *		PacketType:					set to 4 for IPX										*
 *		DestNetworkNumber:		Network Number of the destination system		*
 *		DestNetworkNode:			Node Address of the destination system			*
 *		DestNetworkSocket:		the destination system's socket to send to;	*
 *										this doesn't have to be the same as the		*
 *										socket opened on the local machine.				*
 *                                                                         *
 * You should initialize the ECB & IPXHeader to 0's before filling them in.*
 *                                                                         *
 * INPUT:                                                                  *
 *		ecb_ptr		pointer to a filled-in ECB											*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		none.  This function doesn't return anything; the caller must check	*
 *    its ECB.CompletionCode for: 0 = OK, IPX Error otherwise.					*
 *                                                                         *
 * WARNINGS:                                                               *
 *		The ECB must be located in real-mode memory, as well as the values	*
 *		pointed to in the ECB (the IPX Header, the buffer to send, etc.)		*
 *                                                                         *
 * HISTORY:                                                                *
 *   12/15/1994 BR : Created.                                              *
 *=========================================================================*/
#ifndef WIN32		// WIN32 version is in IPX95.CPP
void IPX_Send_Packet(struct ECB *ecb_ptr)
{
	union REGS      regs;
	struct SREGS    sregs;
	RMIType rmi;

	//------------------------------------------------------------------------
	//	Call IPX with ES:SI=ecb_ptr
	//------------------------------------------------------------------------
	//........................................................................
	//	Fill in registers for the DPMI call
	//........................................................................
	memset (&regs, 0 ,sizeof(regs));
	segread (&sregs);
	memset (&rmi, 0 ,sizeof(rmi));
	regs.w.ax = DPMI_CALL_REAL_INT;	// DPMI function to call
	regs.w.bx = IPX_INT;					// interrupt # to invoke
	sregs.es = FP_SEG(&rmi);			// tell DPMI where the RMI is
	regs.x.edi = FP_OFF(&rmi);

	//........................................................................
	//	Fill in registers for the interrupt call
	//........................................................................
	rmi.ebx = IPX_SEND_PACKET;
	rmi.es = (short)((long)ecb_ptr >> 4);
	rmi.esi = (long)((long)ecb_ptr & 0x000f);

	//........................................................................
	//	call DPMI
	//........................................................................
	int386x(DPMI_INT, &regs, &regs, &sregs);

}	/* end of IPX_Send_Packet */
#endif	//WIN32

/***************************************************************************
 * IPX_Get_Local_Target -- fills in ImmediateAddress field of ECB          *
 *                                                                         *
 * Use this function to fill in the ECB's ImmediateAddress field when		*
 * sending a packet.  The Immediate Address is the node address of the		*
 * bridge that must route the message.  If there is no bridge, it's 			*
 * filled in with the destination Node Address.  In either case, it			*
 * will contain the proper value for sending.										*
 *                                                                         *
 * INPUT:                                                                  *
 *		dest_network			destination Network Number								*
 *		dest_node				destination Node Address								*
 *		dest_socket				destination Socket Number								*
 *		bridge_address			field to fill in with Immediate Address			*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		0 = OK, error otherwise																*
 *                                                                         *
 * WARNINGS:                                                               *
 *		If the Connection Number is 0 (user not logged in), dest_network		*
 *		and dest_node will be garbage, and this routine will probably crash.	*
 *                                                                         *
 * HISTORY:                                                                *
 *   12/15/1994 BR : Created.                                              *
 *=========================================================================*/
#ifndef WIN32		// WIN32 version is in IPX95.CPP
int IPX_Get_Local_Target(unsigned char *dest_network, unsigned char *dest_node,
	unsigned short dest_socket, unsigned char *bridge_address)
{
	struct request_buffer {
		unsigned char   network_number [4];
		unsigned char   physical_node  [6];
		unsigned short  socket;
	};
	struct reply_buffer {
		unsigned char   local_target [6];
	};
	unsigned int rc;
	union REGS regs;
	struct SREGS sregs;
	RMIType rmi;
	struct request_buffer *reqbuf;
	struct reply_buffer *replybuf;
	unsigned short segment;							// for DOS allocation
	unsigned short selector;						// for DOS allocation

	//------------------------------------------------------------------------
	//	Allocate DOS memory to store the buffers passed to the interrupt
	//------------------------------------------------------------------------
	memset (&regs, 0 ,sizeof(regs));
	segread (&sregs);
	regs.x.eax = DPMI_ALLOC_DOS_MEM;						// DPMI function to call
	regs.x.ebx = (sizeof(struct request_buffer) + 	// # paragraphs to allocate
		sizeof(struct reply_buffer) + 15) >> 4;
	int386x (DPMI_INT, &regs, &regs, &sregs);			// allocate the memory

	//------------------------------------------------------------------------
	//	If the carry flag is set, DPMI is indicating an error.
	//------------------------------------------------------------------------
	if (regs.x.cflag) {
		return(-1);
	}

	//------------------------------------------------------------------------
	//	Get pointers to allocated memory.
	//	'reqbuf' is just the returned real-mode segment, multiplied by 16.
	//	'replybuf' is an offset from 'reqbuf'.
	//------------------------------------------------------------------------
	segment = regs.w.ax;
	selector = regs.w.dx;
	reqbuf = (struct request_buffer *)(segment << 4);
	replybuf = (struct reply_buffer *)
		(((char *)reqbuf) + sizeof (struct request_buffer));

	//------------------------------------------------------------------------
	//	Init the contents of the request & reply buffers
	//------------------------------------------------------------------------
	memcpy(reqbuf->network_number, dest_network, 4);
	memcpy(reqbuf->physical_node, dest_node, 6);
	reqbuf->socket = dest_socket;

	//------------------------------------------------------------------------
	//	Invoke IPX with DS:SI=&request_buffer, ES:DI=&reply_buffer
	//------------------------------------------------------------------------
	//........................................................................
	//	Fill in registers for the DPMI call
	//........................................................................
	memset (&regs, 0 ,sizeof(regs));
	segread (&sregs);
	memset (&rmi, 0 ,sizeof(rmi));
	regs.w.ax = DPMI_CALL_REAL_INT;	// DPMI function to call
	regs.w.bx = IPX_INT;					// interrupt # to invoke
	sregs.es = FP_SEG(&rmi);			// tell DPMI where the RMI is
	regs.x.edi = FP_OFF(&rmi);

	//........................................................................
	//	Fill in registers for the interrupt call
	//........................................................................
	rmi.ebx = IPX_GET_LOCAL_TARGET;
	rmi.ds = segment;
	rmi.esi = 0;
	rmi.es = segment;
	rmi.edi = sizeof(struct request_buffer);
	//........................................................................
	//	call DPMI
	//........................................................................
	int386x(DPMI_INT, &regs, &regs, &sregs);

	rc = (rmi.eax & 0x00ff);
	memcpy(bridge_address, replybuf->local_target, 6);

	//------------------------------------------------------------------------
	//	Free DOS memory
	//------------------------------------------------------------------------
	memset (&regs, 0 ,sizeof(regs));
	segread (&sregs);
	regs.x.eax = DPMI_FREE_DOS_MEM;						// DPMI function to call
	regs.x.edx = selector;									// ptr to free
	int386x (DPMI_INT, &regs, &regs, &sregs);			// allocate the memory

	return(rc);

}	/* end of IPX_Get_Local_Target */
#endif	//WIN32

/***************************************************************************
 * IPX_Cancel_Event -- cancels an operation in progress                    *
 *                                                                         *
 * INPUT:                                                                  *
 *		ecb_ptr		pointer to ECB event to cancel									*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		???																						*
 *                                                                         *
 * WARNINGS:                                                               *
 *		The ECB must be located in real-mode memory, as well as the values	*
 *		pointed to in the ECB (the IPX Header, the buffer to send, etc.)		*
 *                                                                         *
 * HISTORY:                                                                *
 *   12/15/1994 BR : Created.                                              *
 *=========================================================================*/
#ifndef WIN32		// WIN32 version is in IPX95.CPP
int IPX_Cancel_Event(struct ECB *ecb_ptr)
{
	union REGS      regs;
	struct SREGS    sregs;
	RMIType rmi;
	unsigned int rc;

	//------------------------------------------------------------------------
	//	Fill in registers for the DPMI call
	//------------------------------------------------------------------------
	memset (&regs, 0 ,sizeof(regs));
	segread (&sregs);
	memset (&rmi, 0 ,sizeof(rmi));
	regs.w.ax = DPMI_CALL_REAL_INT;	// DPMI function to call
	regs.w.bx = IPX_INT;					// interrupt # to invoke
	sregs.es = FP_SEG(&rmi);			// tell DPMI where the RMI is
	regs.x.edi = FP_OFF(&rmi);

	//------------------------------------------------------------------------
	//	Fill in registers for the interrupt call
	//------------------------------------------------------------------------
	rmi.ebx = IPX_CANCEL_EVENT;
	rmi.es = (short)((long)ecb_ptr >> 4);
	rmi.esi = (long)((long)ecb_ptr & 0x000f);

	//------------------------------------------------------------------------
	//	call DPMI
	//------------------------------------------------------------------------
	int386x(DPMI_INT, &regs, &regs, &sregs);

	rc = (rmi.eax & 0x00ff);

	return(rc);

}	/* end of IPX_Cancel_Event */
#endif	//WIN32

/***************************************************************************
 * Let_IPX_Breath -- gives IPX some CPU time                               *
 *                                                                         *
 * Use this function if you're polling the ECB's InUse flag, waiting			*
 * for it to go to 0:																		*
 *                                                                         *
 *		while ECBType.InUse																	*
 *			Let_IPX_Breath();																	*
 *                                                                         *
 * INPUT:                                                                  *
 *		none.																						*
 *                                                                         *
 * OUTPUT:                                                                 *
 *		none.																						*
 *                                                                         *
 * WARNINGS:                                                               *
 *		none.																						*
 *                                                                         *
 * HISTORY:                                                                *
 *   12/15/1994 BR : Created.                                              *
 *=========================================================================*/
#ifndef WIN32		// WIN32 version is in IPX95.CPP
void Let_IPX_Breath(void)
{
	union REGS      regs;
	struct SREGS    sregs;
	RMIType rmi;

	//------------------------------------------------------------------------
	//	Fill in registers for the DPMI call
	//------------------------------------------------------------------------
	memset (&regs, 0 ,sizeof(regs));
	segread (&sregs);
	memset (&rmi, 0 ,sizeof(rmi));
	regs.w.ax = DPMI_CALL_REAL_INT;	// DPMI function to call
	regs.w.bx = IPX_INT;					// interrupt # to invoke
	sregs.es = FP_SEG(&rmi);			// tell DPMI where the RMI is
	regs.x.edi = FP_OFF(&rmi);

	//------------------------------------------------------------------------
	//	Fill in registers for the interrupt call
	//------------------------------------------------------------------------
	rmi.ebx = IPX_RELINQUISH_CONTROL;

	//------------------------------------------------------------------------
	//	call DPMI
	//------------------------------------------------------------------------
	int386x(DPMI_INT, &regs, &regs, &sregs);

}	/* end of Let_IPX_Breath */
#endif	//WIN32
/**************************** end of ipx.cpp *******************************/