/*******************************************************************************
********************************************************************************
**
**  Copyright(c) 2022, Alliance for Automotive Innovation
**  Used only under license from the Alliance for Automotive Innovation. All Rights Reserved.
**
**  Project:  J1699-3
**  FileName: SidSaveResponseData.c
**  Author:   EnGenius
**  Date:     2/25/2022
**  Email:    <support@autosinnovate.org>
**
**  Purpose:  SAE J1699-3 Vehicle OBD II Compliance Test Cases Source Code.
**            This source code is intended to run the tests described in
**            the SAE J1699-3 document in an automated manner, when compiled
**            and used with an SAE J2534-compatible pass-thru device.
**
**            File j1699.c contains information on building and running this test.
**
**  Description:
**
**  Modifications:  12/07/2022  Initial Version
**
********************************************************************************
*******************************************************************************/

#include <stdio.h>    // C Library input and output declarations
#include <stdlib.h>   // C Library general function declarations
#include <string.h>   // C Library character array declarations
#include <time.h>     // C Library time and date declarations
#include <windows.h>  // Windows API declarations
#include "j2534.h"    // j1699 project j2534 declarations
#include "j1699.h"    // j1699 project general declarations


// Function Prototypes
STATUS LookupEcuIndex  ( PASSTHRU_MSG  *pRxMsg,
                         BYTE          *pEcuIdx );
void   ChkIFRAdjust    ( PASSTHRU_MSG  *pRxMsg );   // Logic ajustment for PID Group Reverse order request.
STATUS IsMessageUnique ( BYTE          *pBuffer,    // pointer to buffer
                         unsigned short BufSize,    // size of buffer
                         BYTE          *pMsg,       // pointer to message to be checked
                         unsigned short MsgSize );  // size of message
STATUS LookupEcuIndex  ( PASSTHRU_MSG  *pRxMsg,
                         BYTE          *pEcuIndex );

extern STATUS VerifyEcuId ( BYTE EcuId[] );


extern unsigned int  gVariablePidSize;


/*******************************************************************************
**
**  Function:  SaveSidResponseData
**
**  Purpose:   Save SID response data
**
*******************************************************************************/
STATUS SaveSidResponseData ( PASSTHRU_MSG  *pRxMsg,
                             REQ_MSG       *pstReqMsg,
                             BYTE          *pNumResponses )
{
	BYTE          HeaderSize;
	BYTE          Data0Offset;
	BYTE          EcuIdx;
	unsigned long ByteIdx;
	BYTE          ElementOffset;
	unsigned long Index;  // coordinate mode 6 data respones for multiple data response.
	unsigned int  ExpectedDataSize = 0;
	BOOL          bCheckDataSize = TRUE;


	// Set the response header size based on the protocol
	HeaderSize = gstProtocolList[gProtocolIdx].HeaderSize;
	Data0Offset = HeaderSize + 1;

	// Get index into gstResponse struct
	if ( LookupEcuIndex ( pRxMsg, &EcuIdx ) != PASS )
	{
		return FAIL;
	}

	ChkIFRAdjust ( pRxMsg );

	if ( gstResponse[EcuIdx].bResponseReceived == TRUE )
	{
		Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "ECU %X  has already responded\n",
		      GetEcuId ( EcuIdx ) );
	}
	else
	{
		(*pNumResponses)++;  // count the new response
	}

	// Save the data in the appropriate SID/PID/MID/TID/InfoType
	switch ( pRxMsg->Data[HeaderSize] )
	{
		// SID 1 (Mode 1)
		case 0x41:
		{
			// indicate that this ECU has responded
			gstResponse[EcuIdx].bResponseReceived = TRUE;

			// Process message based on PID number
			switch ( pRxMsg->Data[Data0Offset] )
			{
				case 0x00:
				{
					if ( gbVerifyLink == TRUE ||
					     gbDetermineProtocol == TRUE )
					{
						gstResponse[EcuIdx].bLinkActive = TRUE;

						if ( gstResponse[EcuIdx].PidSupportSize != 0 )
						{
							break;
						}
					}
				}
				// 0x00 falls through

				case 0x20:
				case 0x40:
				case 0x60:
				case 0x80:
				case 0xA0:
				case 0xC0:
				case 0xE0:
				{
					// Make sure there is enough data in the message to process
					if ( pRxMsg->DataSize < (Data0Offset + sizeof ( ID_SUPPORT )) )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  Not enough data in SID $01 PID $%02X support response message to process\n",
						      GetEcuId ( EcuIdx ),
						      pRxMsg->Data[Data0Offset] );
						return FAIL;
					}

					// Save the data in the buffer and set the new size
					for ( Index = Data0Offset;
					      Index < pRxMsg->DataSize;
					      Index += sizeof ( ID_SUPPORT ) )
					{
						// find the element number (for the array of structures)
						ElementOffset = (BYTE)(pRxMsg->Data[Index]) >> 5;

						// move the data into the element
						memcpy ( &gstResponse[EcuIdx].PidSupport[ElementOffset],
						         &pRxMsg->Data[Index],
						         sizeof ( ID_SUPPORT ) );

						// increase the element count if the element in the array was previously unused
						if ( (ElementOffset + 1) > gstResponse[EcuIdx].PidSupportSize )
						{
							gstResponse[EcuIdx].PidSupportSize = (ElementOffset + 1);
						}
					}
				}
				//  00-E0 FALL THROUGH AND TREAT LIKE OTHER PIDS

				default:
				{
					// If not PID 0, then check if this is a response to an unsupported PID
					if ( gbIgnoreUnsupported == FALSE &&
					     pRxMsg->Data[Data0Offset] != 0 &&
					     (gstResponse[EcuIdx].PidSupport[(pRxMsg->Data[Data0Offset] - 1) >> 5].SupBits[((pRxMsg->Data[Data0Offset] - 1) >> 3) & 0x03]
					       & (0x80 >> ((pRxMsg->Data[Data0Offset] - 1) & 0x07))) == 0 )
					{
						Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  Unsupported SID $01 PID $%02X detected\n",
						      GetEcuId ( EcuIdx ),
						      pRxMsg->Data[Data0Offset] );
					}

					// Make sure there is enough data in the message to process
					if ( pRxMsg->DataSize <= (unsigned long)(Data0Offset) )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  Not enough data in SID $01 PID $%02X response message to process\n",
						      GetEcuId ( EcuIdx ),
						      pRxMsg->Data[Data0Offset] );
						return FAIL;
					}

					// If only one PID requested, make sure the
					// PID has the correct amount of data
					if ( pstReqMsg->NumIds == 1 )
					{
						switch ( pRxMsg->Data[Data0Offset] )
						{
							// Single byte PIDs
							case 0x04:
							case 0x05:
							case 0x0A:
							case 0x0B:
							case 0x0D:
							case 0x0E:
							case 0x0F:
							case 0x11:
							case 0x12:
							case 0x13:
							case 0x1C:
							case 0x1D:
							case 0x1E:
							case 0x2C:
							case 0x2D:
							case 0x2E:
							case 0x2F:
							case 0x30:
							case 0x33:
							case 0x45:
							case 0x46:
							case 0x47:
							case 0x48:
							case 0x49:
							case 0x4A:
							case 0x4B:
							case 0x4C:
							case 0x51:
							case 0x52:
							case 0x5A:
							case 0x5B:
							case 0x5C:
							case 0x5F:
							case 0x61:
							case 0x62:
							case 0x7D:
							case 0x7E:
							case 0x84:
							case 0x8D:
							case 0x8E:
							{
								ExpectedDataSize = 1;
							}
							break;

							// Two byte PIDs
							case 0x02:
							case 0x03:
							case 0x0C:
							case 0x10:
							case 0x14:
							case 0x15:
							case 0x16:
							case 0x17:
							case 0x18:
							case 0x19:
							case 0x1A:
							case 0x1B:
							case 0x1F:
							case 0x21:
							case 0x22:
							case 0x23:
							case 0x31:
							case 0x32:
							case 0x3C:
							case 0x3D:
							case 0x3E:
							case 0x3F:
							case 0x42:
							case 0x43:
							case 0x44:
							case 0x4D:
							case 0x4E:
							case 0x53:
							case 0x54:
							case 0x59:
							case 0x5D:
							case 0x5E:
							case 0x63:
							case 0x65:
							case 0x92:
							case 0x9E:
							case 0xA2:
							{
								ExpectedDataSize = 2;
							}
							break;

							// Three byte PIDs
							case 0x67:
							case 0x6F:
							case 0x90:
							case 0x93:
							{
								ExpectedDataSize = 3;
							}
							break;

							// Four byte PIDs
							case 0x00:
							case 0x20:
							case 0x40:
							case 0x60:
							case 0x80:
							case 0xA0:
							case 0xC0:
							case 0xE0:
							case 0x01:
							case 0x24:
							case 0x25:
							case 0x26:
							case 0x27:
							case 0x28:
							case 0x29:
							case 0x2A:
							case 0x2B:
							case 0x34:
							case 0x35:
							case 0x36:
							case 0x37:
							case 0x38:
							case 0x39:
							case 0x3A:
							case 0x3B:
							case 0x41:
							case 0x4F:
							case 0x50:
							case 0x9B:
							case 0x9D:
							case 0xA4:
							case 0xA5:
							case 0xA6:
							{
								ExpectedDataSize = 4;
								// IF not four bytes of data and PID $01, save appropriate size data to allow the other ECU's to be saved and evaluated
								if ( (pRxMsg->DataSize - (HeaderSize + 2)) != 4 &&
								     pRxMsg->Data[Data0Offset] == 0x01 )
								{
									if ( (pRxMsg->DataSize - (HeaderSize + 2)) < 4 )
									{
										memset ( &pRxMsg->Data[pRxMsg->DataSize],
										         0x00,
										         4 - (pRxMsg->DataSize - (HeaderSize + 2)) );
									}
									pRxMsg->DataSize = 4 + (HeaderSize + 2);
								}
							}
							break;

							// Five byte PIDs
							case 0x64:
							case 0x66:
							case 0x6A:
							case 0x6B:
							case 0x6C:
							case 0x72:
							case 0x73:
							case 0x74:
							case 0x77:
							case 0x86:
							case 0x87:
							case 0x91:
							{
								ExpectedDataSize = 5;
							}
							break;

							// Six byte PIDs
							case 0x71:
							case 0x9A:
							{
								ExpectedDataSize = 6;
							}
							break;

							// Seven byte PIDs
							case 0x68:
							case 0x69:
							case 0x75:
							case 0x76:
							case 0x7A:
							case 0x7B:
							case 0x8B:
							case 0x8F:
							{
								ExpectedDataSize = 7;
							}
							break;

							// Nine byte PIDs
							case 0x6E:
							case 0x78:
							case 0x79:
							case 0x7C:
							case 0x83:
							case 0x98:
							case 0x99:
							case 0x9F:
							case 0xA1:
							case 0xA3:
							{
								ExpectedDataSize = 9;
							}
							break;

							// Ten byte PIDs
							case 0x70:
							case 0x85:
							{
								ExpectedDataSize = 10;
							}
							break;

							// Eleven byte PIDs
							case 0x6D:
							{
								ExpectedDataSize = 11;
							}
							break;

							// Twelve byte PIDs
							case 0x94:
							{
								ExpectedDataSize = 12;
							}
							break;

							// Thirteen byte PIDs
							case 0x7F:
							case 0x88:
							{
								ExpectedDataSize = 13;
							}
							break;

							// Seventeen byte PIDs
							case 0x8C:
							case 0x9C:
							{
								ExpectedDataSize = 17;
							}
							break;

							// Forty-One byte PIDs
							case 0x81:
							case 0x82:
							case 0x89:
							case 0x8A:
							{
								ExpectedDataSize = 41;
							}
							break;

							// 1 or 2 byte PIDs
							case 0x06:      // PID $06 1 / 2-Byte definition
							case 0x07:      // PID $07 1 / 2-Byte definition
							case 0x08:      // PID $08 1 / 2-Byte definition
							case 0x09:      // PID $09 1 / 2-Byte definition
							case 0x55:      // PID $55 1 / 2-Byte definition
							case 0x56:      // PID $56 1 / 2-Byte definition
							case 0x57:      // PID $57 1 / 2-Byte definition
							case 0x58:      // PID $58 1 / 2-Byte definition
							{
								// Only check if successfully determined the (variable) PID size
								if ( gVariablePidSize > 0 )
								{
									ExpectedDataSize = gVariablePidSize;
								}
								else
								{
									bCheckDataSize = FALSE;
								}
							}
							break;

							default:
							{
								// Non-OBD PID
								bCheckDataSize = FALSE;
							}
							break;
						}  // end switch ( pRxMsg->Data[HeaderSize + 2] )

						// Check for correct number of data bytes
						if ( bCheckDataSize == TRUE &&
						    (pRxMsg->DataSize - (HeaderSize + 2)) != ExpectedDataSize )
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  SID $01 PID $%02X  has %d data bytes (should be %d)\n",
							      GetEcuId ( EcuIdx ),
							      pRxMsg->Data[Data0Offset],
							      (pRxMsg->DataSize - (HeaderSize + 2)),
							      ExpectedDataSize );
							return FAIL;
						}
					}  // end if ( pstReqMsg->NumIds == 1 )

					// Determine if there is enough room in the buffer to store the data
					if ( (pRxMsg->DataSize - HeaderSize - 1) > sizeof ( gstResponse[EcuIdx].Pid ) )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  SID $01 response data exceeded buffer size\n",
						      GetEcuId ( EcuIdx ) );
						return FAIL;
					}

					// Save the data in the buffer and set the new size
					memcpy ( &gstResponse[EcuIdx].Pid[0],
					         &pRxMsg->Data[Data0Offset],
					         (pRxMsg->DataSize - HeaderSize - 1) );
					gstResponse[EcuIdx].PidSize = (unsigned short)(pRxMsg->DataSize - HeaderSize - 1);
				}
				break;
			}
		}
		break;


		// SID 2 (Mode 2)
		case 0x42:
		{
			// indicate that this ECU has responded
			gstResponse[EcuIdx].bResponseReceived = TRUE;

			// Process message based on PID number
			switch ( pRxMsg->Data[Data0Offset] )
			{
				case 0x00:
				case 0x20:
				case 0x40:
				case 0x60:
				case 0x80:
				case 0xA0:
				case 0xC0:
				case 0xE0:
				{
					// Make sure there is enough data in the message to process
					if ( pRxMsg->DataSize < (Data0Offset + sizeof ( ID_SUPPORT )) )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  Not enough data in SID $02 support response message to process\n",
						      GetEcuId ( EcuIdx ) );
						return FAIL;
					}

					// Save the data in the buffer and set the new size
					for ( Index = Data0Offset;
					      Index < pRxMsg->DataSize;
					      Index += sizeof ( FF_SUPPORT ) )
					{
						// find the element number (for the array of structures)
						ElementOffset = (BYTE)(pRxMsg->Data[Index]) >> 5;

						// move the data into the element
						memcpy ( &gstResponse[EcuIdx].FFSupport[ElementOffset],
						         &pRxMsg->Data[Index],
						         sizeof ( FF_SUPPORT ) );

						// increase the element count if the element in the array was previously unused
						if ( (ElementOffset + 1) > gstResponse[EcuIdx].FFSupportSize )
						{
							gstResponse[EcuIdx].FFSupportSize = ElementOffset + 1;
						}
					}
				}
				//  00-E0 FALL THROUGH AND TREAT LIKE OTHER PIDS

				default:
				{
					// Check if this is a response to an unsupported PID
					if ( gbIgnoreUnsupported == FALSE &&
					     IsIDSupported ( EcuIdx, FFREQUEST, pRxMsg->Data[Data0Offset] ) == FALSE )
					{
						Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  Unsupported SID $02 PID $%02X detected\n",
						      GetEcuId ( EcuIdx ),
						      pRxMsg->Data[Data0Offset] );
					}

					// Make sure there is enough data in the message to process
					if ( pRxMsg->DataSize <= (unsigned long)(Data0Offset) )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  Not enough data in SID $02 response message to process\n",
						      GetEcuId ( EcuIdx ) );
						return FAIL;
					}

					// Determine if there is enough room in the buffer to store the data
					if ( (pRxMsg->DataSize - HeaderSize - 1) > sizeof ( gstResponse[EcuIdx].FF ) )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  SID $02 response data exceeded buffer size\n",
						      GetEcuId ( EcuIdx ) );
						return FAIL;
					}

					// Save the data in the buffer and set the new size
					memcpy ( &gstResponse[EcuIdx].FF[0],
					         &pRxMsg->Data[Data0Offset],
					         (pRxMsg->DataSize - HeaderSize - 1) );
					gstResponse[EcuIdx].FFSize = (unsigned short)(pRxMsg->DataSize - HeaderSize - 1);
				}
				break;
			}
		}
		break;


		// SID 3 (Mode 3)
		case 0x43:
		{
			gstResponse[EcuIdx].bConfDTCSupported = TRUE;

			if ( gstProtocolList[gProtocolIdx].eProtocol != ISO15765 )
			{
				if ( IsMessageUnique ( &gstResponse[EcuIdx].ConfDTC[0],
				                       gstResponse[EcuIdx].ConfDTCSize,
				                       &pRxMsg->Data[Data0Offset],
				                       (unsigned short)(pRxMsg->DataSize - HeaderSize - 1) ) == FAIL )
				{
					Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X  has already responded (SID $3)\n",
					      GetEcuId ( EcuIdx ) );
					(*pNumResponses)--;  // don't count the response
				}
				else
				{
					// Determine if there is enough room in the buffer to store the data
					if ( (pRxMsg->DataSize - HeaderSize - 1) > (sizeof ( gstResponse[EcuIdx].ConfDTC ) - gstResponse[EcuIdx].ConfDTCSize) )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  SID $03 response data exceeded buffer size\n",
						      GetEcuId ( EcuIdx ) );
						return FAIL;
					}

					// Just copy the DTC bytes
					memcpy ( &gstResponse[EcuIdx].ConfDTC[gstResponse[EcuIdx].ConfDTCSize],
					         &pRxMsg->Data[Data0Offset],
					         pRxMsg->DataSize - HeaderSize - 1 );
					gstResponse[EcuIdx].ConfDTCSize += (unsigned short)(pRxMsg->DataSize - HeaderSize - 1);
				}
			}
			else
			{
				// indicate that this ECU has responded
				gstResponse[EcuIdx].bResponseReceived = TRUE;

				// Determine if there is enough room in the buffer to store the data
				if ( (pRxMsg->DataSize - HeaderSize - 1) > sizeof ( gstResponse[EcuIdx].ConfDTC ) )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X  SID $03 response data exceeded buffer size\n",
					      GetEcuId ( EcuIdx ) );
					return FAIL;
				}

				// If ISO15765, skip the number of DTCs byte
				if ( pRxMsg->DataSize <= (unsigned long)(HeaderSize + 2) ||
				     pRxMsg->Data[Data0Offset] == 0 )
				{
					// If no DTCs, set to zero
					memset ( &gstResponse[EcuIdx].ConfDTC[0],
					         0,
					         2 );
					gstResponse[EcuIdx].ConfDTCSize = 2;
				}
				else
				{
					// Otherwise copy the DTC data
					memcpy ( &gstResponse[EcuIdx].ConfDTC[0],
					         &pRxMsg->Data[HeaderSize + 2],
					         (pRxMsg->DataSize - HeaderSize - 2) );
					gstResponse[EcuIdx].ConfDTCSize = (unsigned short)(pRxMsg->DataSize - HeaderSize - 2);
				}

				// verify that reported number of DTCs matches that of actual DTC count.
				if ( pRxMsg->Data[Data0Offset] != ( (pRxMsg->DataSize - HeaderSize - 2) / 2 ) )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X  SID $03 DTC Count must match the # of DTC's reported.\n",
					      GetEcuId ( EcuIdx ) );
					return FAIL;
				}
			}
		}
		break;


		// SID 4 (Mode 4)
		case 0x44:
		{
			// indicate that this ECU has responded
			gstResponse[EcuIdx].bResponseReceived = TRUE;

			// Indicate positive response
			gstResponse[EcuIdx].ClrDTC[0]  = 0x44;
			gstResponse[EcuIdx].ClrDTCSize = 1;
		}
		break;


		// SID 5 (Mode 5)
		case 0x45:
		{
			// indicate that this ECU has responded
			gstResponse[EcuIdx].bResponseReceived = TRUE;

			// Indicate positive response
			gstResponse[EcuIdx].O2Tid[0]  = 0x45;
			gstResponse[EcuIdx].O2TidSize = 1;
		}
		break;


		// SID 6 (Mode 6)
		case 0x46:
		{
			if ( gstProtocolList[gProtocolIdx].eProtocol == ISO15765 )
			{
				// indicate that this ECU has responded
				gstResponse[EcuIdx].bResponseReceived = TRUE;
			}

			// Process message based on PID number
			switch ( pRxMsg->Data[Data0Offset] )
			{
				case 0x00:
				case 0x20:
				case 0x40:
				case 0x60:
				case 0x80:
				case 0xA0:
				case 0xC0:
				case 0xE0:
				{
					// Make sure there is enough data in the message to process
					if ( pRxMsg->DataSize < (Data0Offset + sizeof ( ID_SUPPORT )) )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  Not enough data in SID $06 support response message to process\n",
						      GetEcuId ( EcuIdx ) );
						return FAIL;
					}

					// If not ISO15765 protocol, get rid of 0xFF to make data look the same
					if ( gstProtocolList[gProtocolIdx].eProtocol != ISO15765 )
					{
						for ( ByteIdx = (HeaderSize + 2);
						      ByteIdx < (pRxMsg->DataSize - 1);
						      ByteIdx++ )
						{
							pRxMsg->Data[ByteIdx] = pRxMsg->Data[ByteIdx + 1];
						}
						pRxMsg->DataSize -= 2;
					}

					// Save the data in the buffer and set the new size
					for ( Index = Data0Offset;
					      Index < pRxMsg->DataSize;
					      Index += sizeof ( ID_SUPPORT ) )
					{
						// find the element number (for the array of structures)
						ElementOffset = (BYTE)(pRxMsg->Data[Index]) >> 5;

						// move the data into the element
						memcpy ( &gstResponse[EcuIdx].MidSupport[ElementOffset],
						         &pRxMsg->Data[Index],
						         sizeof ( ID_SUPPORT ) );

						// increase the element count if the element in the array was previously unused
						if ( (ElementOffset + 1) > gstResponse[EcuIdx].MidSupportSize )
						{
							gstResponse[EcuIdx].MidSupportSize = (ElementOffset + 1);
						}
					}
				}
				//  00-E0 FALL THROUGH AND TREAT LIKE OTHER PIDS

				default:
				{
					// Check if this is a response to an unsupported MID
					if ( IsIDSupported ( EcuIdx, MIDREQUEST, pRxMsg->Data[Data0Offset] ) == FALSE )
					{
						Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  Unsupported SID $06 MID $%02X detected\n",
						      GetEcuId ( EcuIdx ),
						      pRxMsg->Data[Data0Offset] );
					}

					// Make sure there is enough data in the message to process
					if ( pRxMsg->DataSize <= (unsigned long)(Data0Offset) )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  Not enough data in SID $06 response message to process\n",
						      GetEcuId ( EcuIdx ) );
						return FAIL;
					}

					// Save the data in the buffer and set the new size
					if ( gstProtocolList[gProtocolIdx].eProtocol == ISO15765 )
					{
						// Determine if there is enough room in the buffer to store the data
						if ( (pRxMsg->DataSize - HeaderSize - 1) > sizeof ( gstResponse[EcuIdx].Mid ) )
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  SID $06 response data exceeded buffer size\n",
							      GetEcuId ( EcuIdx ) );
							return FAIL;
						}

						memcpy ( &gstResponse[EcuIdx].Mid[0],
						         &pRxMsg->Data[Data0Offset],
						         (pRxMsg->DataSize - HeaderSize - 1) );
						gstResponse[EcuIdx].MidSize = (unsigned short)(pRxMsg->DataSize - HeaderSize - 1);
					}
					else
					{
						// If not ISO15765 protocol, make the data look like it
						// Check if it is a max or min limit
						if ( pRxMsg->Data[HeaderSize + 2] & 0x80 )
						{
							// Move the limit to the minimum location
							pRxMsg->Data[HeaderSize + 7] = pRxMsg->Data[HeaderSize + 6];
							pRxMsg->Data[HeaderSize + 6] = pRxMsg->Data[HeaderSize + 5];

							// Set the maximum to 0xFFFF
							pRxMsg->Data[HeaderSize + 9] = 0xFF;
							pRxMsg->Data[HeaderSize + 8] = 0xFF;
						}
						else
						{
							// Move the limit to the maximum location
							pRxMsg->Data[HeaderSize + 9] = pRxMsg->Data[HeaderSize + 6];
							pRxMsg->Data[HeaderSize + 8] = pRxMsg->Data[HeaderSize + 5];

							// Set the minimum to 0x0000
							pRxMsg->Data[HeaderSize + 7] = 0x00;
							pRxMsg->Data[HeaderSize + 6] = 0x00;
						}

						// Move the value to the new location
						pRxMsg->Data[HeaderSize + 5] = pRxMsg->Data[HeaderSize + 4];
						pRxMsg->Data[HeaderSize + 4] = pRxMsg->Data[HeaderSize + 3];

						// Set the unit and scaling ID to zero
						pRxMsg->Data[HeaderSize + 3] = 0x00;

						// Adjust the message size
						pRxMsg->DataSize += 3;

						if ( IsMessageUnique ( &gstResponse[EcuIdx].Mid[0],
						                       gstResponse[EcuIdx].MidSize,
						                       &pRxMsg->Data[Data0Offset],
						                       (unsigned short)(pRxMsg->DataSize - HeaderSize - 1) ) == FAIL )
						{
							Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  has already responded (SID $6)\n",
							      GetEcuId ( EcuIdx ) );
							(*pNumResponses)--;  // don't count the response
						}
						else
						{
							// Determine if there is enough room in the buffer to store the data
							if ( (pRxMsg->DataSize - HeaderSize - 1) > (sizeof ( gstResponse[EcuIdx].Mid) - gstResponse[EcuIdx].MidSize ) )
							{
								Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
								      "ECU %X  SID $06 response data exceeded buffer size\n",
								      GetEcuId ( EcuIdx ) );
								return FAIL;
							}

							memcpy ( &gstResponse[EcuIdx].Mid[gstResponse[EcuIdx].MidSize],
							         &pRxMsg->Data[Data0Offset],
							         (pRxMsg->DataSize - HeaderSize - 1) );
							gstResponse[EcuIdx].MidSize += (unsigned short)(pRxMsg->DataSize - HeaderSize - 1);
						}
					}
				}
				break;
			}
		}
		break;


		// SID 7 (Mode 7)
		case 0x47:
		{
			gstResponse[EcuIdx].bPendDTCSupported = TRUE;

			if ( gstProtocolList[gProtocolIdx].eProtocol != ISO15765 )
			{
				if ( IsMessageUnique ( &gstResponse[EcuIdx].PendDTC[0],
				                       gstResponse[EcuIdx].PendDTCSize,
				                       &pRxMsg->Data[Data0Offset],
				                       (unsigned short)(pRxMsg->DataSize - HeaderSize - 1) ) == FAIL )
				{
					Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X  has already responded (SID $7)\n",
					      GetEcuId ( EcuIdx ) );
					(*pNumResponses)--;  // don't count the response
				}
				else
				{
					// Determine if there is enough room in the buffer to store the data
					if ( (pRxMsg->DataSize - HeaderSize - 1) > (sizeof ( gstResponse[EcuIdx].PendDTC ) - gstResponse[EcuIdx].PendDTCSize) )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  SID $07 response data exceeded buffer size\n",
						      GetEcuId ( EcuIdx ) );
						return FAIL;
					}

					// Just copy the DTC bytes
					memcpy ( &gstResponse[EcuIdx].PendDTC[gstResponse[EcuIdx].PendDTCSize],
					         &pRxMsg->Data[Data0Offset],
					         pRxMsg->DataSize - HeaderSize - 1 );
					gstResponse[EcuIdx].PendDTCSize += (unsigned short)(pRxMsg->DataSize - HeaderSize - 1);
				}
			}
			else
			{
				// indicate that this ECU has responded
				gstResponse[EcuIdx].bResponseReceived = TRUE;

				// Determine if there is enough room in the buffer to store the data
				if ( (pRxMsg->DataSize - HeaderSize - 1) > sizeof ( gstResponse[EcuIdx].PendDTC ) )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X  SID $07 response data exceeded buffer size\n",
					      GetEcuId ( EcuIdx ) );
					return FAIL;
				}

				// If ISO15765, skip the number of DTCs byte
				if ( pRxMsg->DataSize <= (unsigned long)(HeaderSize + 2) ||
				     pRxMsg->Data[Data0Offset] == 0 )
				{
					// If no DTCs, set to zero
					memset ( &gstResponse[EcuIdx].PendDTC[0],
					         0,
					         2 );
					gstResponse[EcuIdx].PendDTCSize = 2;
				}
				else
				{
					// Otherwise copy the DTC data
					memcpy ( &gstResponse[EcuIdx].PendDTC[0],
					         &pRxMsg->Data[HeaderSize + 2],
					         (pRxMsg->DataSize - HeaderSize - 2) );
					gstResponse[EcuIdx].PendDTCSize = (unsigned short)(pRxMsg->DataSize - HeaderSize - 2);
				}

				// verify that reported number of DTCs matches that of actual DTC count.
				if ( pRxMsg->Data[Data0Offset] != ( (pRxMsg->DataSize - HeaderSize - 2) / 2 ) )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X  SID $07 DTC Count must match the # of DTC's reported.\n",
					      GetEcuId ( EcuIdx ) );
					return FAIL;
				}
			}  // end else ISO15765
		}
		break;


		// SID 8 (Mode 8)
		case 0x48:
		{
			// indicate that this ECU has responded
			gstResponse[EcuIdx].bResponseReceived = TRUE;

			// Process message based on PID number
			switch ( pRxMsg->Data[Data0Offset] )
			{
				case 0x00:
				case 0x20:
				case 0x40:
				case 0x60:
				case 0x80:
				case 0xA0:
				case 0xC0:
				case 0xE0:
				{
					// Make sure there is enough data in the message to process
					if ( pRxMsg->DataSize < (Data0Offset + sizeof ( ID_SUPPORT )) )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  Not enough data in SID $08 support response message to process\n",
						      GetEcuId ( EcuIdx ) );
						return FAIL;
					}

					// Save the data in the buffer and set the new size
					for ( Index = Data0Offset;
					      Index < pRxMsg->DataSize;
					      Index += sizeof ( ID_SUPPORT ) )
					{
						// find the element number (for the array of structures)
						ElementOffset = (BYTE)(pRxMsg->Data[Index]) >> 5;

						// move the data into the element
						memcpy ( &gstResponse[EcuIdx].TidSupport[ElementOffset],
						         &pRxMsg->Data[Index],
						         sizeof ( ID_SUPPORT ) );

						// increase the element count if the element in the array was previously unused
						if ( (ElementOffset + 1) > gstResponse[EcuIdx].TidSupportSize )
						{
							gstResponse[EcuIdx].TidSupportSize = ElementOffset + 1;
						}
					}
				}
				//  00-E0 FALL THROUGH AND TREAT LIKE OTHER PIDS

				default:
				{
					// Check if this is a response to an unsupported TID
					if ( IsIDSupported ( EcuIdx, TIDREQUEST, pRxMsg->Data[Data0Offset] ) == FALSE )
					{
						Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  Unsupported SID $08 TID $%02X detected\n",
						      GetEcuId ( EcuIdx ),
						      pRxMsg->Data[Data0Offset] );
					}

					// Make sure there is enough data in the message to process
					if ( pRxMsg->DataSize <= (unsigned long)(HeaderSize + 2) )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  Not enough data in SID $08 response message to process\n",
						      GetEcuId ( EcuIdx ) );
						return FAIL;
					}

					if ( gstProtocolList[gProtocolIdx].eProtocol == ISO15765 )
					{
						// Determine if there is enough room in the buffer to store the data
						if ( (pRxMsg->DataSize - HeaderSize - 1) > sizeof ( gstResponse[EcuIdx].Tid ) )
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  SID $08 response data exceeded buffer size\n",
							      GetEcuId ( EcuIdx ) );
							return FAIL;
						}

						memcpy ( &gstResponse[EcuIdx].Tid[0],
						         &pRxMsg->Data[Data0Offset],
						         (pRxMsg->DataSize - (Data0Offset)) );
						gstResponse[EcuIdx].TidSize = (unsigned short)(pRxMsg->DataSize - (Data0Offset));
					}
					else
					{
						// Determine if there is enough room in the buffer to store the data
						if ( (pRxMsg->DataSize - HeaderSize - 1) > (sizeof ( gstResponse[EcuIdx].Tid ) - gstResponse[EcuIdx].TidSize) )
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  SID $08 response data exceeded buffer size\n",
							      GetEcuId ( EcuIdx ) );
							return FAIL;
						}

						memcpy ( &gstResponse[EcuIdx].Tid[gstResponse[EcuIdx].TidSize],
						         &pRxMsg->Data[Data0Offset],
						         (pRxMsg->DataSize - (Data0Offset)) );
						gstResponse[EcuIdx].TidSize += (unsigned short)(pRxMsg->DataSize - (Data0Offset));
					}
				}
				break;
			}  // end SID 8 switch ( pRxMsg->Data[Data0Offset] )
		}  // end case 0x48
		break;


		// SID 9 (Mode 9)
		case 0x49:
		{
			if ( gstProtocolList[gProtocolIdx].eProtocol == ISO15765 )
			{
				// indicate that this ECU has responded
				gstResponse[EcuIdx].bResponseReceived = TRUE;
			}

			// Process message based on PID number
			switch ( pRxMsg->Data[Data0Offset] )
			{
				case 0x00:
				case 0x20:
				case 0x40:
				case 0x60:
				case 0x80:
				case 0xA0:
				case 0xC0:
				case 0xE0:
				{
					if ( gstProtocolList[gProtocolIdx].eProtocol != ISO15765 )
					{
						// Make sure there is enough data in the message to process
						if ( pRxMsg->DataSize < (HeaderSize + sizeof ( INF )) )
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  Not enough data in SID $09 support response message to process.\n",
							      GetEcuId ( EcuIdx ) );
							return FAIL;
						}

						// Save the data in the buffer and set the new size

						// find the element number (for the array of structures)
						ElementOffset = (BYTE)(pRxMsg->Data[Data0Offset]) >> 5;

						// move the INF into the element
						gstResponse[EcuIdx].InfSupport[ElementOffset].ID = pRxMsg->Data[Data0Offset];
						// move the data into the element ... skipping the message count
						memcpy ( &gstResponse[EcuIdx].InfSupport[ElementOffset].SupBits[0],
						         &pRxMsg->Data[HeaderSize + 3],
						         4 );

						// increase the element count if the element in the array was previously unused
						if ( (ElementOffset + 1) > gstResponse[EcuIdx].InfSupportSize )
						{
							gstResponse[EcuIdx].InfSupportSize = ElementOffset + 1;
						}
					}
					else
					{
						Index = Data0Offset;

						// Make sure there is enough data in the message to process
						if ( pRxMsg->DataSize < (Index + sizeof ( ID_SUPPORT )) )
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  Not enough data in SID $09 support response message to process.\n",
							      GetEcuId ( EcuIdx ) );
							return FAIL;
						}

						// Save the data in the buffer and set the new size
						for ( ;
						      Index < pRxMsg->DataSize;
						      Index += sizeof ( ID_SUPPORT ) )
						{
							// find the element number (for the array of structures)
							ElementOffset = (BYTE)(pRxMsg->Data[Index]) >> 5;

							// move the data into the element
							memcpy ( &gstResponse[EcuIdx].InfSupport[ElementOffset],
							         &pRxMsg->Data[Index],
							         sizeof ( ID_SUPPORT ) );

							// increase the element count if the element in the array was previously unused
							if ( (ElementOffset + 1) > gstResponse[EcuIdx].InfSupportSize )
							{
								gstResponse[EcuIdx].InfSupportSize = ElementOffset + 1;
							}
						}
					}
				}
				// 00-E0 FALL THROUGH AND TREAT LIKE OTHER PIDS

				default:
				{
					// Check if this is a response to an unsupported INF
					if ( IsIDSupported ( EcuIdx,INFREQUEST, pRxMsg->Data[Data0Offset] ) == FALSE )
					{
						Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  Unsupported INF $%02X detected\n",
						      GetEcuId ( EcuIdx ),
						      pRxMsg->Data[Data0Offset] );
					}

					// Make sure there is enough data in the message to process
					if ( pRxMsg->DataSize <= (unsigned long)(HeaderSize + 2) )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  Not enough data in SID $09 response message to process\n",
						      GetEcuId ( EcuIdx ) );
						return FAIL;
					}

					// Save the data in the buffer and set the new size
					if ( gstProtocolList[gProtocolIdx].eProtocol == ISO15765 )
					{
						// Determine if there is enough room in the buffer to store the data
						if ( (pRxMsg->DataSize - HeaderSize - 1) > sizeof ( gstResponse[EcuIdx].Inf ) )
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  SID $09 response data exceeded buffer size\n",
							      GetEcuId ( EcuIdx ) );
							return FAIL;
						}

						memcpy ( &gstResponse[EcuIdx].Inf[0],
						         &pRxMsg->Data[Data0Offset],
						         (pRxMsg->DataSize - (Data0Offset)) );
						gstResponse[EcuIdx].InfSize = (unsigned short)(pRxMsg->DataSize - (Data0Offset));

						// Verify CVNs contain 4 HEX bytes
						if ( pRxMsg->Data[HeaderSize+1] == INF_TYPE_CVN )
						{
							// HeaderSize is 4; 0x49 0x06 0x(CVN Count) 0x(DATA) ...
							if ( ((pRxMsg->DataSize - (HeaderSize + 3)) % 4) != 0 )
							{
								Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
								      "ECU %X  INF $6, All CVNs must contain 4 HEX bytes.\n",
								      GetEcuId ( EcuIdx ) );
							}
						}
						// Verify CALIDs contain 16 HEX bytes
						else if ( pRxMsg->Data[HeaderSize+1] == INF_TYPE_CALID )
						{
							// HeaderSize is 4; 0x49 0x04 0x(CALID Count) 0x(DATA) ...
							if ( ((pRxMsg->DataSize - (HeaderSize + 3)) % 16) != 0 )
							{
								Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
								      "ECU %X  INF $4, All CALIDs must contain 16 HEX bytes.\n",
								      GetEcuId ( EcuIdx ) );
							}
						}
					}
					else
					{
						if ( IsMessageUnique ( &gstResponse[EcuIdx].Inf[0],
						                       gstResponse[EcuIdx].InfSize,
						                       &pRxMsg->Data[Data0Offset],
						                       (unsigned short)(pRxMsg->DataSize - HeaderSize - 1) ) == FAIL )
						{
							Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  has already responded (SID $9)\n",
							      GetEcuId ( EcuIdx ) );
							(*pNumResponses)--;  // don't count the response
						}
						else
						{
							// Determine if there is enough room in the buffer to store the data
							if ( (pRxMsg->DataSize - HeaderSize - 1) > (sizeof ( gstResponse[EcuIdx].Inf ) - gstResponse[EcuIdx].InfSize) )
							{
								Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
								      "ECU %X  SID $09 response data exceeded buffer size\n",
								      GetEcuId ( EcuIdx ) );
								return FAIL;
							}

							memcpy ( &gstResponse[EcuIdx].Inf[gstResponse[EcuIdx].InfSize],
							         &pRxMsg->Data[Data0Offset],
							         sizeof ( INF ) );
							gstResponse[EcuIdx].InfSize += sizeof ( INF );

							// Verify CALIDs and CVNs contain 4 HEX bytes
							if ( ( pRxMsg->Data[HeaderSize+1] == INF_TYPE_CVN ||
							       pRxMsg->Data[HeaderSize+1] == INF_TYPE_CALID ) &&
							     pRxMsg->DataSize   != 0x0A )
							{
								Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
								      "ECU %X  INF $%X response must contain 4 bytes of HEX data!\n",
								      GetEcuId ( EcuIdx ),
								      pRxMsg->Data[HeaderSize+1] );
							}
						}
					}
				}
				break;
			}
		}
		break;


		// SID A (Mode A)
		case 0x4A:
		{
			/*
			 * THE SOFTWARE ASSUMES THAT SID $0A IS ONLY SUPPORTED ON ISO 15765
			 */

			gstResponse[EcuIdx].bPermDTCSupported = TRUE;

			// indicate that this ECU has responded
			gstResponse[EcuIdx].bResponseReceived = TRUE;

			// Determine if there is enough room in the buffer to store the data
			if ( (pRxMsg->DataSize - HeaderSize - 1) > sizeof ( gstResponse[EcuIdx].PermDTC ) )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  SID $0A response data exceeded buffer size\n",
				     GetEcuId ( EcuIdx ) );
				return FAIL;
			}

			// If ISO15765, skip the number of DTCs byte
			if ( pRxMsg->DataSize <= (unsigned long)(HeaderSize + 2) ||
			     pRxMsg->Data[Data0Offset] == 0 )
			{
				// If no DTCs, set to zero
				memset ( &gstResponse[EcuIdx].PermDTC[0],
				         0,
				         2 );
				gstResponse[EcuIdx].PendDTCSize = 2;
			}
			else
			{
				// Otherwise copy the DTC data
				memcpy ( &gstResponse[EcuIdx].PermDTC[0],
				         &pRxMsg->Data[HeaderSize + 2],
				         (pRxMsg->DataSize - HeaderSize - 2) );
				gstResponse[EcuIdx].PermDTCSize = (unsigned short)(pRxMsg->DataSize - HeaderSize - 2);
			}

			// Verify that reported number of DTCs matches that of actual DTC count.
			if ( pRxMsg->Data[Data0Offset] != ( (pRxMsg->DataSize - HeaderSize - 2) / 2 ) )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  SID $0A DTC Count must match the # of DTC's reported.\n",
				      GetEcuId ( EcuIdx ) );
				return FAIL;
			}
		}
		break;

		default:
		{
			// indicate that this ECU has responded
			gstResponse[EcuIdx].bResponseReceived = TRUE;

			// Check for a negative response code
			if ( pRxMsg->Data[HeaderSize] == NAK )
			{
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  Received negative response code\n",
				      GetEcuId ( EcuIdx ));
				if ( pRxMsg->Data[HeaderSize + 2] == NAK_RESPONSE_PENDING )
				{
					(*pNumResponses)--;  // don't count the 0x78 response
				}
			}
			else
			{
				// Unexpected SID response
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  Unexpected SID $%02X response\n",
				      GetEcuId ( EcuIdx ),
				      (pRxMsg->Data[HeaderSize] - OBD_RESPONSE_BIT) );
				return FAIL;
			}
		}

	}  // end switch ( pRxMsg->Data[HeaderSize] ) SID

	return PASS;
}


/*******************************************************************************
**
**  Function:  ChkIFRAdjust
**
**  Purpose:   Account for checksum or IFR in data size.
**
*******************************************************************************/
void ChkIFRAdjust ( PASSTHRU_MSG *pRxMsg )
{
	// If extra data was returned (e.g. checksum, IFR, ...), remove it
	if ( pRxMsg->ExtraDataIndex != 0 &&
	     pRxMsg->DataSize != pRxMsg->ExtraDataIndex )
	{
		// Adjust the data size to ignore the extra data
		pRxMsg->DataSize = pRxMsg->ExtraDataIndex;
	}
}


/*******************************************************************************
**
**  Function:  LookupEcuIndex
**
**  Purpose:   Return index into gstResponse struct for this message
**
*******************************************************************************/
STATUS LookupEcuIndex ( PASSTHRU_MSG *pRxMsg,
                        BYTE         *pEcuIdx )
{
	BYTE           HeaderSize;
	BYTE           EcuIdx;
	unsigned long  ByteIdx;
	unsigned long  EcuId = 0;

	BOOL           bTestFailed = FALSE;


	// Set the response header size based on the protocol
	HeaderSize = gstProtocolList[gProtocolIdx].HeaderSize;

	// Find the appropriate EcuIdx based on the header information
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		// Check if we have a header match
		for ( ByteIdx = 0;
		      ByteIdx < HeaderSize;
		      ByteIdx++ )
		{
			if ( pRxMsg->Data[ByteIdx] != gstResponse[EcuIdx].Header[ByteIdx] )
			{
				//  If ISO14230 protocol, ignore the length bits in the first byte of header
				if ( gstProtocolList[gProtocolIdx].eProtocol == ISO14230 &&
				     ByteIdx == 0 )
				{
					if ( (pRxMsg->Data[ByteIdx] & 0xC0) != (gstResponse[EcuIdx].Header[ByteIdx] & 0xC0) )
					{
						break;  // leave byte for loop
					}
				}
				else
				{
					break;  // leave byte for loop
				}
			}
		}

		// If no match, check if EcuIdx is empty
		if ( ByteIdx != HeaderSize )
		{
			if ( gstResponse[EcuIdx].Header[0] == 0x00 &&
			     gstResponse[EcuIdx].Header[1] == 0x00 &&
			     gstResponse[EcuIdx].Header[2] == 0x00 &&
			     gstResponse[EcuIdx].Header[3] == 0x00 )
			{
				// if not currently in the process of determining the protocol
				// then check every Rx msg's ID
				if ( gbProtocolDetermined == TRUE )
				{
					if ( VerifyEcuId ( &pRxMsg->Data[0] ) == FAIL )
					{
						// create the ECU ID for later use
						for ( ByteIdx = 0;
						      ByteIdx < HeaderSize;
						      ByteIdx++ )
						{
							EcuId = (EcuId << 8) + (pRxMsg->Data[ByteIdx]);
						}

						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "Response from ECU %X not in initial list\n",
						      EcuId );
						bTestFailed = TRUE;
						break;  // leave ECU for loop
					}
				}

				// If empty, add the new response
				memcpy ( &gstResponse[EcuIdx].Header[0],
				         &pRxMsg->Data[0],
				         HeaderSize );
				break;  // leave ECU for loop
			}
		}
		else
		{
			// We have a header match
			break;  // leave ECU for loop
		}
	}

	*pEcuIdx = EcuIdx;

	if ( (EcuIdx >= OBD_LEGACY_MAX_ECUS &&
	      gstProtocolList[gProtocolIdx].eProtocol != ISO15765_29_BIT_I &&
	      gstProtocolList[gProtocolIdx].eProtocol != ISO15765_250K_29_BIT_I) ||
	     (EcuIdx >= MAX_ECUS &&
	      (gstProtocolList[gProtocolIdx].eProtocol == ISO15765_29_BIT_I ||
	       gstProtocolList[gProtocolIdx].eProtocol == ISO15765_250K_29_BIT_I)) )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Too many OBD ECU responses\n" );
		bTestFailed = TRUE;
	}


	if ( bTestFailed == TRUE )
	{
		return FAIL;
	}

	return PASS;
}


/*******************************************************************************
**
**  Function:  IsMessageUnique
**
**  Purpose:   Check to see if this is a duplicate message
**
**  RETURNS    PASS - message is unique
**             FAIL - message is a duplicate
**
*******************************************************************************/
STATUS IsMessageUnique ( BYTE          *pBuffer,     // pointer to buffer
                         unsigned short BufSize,     // size of buffer
                         BYTE          *pMsg,        // pointer to message to be checked
                         unsigned short MsgSize )    // size of message
{
	unsigned short i;
	unsigned short x;
	BOOL bDone = FALSE;


	for ( i = 0, x = 0;
	      i < BufSize && x < MsgSize;
	      i += MsgSize )
	{
		x = 0;

		while ( pBuffer[i + x] == pMsg[x] &&
		        x < MsgSize &&
		        (i + x) < BufSize )
		{
			x++;
		}

		if ( x == MsgSize )
		{
			bDone = TRUE;
		}
	}

	if ( bDone == TRUE )
	{
		return FAIL;
	}

	return PASS;
}
