#include <nt.h>
#include <ntddtx.h>
#include <malloc.h>

#include "sim32.h"

UCHAR		TransmitPkt[MAXSIZE];
UCHAR		ReceivePkt[MAXSIZE];

HANDLE		DeviceHandle;
IO_STATUS_BLOCK IoStatusBlock;
NTSTATUS	Status;


/*****************************************************************************
*
*	Sim32GetVDMMemory
*
*	This routine gets 'Size' bytes from WOW VDM starting at address
*	specified by 'Address'. These bytes are returned to the caller in
*	the Buffer (which is owned by the caller).
*
*****************************************************************************/


USHORT Sim32GetVDMMemory (IN ULONG Address,
		       IN USHORT  Size,
		       IN OUT PVOID Buffer)

{
    if (Size < MAXSIZE-11) {
	TransmitPkt[0] = SOH;
	TransmitPkt[1] = GETMEM;
	TransmitPkt[2] = 11;
	TransmitPkt[3] = 0;
	TransmitPkt[4] = (UCHAR) FIRSTBYTE(Address);
	TransmitPkt[5] = (UCHAR) SECONDBYTE(Address);
	TransmitPkt[6] = (UCHAR) THIRDBYTE(Address);
	TransmitPkt[7] = (UCHAR) FOURTHBYTE(Address);
	TransmitPkt[8] = (UCHAR) FIRSTBYTE(Size);
	TransmitPkt[9] = (UCHAR) SECONDBYTE(Size);
	TransmitPkt[10] = EOT;

	if (!Xceive((USHORT)(Size+5), 11)) {
	    DbgPrint ("Sim32GetVDMMemory.....BAD Memory \a\n");
	    return (BAD);
	}

	RtlMoveMemory(Buffer, &ReceivePkt[4], Size);

	return(GOOD);

    }
    else {
	DbgPrint ("Bad Packet Size %d\n", Size);
	return (BADSIZE);
    }
}


/*****************************************************************************
*
*	Sim32SetVDMMemory
*
*	This routine sets 'Size' bytes in WOW VDM starting at address
*	specified by 'Address' to the values in Buffer.
*
*****************************************************************************/


USHORT Sim32SetVDMMemory (IN ULONG Address,
		       IN USHORT  Size,
		       IN OUT PVOID Buffer)
{
    if (Size < MAXSIZE-11) {
	TransmitPkt[0] = SOH;
	TransmitPkt[1] = SETMEM;
	TransmitPkt[2] = (UCHAR) (Size+11);
	TransmitPkt[3] = 0;
	TransmitPkt[4] = (UCHAR) FIRSTBYTE(Address);
	TransmitPkt[5] = (UCHAR) SECONDBYTE(Address);
	TransmitPkt[6] = (UCHAR) THIRDBYTE(Address);
	TransmitPkt[7] = (UCHAR) FOURTHBYTE(Address);
	TransmitPkt[8] = (UCHAR) FIRSTBYTE(Size);
	TransmitPkt[9] = (UCHAR) SECONDBYTE(Size);
	TransmitPkt[10+Size] = EOT;

	RtlMoveMemory(&TransmitPkt[10], Buffer, Size);

	if (!Xceive(7, (USHORT)(Size+11))) {
	    DbgPrint ("Sim32SetVDMMemory... could not set : \a\n");
	    return (BAD);
	}

	return(GOOD);

    }
    else  {
	DbgPrint ("Bad Packet Size %d\n", Size);
	return (BADSIZE);
    }
}



/*****************************************************************************
*
*	Sim32GetVDMPSZPointer
*
*	This routine returns a pointer to a null terminated string in the WOW
*	VDM at the specified address.
*
*	This routine does the following,
*	    allocates a sufficient size buffer,
*	    gets the string from SIM16,
*	    copies the string into buffer,
*	    returns a pointer to the buffer.
*
*****************************************************************************/


PSZ  Sim32GetVDMPSZPointer (IN ULONG Address)
{
    USHORT  Size;
    PSZ     Ptr;


    TransmitPkt[0] = SOH;
    TransmitPkt[1] = PSZLEN;
    TransmitPkt[2] = 9;
    TransmitPkt[3] = 0;
    TransmitPkt[4] = (UCHAR) FIRSTBYTE(Address);
    TransmitPkt[5] = (UCHAR) SECONDBYTE(Address);
    TransmitPkt[6] = (UCHAR) THIRDBYTE(Address);
    TransmitPkt[7] = (UCHAR) FOURTHBYTE(Address);
    TransmitPkt[8] = EOT;

    if (!Xceive(7, 9)) {
	DbgPrint ("Sim32GetVDMPSZPointer.....Attempt to get PSZ length failed \a\a\n");
	return NULL;
    }

    Size = *(PUSHORT)(ReceivePkt+4);


    //
    //	allocate buffer to copy string into
    //

    Ptr = (PSZ) malloc(Size);

    if (!Ptr) {
	DbgPrint ("Sim32GetVDMPSZPointer...,  malloc failed \a\a\n");
    }


    //
    // get null terminated string
    //

    if (Size < MAXSIZE-11) {
	TransmitPkt[1] = GETMEM;
	TransmitPkt[2] = 11;
	TransmitPkt[3] = 0;
	TransmitPkt[8] = (UCHAR) FIRSTBYTE(Size);
	TransmitPkt[9] = (UCHAR) SECONDBYTE(Size);
	TransmitPkt[10] = EOT;

	if (!Xceive((USHORT)(Size+5), 11)) {
	    DbgPrint ("Sim32GetVDMPSZPointer.....Unsuccessful \a\a\n");
	    return NULL;
	}

	RtlMoveMemory(Ptr, &ReceivePkt[4], Size);
    } else {
	DbgPrint ("Sim32GetVDMPSZPointer.....Size of the string too big Size = %d\a\a\n", Size);
	return NULL;
    }

    return Ptr;

}



/*****************************************************************************
*
*	Sim32FreeVDMPointer
*
*	This routine frees the buffer which was allocated earlier.
*
*****************************************************************************/


VOID Sim32FreeVDMPointer (PVOID Ptr)
{
    free (Ptr);
}



/*****************************************************************************
*
*	Sim32SendSim16
*
*	This routine specifies the stack of the WOW VDM task and asks the
*	WOW 16 to make that task run.
*
*****************************************************************************/


USHORT Sim32SendSim16 (IN OUT ULONG *WOWStack)
{
    static  USHORT fInit = 0;

    if (fInit) {
	TransmitPkt[0] = SOH;
	TransmitPkt[1] = WAKEUP;
	TransmitPkt[2] = 9;
	TransmitPkt[3] = 0;
	TransmitPkt[4] = (UCHAR) FIRSTBYTE(*WOWStack);
	TransmitPkt[5] = (UCHAR) SECONDBYTE(*WOWStack);
	TransmitPkt[6] = (UCHAR) THIRDBYTE(*WOWStack);
	TransmitPkt[7] = (UCHAR) FOURTHBYTE(*WOWStack);
	TransmitPkt[8] = EOT;

	if (!Xceive(9, 9)) {
	    return (BAD);
	}

	*WOWStack = *(PULONG)(ReceivePkt+4);

	return(GOOD);
    }
    else {
	Initialize(9);
	*WOWStack = *(PULONG)(ReceivePkt+4);
	fInit = 1;
	return (GOOD);
    }
}


/*****************************************************************************
*
*	Xceive
*
*	This routine transmits a packet and waits for the data from the remote
*	side to come. When this routine returns, the ReceivePkt has the data
*	sent by the remote machine.
*
*****************************************************************************/


USHORT Xceive(IN USHORT Length_In, IN USHORT Length_Out)
{
    BOOLEAN Done = FALSE;
    USHORT     i = 0;

    while ((i < MAXTRY) && (!Done)) {

	Status = NtDeviceIoControlFile(
		DeviceHandle,
		NULL,
		NULL,
		NULL,
		&IoStatusBlock,
		IOCTL_TRNXT_XCEIVE,
		TransmitPkt,
		(ULONG) Length_Out,
		ReceivePkt,
		(ULONG) Length_In
		);

	//
	// check error condition
	// if no error, then
	//

	if (ReceivePkt[0] == SOH) {
	    if (ReceivePkt[1] != NAK) {
		i = *(PUSHORT)(ReceivePkt+2);
		if (ReceivePkt[(--i)] == EOT) {
		    Done = TRUE;
		}
		else {
		    DbgPrint ("EOT is missing from the packet, *ERROR*, Do Not Proceed Further !\a\a\n");
		}
	    }
	    else {
		DbgPrint ("It is a NAK packet, *ERROR*, Do Not Proceed Further !\a\a\n");
	    }
	}
	else {
	    DbgPrint ("SOH is missing from the packet, *ERROR*, Do Not Proceed Further !\a\a\n");
	}

	if (!Done) {
	    i++;
	    DbgPrint ("\nSTOP STOP STOP !!!\a\a\a\a\a\n");
	}
    }

    if (Done) {
	return (GOOD);
    }
    else {
	return (BAD);
    }

}

void Initialize (IN USHORT Length_In)
{
    OBJECT_ATTRIBUTES	ObjectAttributes;

    STRING		DeviceName;
    USHORT		j;
    char		TestPkt[] = "WOW 32 Simulator on NT\n\r";

    RtlInitString(&DeviceName, "\\Device\\Serial1");

    //
    // set attributes
    //

    ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
    ObjectAttributes.RootDirectory = NULL;
    ObjectAttributes.ObjectName = &DeviceName;
    ObjectAttributes.Attributes = OBJ_INHERIT;
    ObjectAttributes.SecuriAR) SECONDBYTE(Size);
	TransmitPkt[10] = EOT;

	if (!Xceive((USHORT)(Size+5), 11)) {
	    DbgPrint ("Sim32GetVDMPSZPointer.....Unsuccessful \a\a\n");
	    return NULL;
	}

	RtlMoveMemory(Ptr, &ReceivePkt[4], Size);
    } else {
	DbgPrint ("Sim32GetVDMPSZPointer.....Size of the string too big Size = %d\a\a\n", Size);
	return NULL;
    }

    return Ptr;

}



/*****************************************************************************
*
*	Sim32FreeVDMPointer
*
*	This routine frees the buffer which was allocated earlier.
*
*****************************************************************************/


VOID Sim32FreeVDMPointer (PVOID Ptr)
{
    free (Ptr);
}



/******************************
