//=============================================================================
//  FILE: VoiceParser.cpp
//
//  Description: DirectPlay Service Provider Parser
//
//
//  Modification History:
//
//  Michael Milirud      08/Aug/2000    Created
//=============================================================================


//==================//
// Standard headers //
//==================//
#include <string>
#include <winsock2.h>
#include <wsipx.h>

// DVoice.h, included by DVProt.h, will _define_ the Compression Type GUIDs.
#include <initguid.h>


//=====================//
// Proprietary headers //
//=====================//

// Prototypes
#include "VoiceParser.hpp"

// Voice protocol header
#include "DVoice.h"
#include "DVProt.h"


namespace
{
	HPROTOCOL  g_hVoiceProtocol;

	
	//====================//
	// Message Type field //---------------------------------------------------------------------------------------------
	//====================//
	LABELED_BYTE g_arr_MessageTypeByteLabels[] = { { DVMSGID_CONNECTREQUEST,	  "Establishing connection"						   },
												   { DVMSGID_CONNECTREFUSE,		  "Connection request rejected"					   },
										           { DVMSGID_CONNECTACCEPT,		  "Connection request granted"					   },
										           { DVMSGID_SETTINGSCONFIRM,     "Confirming support for the connection settings" },
										           { DVMSGID_PLAYERLIST,		  "List of players in the session"				   },
										           { DVMSGID_SPEECH,			  "Audio data"									   },
										           { DVMSGID_SPEECHWITHTARGET,	  "Targeted audio data"							   },
										           { DVMSGID_SPEECHWITHFROM,	  "Proxied audio data"							   },
										           { DVMSGID_SETTARGETS,		  "Setting client's target"			 		       },
										           { DVMSGID_CREATEVOICEPLAYER,	  "New player joined the session"			       },
										           { DVMSGID_DELETEVOICEPLAYER,	  "Player left the session"						   },
										           { DVMSGID_SESSIONLOST,		  "Session is lost"								   },
										           { DVMSGID_DISCONNECTCONFIRM,	  "Disconnection notification acknowledged"	       },
										           { DVMSGID_DISCONNECT,		  "Disconnecting"								   },
												   { DVMSGID_PLAYERLIST,		  "Players list"								   } };

	SET g_LabeledMessageTypeByteSet = { sizeof(g_arr_MessageTypeByteLabels) / sizeof(LABELED_BYTE), g_arr_MessageTypeByteLabels };



	//===================//
	// Result Code field //----------------------------------------------------------------------------------------------
	//===================//
	LABELED_DWORD g_arr_ResultCodeDWordLabels[] = { { DVERR_BUFFERTOOSMALL,				"Buffer is too small"										},
												    { DVERR_EXCEPTION,					"Exception was thrown"										},
												    { DVERR_GENERIC,					"Generic error"												},
												    { DVERR_INVALIDFLAGS,				"Invalid flags"												},
												    { DVERR_INVALIDOBJECT,				"Invalid object"											},
												    { DVERR_INVALIDPARAM,				"Invalid parameter(s)"										},
												    { DVERR_INVALIDPLAYER,				"Invalid player"											},
												    { DVERR_INVALIDGROUP,				"Invalid group"												},
												    { DVERR_INVALIDHANDLE,				"Invalid handle"											},
												    { DVERR_INVALIDPOINTER,				"Invalid pointer"											},
												    { DVERR_OUTOFMEMORY,				"Out of memory"												},
												    { DVERR_CONNECTABORTING,			"Aborting connection"										},
												    { DVERR_CONNECTIONLOST,				"Connection lost"											},
												    { DVERR_CONNECTABORTED,				"Connection aborted"										},
												    { DVERR_CONNECTED,					"Connected"													},
												    { DVERR_NOTCONNECTED,				"Not connected"												},
												    { DVERR_NOTINITIALIZED,				"Not initialized"											},
												    { DVERR_NOVOICESESSION,				"No voice session"											},
												    { DVERR_NOTALLOWED,					"Not allowed"												},
												    { DVERR_NOTHOSTING,					"Not hosting"												},
												    { DVERR_NOTSUPPORTED,				"Not supported"												},
												    { DVERR_NOINTERFACE,				"No interface"												},
												    { DVERR_NOTBUFFERED,				"Not buffered"												},
												    { DVERR_NOTRANSPORT,				"No transport"												},
												    { DVERR_NOCALLBACK,					"No callback"												},
												    { DVERR_NO3DSOUND,					"No 3D sound"												},
												    { DVERR_NORECVOLAVAILABLE,			"No recording volume available"								},
												    { DVERR_SESSIONLOST,				"Session lost"												},
												    { DVERR_PENDING,					"Pending"													},
												    { DVERR_INVALIDTARGET,				"Invalid target"											},
												    { DVERR_TRANSPORTNOTHOST,			"Transport is not hosting"									},
												    { DVERR_COMPRESSIONNOTSUPPORTED,	"Compression is not supported"								},
												    { DVERR_ALREADYPENDING,				"Already pending"											},
												    { DVERR_SOUNDINITFAILURE,			"Sound initialization failed"								},
												    { DVERR_TIMEOUT,					"Timeout"													},
												    { DVERR_ALREADYBUFFERED,			"Already buffered"											},
												    { DVERR_HOSTING,					"Hosting"													},
												    { DVERR_INVALIDDEVICE,				"Invalid device"											},
												    { DVERR_RECORDSYSTEMERROR,			"Record system error"										},
												    { DVERR_PLAYBACKSYSTEMERROR,		"Playback system error"										},
												    { DVERR_SENDERROR,					"Send error"												},
												    { DVERR_USERCANCEL,					"Cancelled by user"											},
												    { DVERR_RUNSETUP,					"Run setup"													},
												    { DVERR_INCOMPATIBLEVERSION,		"Incompatible version"										},
												    { DVERR_INITIALIZED,				"Initialized"												},
												    { DVERR_TRANSPORTNOTINIT,			"Transport not initialized"									},
												    { DVERR_TRANSPORTNOSESSION,			"Transport is not hosting or connecting"					},
												    { DVERR_TRANSPORTNOPLAYER,			"Legacy DirectPlay local player has not yet been created"	},
												    { DVERR_USERBACK,					"Back button was used improperly in the wizard"				},
												    { DVERR_INVALIDBUFFER,				"Invalid buffer"											},
													{ DV_OK,							"Success"													} };

	SET g_LabeledResultCodeDWordSet = { sizeof(g_arr_ResultCodeDWordLabels) / sizeof(LABELED_DWORD), g_arr_ResultCodeDWordLabels };


	//====================//
	// Session Type field //---------------------------------------------------------------------------------------------
	//====================//
	LABELED_DWORD g_arr_SessionTypeDWordLabels[] = { { DVSESSIONTYPE_PEER,			"Peer to peer"	  },
													 { DVSESSIONTYPE_MIXING,		"Mixing server"	  },
													 { DVSESSIONTYPE_FORWARDING,  "Forwarding server" },
													 { DVSESSIONTYPE_ECHO,			"Loopback"		  } };

	SET g_LabeledSessionTypeDWordSet = { sizeof(g_arr_SessionTypeDWordLabels) / sizeof(LABELED_DWORD), g_arr_SessionTypeDWordLabels };


	//====================//
	// Session Flags field //--------------------------------------------------------------------------------------------
	//====================//
	LABELED_BIT g_arr_SessionFlagsBitLabels[] = { { 1, "Host Migration enabled",	    "No Host Migration"		     },	    // DVSESSION_NOHOSTMIGRATION
												  { 2, "No Server Control Target mode", "Server Control Target mode" } };	// DVSESSION_SERVERCONTROLTARGET

	SET g_LabeledSessionFlagsBitSet = { sizeof(g_arr_SessionFlagsBitLabels) / sizeof(LABELED_BIT), g_arr_SessionFlagsBitLabels };


	//====================//
	// Player Flags field //---------------------------------------------------------------------------------------------
	//====================//
	LABELED_BIT g_arr_PlayerFlagsBitLabels[] = { { 1, "Player supports full-duplex connection", "Player only supports half-duplex connection" } }; // DVPLAYERCAPS_HALFDUPLEX

	SET g_LabeledPlayerFlagsBitSet = { sizeof(g_arr_PlayerFlagsBitLabels) / sizeof(LABELED_BIT), g_arr_PlayerFlagsBitLabels };


	//=====================//
	// Host Order ID field //--------------------------------------------------------------------------------------------
	//=====================//
	LABELED_DWORD g_arr_HostOrderDWordLabels[] = { { -1, "Hasn't been assigned by the host yet"	} };

	SET g_LabeledHostOrderIDDWordSet = { sizeof(g_arr_HostOrderDWordLabels) / sizeof(LABELED_DWORD), g_arr_HostOrderDWordLabels };


	////////////////////////////////
	// Custom Property Formatters //=====================================================================================
	////////////////////////////////

	// DESCRIPTION: Custom description formatter for the Voice packet summary
	//
	// ARGUMENTS: io_pProperyInstance - Data of the property's instance
	//
	// RETURNS: NOTHING
	//
	VOID WINAPIV FormatPropertyInstance_VoiceSummary( LPPROPERTYINST io_pProperyInstance )
	{
		std::string strSummary;
		char arr_cBuffer[10];

		DVPROTOCOLMSG_FULLMESSAGE&	rVoiceFrame = *reinterpret_cast<DVPROTOCOLMSG_FULLMESSAGE*>(io_pProperyInstance->lpData);

		DWORD dwType = rVoiceFrame.dvGeneric.dwType;


		// Message classification
		switch ( dwType )
		{
		case DVMSGID_CONNECTREQUEST:
		case DVMSGID_CONNECTREFUSE:
		case DVMSGID_CONNECTACCEPT:
		case DVMSGID_DISCONNECT:
		case DVMSGID_DISCONNECTCONFIRM:
		case DVMSGID_SETTINGSCONFIRM:
			{
				strSummary = "Connection Control : ";
				break;
			}

		case DVMSGID_SPEECH:
		case DVMSGID_SPEECHWITHTARGET:
		case DVMSGID_SPEECHWITHFROM:
			{
				strSummary = "Speech : ";
				break;
			}

		case DVMSGID_PLAYERLIST:
		case DVMSGID_SETTARGETS:
		case DVMSGID_CREATEVOICEPLAYER:
		case DVMSGID_DELETEVOICEPLAYER:
		case DVMSGID_SESSIONLOST:
			{
				strSummary = "Session Control : ";
				break;
			}

		default:
			{
				strSummary = "INVALID";
				break;
			}
		}


		// Message title
		switch ( dwType )
		{
		case DVMSGID_CREATEVOICEPLAYER:
			{
				strSummary += "Player ";
				strSummary += _itoa(rVoiceFrame.dvPlayerJoin.dvidID, arr_cBuffer, 16);
				strSummary += " joined the session";
				break;
			}

		default:
			{
				for ( int n = 0; n < sizeof(g_arr_MessageTypeByteLabels) / sizeof(LABELED_BYTE); ++n )
				{
					if ( g_arr_MessageTypeByteLabels[n].Value == dwType )
					{
						strSummary += g_arr_MessageTypeByteLabels[n].Label;
						break;
					}
				}

				break;
			}
		}

		// Message highlights
		switch ( dwType )
		{
		case DVMSGID_PLAYERLIST:
			{
				strSummary += " (";
				strSummary += _itoa(rVoiceFrame.dvPlayerList.dwNumEntries, arr_cBuffer, 10);
				strSummary += " players)";
				break;
			}

		case DVMSGID_CONNECTACCEPT:
			{
				strSummary += " (";
				for ( int n = 0; n < sizeof(g_arr_SessionTypeDWordLabels)/sizeof(LABELED_DWORD); ++n )
				{
					if ( g_arr_SessionTypeDWordLabels[n].Value == rVoiceFrame.dvConnectAccept.dwSessionType )
					{
						strSummary += g_arr_SessionTypeDWordLabels[n].Label;
						break;
					}
				}
				strSummary += ")";
				break;
			}

		case DVMSGID_SPEECH:
		case DVMSGID_SPEECHWITHTARGET:
		case DVMSGID_SPEECHWITHFROM:
			{
				strSummary += " [";
				strSummary += _itoa(rVoiceFrame.dvSpeech.bMsgNum, arr_cBuffer, 10);
				strSummary += ".";
				strSummary += _itoa(rVoiceFrame.dvSpeech.bSeqNum, arr_cBuffer, 10);
				strSummary += "]";
				break;
			}
		}

		strcpy(io_pProperyInstance->szPropertyText, strSummary.c_str());

	} // FormatPropertyInstance_VoiceSummary



	// DESCRIPTION: Custom description formatter for the Compression Type field
	//
	// ARGUMENTS: io_pProperyInstance - Data of the property's instance
	//
	// RETURNS: NOTHING
	//
	VOID WINAPIV FormatPropertyInstance_CompressionType( LPPROPERTYINST io_pProperyInstance )
	{

		std::string strSummary = "Compression Type = ";

		// Check what Voice frame we are dealing with
		REFGUID rguidCompressionType = *reinterpret_cast<GUID*>(io_pProperyInstance->lpData);

		if ( IsEqualGUID(rguidCompressionType, DPVCTGUID_TRUESPEECH) )
		{
			strSummary += "TrueSpeech(TM) (8.6kbps) ";
		}
		else if ( IsEqualGUID(rguidCompressionType, DPVCTGUID_GSM) )
		{
			strSummary += "Microsoft GSM 6.10 (13kbps) ";
		}
		else if ( IsEqualGUID(rguidCompressionType, DPVCTGUID_NONE) )
		{
			strSummary += "None ";
		}
		else if ( IsEqualGUID(rguidCompressionType, DPVCTGUID_ADPCM) )
		{
			strSummary += "Microsoft ADPCM (32.8kbps) ";
		}
		else if ( IsEqualGUID(rguidCompressionType, DPVCTGUID_SC03) )
		{
			strSummary += "Voxware SC03 (3.2kbps) ";
		}
		else if ( IsEqualGUID(rguidCompressionType, DPVCTGUID_SC06) )
		{
			strSummary += "Voxware SC06 (6.4kbps) ";
		}
		else if ( IsEqualGUID(rguidCompressionType, DPVCTGUID_VR12) )
		{
			strSummary += "Voxware VR12 (1.4kbps) ";
		}
		else
		{
			strSummary += "Uknown";
		}


		enum
		{
			nMAX_GUID_STRING = 50	// more than enough characters for a symbolic representation of a GUID
		};

		OLECHAR arr_wcGUID[nMAX_GUID_STRING];
		StringFromGUID2(rguidCompressionType, arr_wcGUID, sizeof(arr_wcGUID)/sizeof(TCHAR));

		char arr_cGUID[nMAX_GUID_STRING];
		WideCharToMultiByte(CP_ACP, 0, arr_wcGUID, -1, arr_cGUID, sizeof(arr_cGUID), NULL, NULL);
		strSummary += arr_cGUID;


		strcpy(io_pProperyInstance->szPropertyText, strSummary.c_str());

	} // FormatPropertyInstance_CompressionType



	// DESCRIPTION: Custom description formatter for the Players List summary
	//
	// ARGUMENTS: io_pProperyInstance - Data of the property's instance
	//
	// RETURNS: NOTHING
	//
	VOID WINAPIV FormatPropertyInstance_PlayersListSummary( LPPROPERTYINST io_pProperyInstance )
	{

		sprintf(io_pProperyInstance->szPropertyText, "List of %d players in the session", io_pProperyInstance->lpPropertyInstEx->Dword[0]);

	} // FormatPropertyInstance_PlayersListSummary



	// DESCRIPTION: Custom description formatter for the Player's Entry summary
	//
	// ARGUMENTS: io_pProperyInstance - Data of the property's instance
	//
	// RETURNS: NOTHING
	//
	VOID WINAPIV FormatPropertyInstance_PlayerEntrySummary( LPPROPERTYINST io_pProperyInstance )
	{

		DWORD* pdwData = io_pProperyInstance->lpPropertyInstEx->Dword;
		sprintf(io_pProperyInstance->szPropertyText, "Player %d out of %d", pdwData[0], pdwData[1]);

	} // FormatPropertyInstance_PlayerEntrySummary



	// DESCRIPTION: Custom description formatter for the Session Flags summary
	//
	// ARGUMENTS: io_pProperyInstance - Data of the property's instance
	//
	// RETURNS: NOTHING
	//
	VOID WINAPIV FormatPropertyInstance_SessionFlagsSummary( LPPROPERTYINST io_pProperyInstance )
	{

		std::string strSummary;

		if ( (*io_pProperyInstance->lpDword & DVSESSION_NOHOSTMIGRATION) == DVSESSION_NOHOSTMIGRATION )
		{
			strSummary = g_arr_SessionFlagsBitLabels[0].LabelOn;
		}
		else
		{
			strSummary = g_arr_SessionFlagsBitLabels[0].LabelOff;
		}


		strSummary += ", ";

		if ( (*io_pProperyInstance->lpDword & DVSESSION_SERVERCONTROLTARGET) == DVSESSION_SERVERCONTROLTARGET )
		{
			strSummary += g_arr_SessionFlagsBitLabels[1].LabelOn;
		}
		else
		{
			strSummary += g_arr_SessionFlagsBitLabels[1].LabelOff;
		}

		strcpy(io_pProperyInstance->szPropertyText, strSummary.c_str());

	} // FormatPropertyInstance_SessionFlagsSummary


	// DESCRIPTION: Custom description formatter for the Player's Entry summary
	//
	// ARGUMENTS: io_pProperyInstance - Data of the property's instance
	//
	// RETURNS: NOTHING
	//
	VOID WINAPIV FormatPropertyInstance_PlayerFlagsSummary( LPPROPERTYINST io_pProperyInstance )
	{

		std::string strSummary;

		if ( (*io_pProperyInstance->lpDword & DVSESSION_NOHOSTMIGRATION) == DVSESSION_NOHOSTMIGRATION )
		{
			strSummary = g_arr_PlayerFlagsBitLabels[0].LabelOn;
		}
		else
		{
			strSummary = g_arr_PlayerFlagsBitLabels[0].LabelOff;
		}

		strcpy(io_pProperyInstance->szPropertyText, strSummary.c_str());

	} // FormatPropertyInstance_PlayerFlagsSummary

	
	//==================//
	// Properties table //-----------------------------------------------------------------------------------------------
	//==================//
	
	PROPERTYINFO g_arr_VoiceProperties[] = 
	{

		// VOICE packet summary property (VOICE_SUMMARY)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "",											// label
		    "DPlay Voice packet",						// status-bar comment
		    PROP_TYPE_SUMMARY,							// data type
		    PROP_QUAL_NONE,								// data type qualifier
		    NULL,										// labeled bit set 
		    512,										// description's maximum length
		    FormatPropertyInstance_VoiceSummary			// generic formatter
		},

		// Message Type property (VOICE_UNPARSABLEFRAGMENT)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "This is a non-initial part of the fragmented Transport layer message and can not be parsed", // label
		    "Unparsable fragment summary",				// status-bar comment
		    PROP_TYPE_SUMMARY,							// data type
		    PROP_QUAL_NONE,								// data type qualifier.
		    NULL,										// labeled bit set 
		    128,										// description's maximum length
		    FormatPropertyInstance						// generic formatter
		},
		
		// Message Type property ((VOICE_INCOMPLETEMESSAGE)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "The rest of the data needed to parse this message has been sent in a separate fragment and can not be parsed",  // label
		    "Incomplete message summary",				// status-bar comment
		    PROP_TYPE_SUMMARY,							// data type
		    PROP_QUAL_NONE,								// data type qualifier.
		    NULL,										// labeled bit set 
		    128,										// description's maximum length
		    FormatPropertyInstance						// generic formatter
		},
		
		// Message Type property (VOICE_MESSAGETYPE)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "Message Type",								// label
		    "Message Type field",						// status-bar comment
		    PROP_TYPE_BYTE,								// data type
		    PROP_QUAL_LABELED_SET,						// data type qualifier.
		    &g_LabeledMessageTypeByteSet,				// labeled bit set 
		    64,											// description's maximum length
		    FormatPropertyInstance						// generic formatter
		},

		// Major Version property (VOICE_MAJORVERSION)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "Major Version",							// label
		    "Major Version field",						// status-bar comment
		    PROP_TYPE_BYTE,								// data type
		    PROP_QUAL_NONE,								// data type qualifier.
		    NULL,										// labeled bit set 
		    64,											// description's maximum length
		    FormatPropertyInstance						// generic formatter
		},

		// Minor Version property (VOICE_MINORVERSION)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "Minor Version",							// label
		    "Minor Version field",						// status-bar comment
		    PROP_TYPE_BYTE,								// data type
		    PROP_QUAL_NONE,								// data type qualifier.
		    NULL,										// labeled bit set 
		    64,											// description's maximum length
		    FormatPropertyInstance						// generic formatter
		},

		// Build Version property (VOICE_BUILDVERSION)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "Build Version",							// label
		    "Build Version field",						// status-bar comment
		    PROP_TYPE_DWORD,							// data type
		    PROP_QUAL_NONE,								// data type qualifier.
		    NULL,										// labeled bit set 
		    64,											// description's maximum length
		    FormatPropertyInstance						// generic formatter
		},

		// Result Code property (VOICE_RESULTCODE)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "Result Code",								// label
		    "Result Code field",						// status-bar comment
		    PROP_TYPE_DWORD,							// data type (HRESULT)
			PROP_QUAL_LABELED_SET,						// data type qualifier.
			&g_LabeledResultCodeDWordSet,				// labeled byte set 
		    64,											// description's maximum length
		    FormatPropertyInstance						// generic formatter
		},

		// Session Type property (VOICE_SESSIONTYPE)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "Session Type",								// label
		    "Session Type field",						// status-bar comment
		    PROP_TYPE_DWORD,							// data type
			PROP_QUAL_LABELED_SET,						// data type qualifier.
			&g_LabeledSessionTypeDWordSet,				// labeled byte set 
		    64,											// description's maximum length
		    FormatPropertyInstance						// generic formatter
		},

		// Session Flags property (VOICE_SESSIONFLAGS_SUMMARY)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "",											// label
		    "Session Flags summary",					// status-bar comment
		    PROP_TYPE_SUMMARY,							// data type
			PROP_QUAL_NONE,								// data type qualifier.
			NULL,										// labeled byte set 
		    64,											// description's maximum length
		    FormatPropertyInstance_SessionFlagsSummary	// generic formatter
		},

		// Session Flags property (VOICE_SESSIONFLAGS)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "Session Flags",							// label
		    "Session Flags field",						// status-bar comment
		    PROP_TYPE_DWORD,							// data type
			PROP_QUAL_FLAGS,							// data type qualifier.
			&g_LabeledSessionFlagsBitSet,				// labeled byte set 
		    512,											// description's maximum length
		    FormatPropertyInstance						// generic formatter
		},

		// Session Flags property (VOICE_PLAYERFLAGS_SUMMARY)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "",											// label
		    "Player Flags summary",						// status-bar comment
		    PROP_TYPE_SUMMARY,							// data type
			PROP_QUAL_NONE,								// data type qualifier.
			NULL,										// labeled byte set 
		    64,											// description's maximum length
		    FormatPropertyInstance_PlayerFlagsSummary	// generic formatter
		},

		// Session Flags property (VOICE_PLAYERFLAGS)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "Player Flags",								// label
		    "Player Flags field",						// status-bar comment
		    PROP_TYPE_DWORD,							// data type
			PROP_QUAL_FLAGS,							// data type qualifier.
			&g_LabeledPlayerFlagsBitSet,				// labeled byte set 
		    512,										// description's maximum length
		    FormatPropertyInstance						// generic formatter
		},

		// Number of Targets property (VOICE_NUMBEROFTARGETS)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "Number of Targets",						// label
		    "Number of Targets field",					// status-bar comment
		    PROP_TYPE_DWORD,							// data type
		    PROP_QUAL_NONE,								// data type qualifier.
		    NULL,										// labeled bit set 
		    64,											// description's maximum length
		    FormatPropertyInstance						// generic formatter
		},

		// Compression Type property (VOICE_COMPRESSIONTYPE)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "Compression Type",							// label
		    "Compression Type field",					// status-bar comment
		    PROP_TYPE_RAW_DATA,							// data type (GUID)
		    PROP_QUAL_NONE,								// data type qualifier.
		    NULL,										// labeled bit set 
		    64,											// description's maximum length
		    FormatPropertyInstance_CompressionType		// generic formatter
		},

		// Host Migration Sequence Number property (VOICE_HOSTORDERID)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "Host Migration Sequence Number",			// label
		    "Host Migration Sequence Number field",		// status-bar comment
		    PROP_TYPE_DWORD,							// data type
		    PROP_QUAL_LABELED_SET,						// data type qualifier.
		    &g_LabeledHostOrderIDDWordSet,				// labeled bit set 
		    64,											// description's maximum length
		    FormatPropertyInstance						// generic formatter
		},

		// Number of Players property (VOICE_NUMBEROFPLAYERS)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "Number of Players",						// label
		    "Number of Players field",					// status-bar comment
		    PROP_TYPE_DWORD,							// data type
		    PROP_QUAL_NONE,								// data type qualifier.
		    NULL,										// labeled bit set 
		    64,											// description's maximum length
		    FormatPropertyInstance						// generic formatter
		},

		// Player's summary property (VOICE_PLAYERLISTSUMMARY)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "",											// label
		    "Player's list summary",					// status-bar comment
		    PROP_TYPE_SUMMARY,							// data type
		    PROP_QUAL_NONE,								// data type qualifier.
		    NULL,										// labeled bit set 
		    64,											// description's maximum length
		    FormatPropertyInstance_PlayersListSummary	// generic formatter
		},

		// Player's summary property (VOICE_PLAYERSUMMARY)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "",											// label
		    "Player's summary",							// status-bar comment
		    PROP_TYPE_SUMMARY,							// data type
		    PROP_QUAL_NONE,								// data type qualifier.
		    NULL,										// labeled bit set 
		    64,											// description's maximum length
		    FormatPropertyInstance_PlayerEntrySummary	// generic formatter
		},

		// Player's ID property (VOICE_PLAYERID)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "Player ID",								// label
		    "Player ID field",							// status-bar comment
		    PROP_TYPE_DWORD,							// data type
		    PROP_QUAL_NONE,								// data type qualifier.
		    NULL,										// labeled bit set 
		    64,											// description's maximum length
		    FormatPropertyInstance						// generic formatter
		},

		// Player's ID property (VOICE_TARGETID)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "Target ID",								// label
		    "Target ID field",							// status-bar comment
		    PROP_TYPE_DWORD,							// data type
		    PROP_QUAL_NONE,								// data type qualifier.
		    NULL,										// labeled bit set 
		    64,											// description's maximum length
		    FormatPropertyInstance						// generic formatter
		},

		// Message Number property (VOICE_MESSAGENUMBER)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "Message #",							// label
		    "Message Number field",						// status-bar comment
		    PROP_TYPE_BYTE,								// data type
		    PROP_QUAL_NONE,								// data type qualifier.
		    NULL,										// labeled bit set 
		    64,											// description's maximum length
		    FormatPropertyInstance						// generic formatter
		},

		// Fragment Number property (VOICE_FRAGMENTNUMBER)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "Fragment #",							// label
		    "Fragment Number field",					// status-bar comment
		    PROP_TYPE_BYTE,								// data type
		    PROP_QUAL_NONE,								// data type qualifier.
		    NULL,										// labeled bit set 
		    64,											// description's maximum length
		    FormatPropertyInstance						// generic formatter
		},

		// Audio Data (VOICE_AUDIODATA)
	    {
		    0,											// handle placeholder (MBZ)
		    0,											// reserved (MBZ)
		    "Audio Data",								// label
		    "Audio Data",								// status-bar comment
		    PROP_TYPE_RAW_DATA,							// data type (GUID)
		    PROP_QUAL_NONE,								// data type qualifier.
		    NULL,										// labeled bit set 
		    64,											// description's maximum length
		    FormatPropertyInstance						// generic formatter
		}
	};

	enum
	{
		nNUM_OF_VOICE_PROPS = sizeof(g_arr_VoiceProperties) / sizeof(PROPERTYINFO)
	};


	// Properties' indices
	enum
	{
		VOICE_SUMMARY = 0,
		VOICE_UNPARSABLEFRAGMENT,
		VOICE_INCOMPLETEMESSAGE,

		VOICE_MESSAGETYPE,
		VOICE_MAJORVERSION,
		VOICE_MINORVERSION,
		VOICE_BUILDVERSION,
		
		VOICE_RESULTCODE,
		VOICE_SESSIONTYPE,
		
		VOICE_SESSIONFLAGS_SUMMARY,
		VOICE_SESSIONFLAGS,
		
		VOICE_PLAYERFLAGS_SUMMARY,
		VOICE_PLAYERFLAGS,
		
		VOICE_NUMBEROFTARGETS,
		VOICE_COMPRESSIONTYPE,
		VOICE_HOSTORDERID,
		VOICE_NUMBEROFPLAYERS,
		
		VOICE_PLAYERLIST_SUMMARY,
		VOICE_PLAYERSUMMARY,
		
		VOICE_PLAYERID,
		VOICE_TARGETID,
		
		VOICE_MESSAGENUMBER,
		VOICE_FRAGMENTNUMBER,
		
		VOICE_AUDIODATA
	};

} // anonymous namespace








// DESCRIPTION: Creates and fills-in a properties database for the protocol.
//				Network Monitor uses this database to determine which properties the protocol supports.
//
// ARGUMENTS: i_hVoiceProtocol - The handle of the protocol provided by the Network Monitor.
//
// RETURNS: NOTHING
//
DPLAYPARSER_API VOID BHAPI VoiceRegister( HPROTOCOL i_hVoiceProtocol ) 
{

	CreatePropertyDatabase(i_hVoiceProtocol, nNUM_OF_VOICE_PROPS);

	// Add the properties to the database
	for( int nProp=0; nProp < nNUM_OF_VOICE_PROPS; ++nProp )
	{
	   AddProperty(i_hVoiceProtocol, &g_arr_VoiceProperties[nProp]);
	}

} // VoiceRegister



// DESCRIPTION: Frees the resources used to create the protocol property database.
//
// ARGUMENTS: i_hVoiceProtocol - The handle of the protocol provided by the Network Monitor.
//
// RETURNS: NOTHING
//
DPLAYPARSER_API VOID WINAPI VoiceDeregister( HPROTOCOL i_hProtocol )
{

	DestroyPropertyDatabase(i_hProtocol);

} // VoiceDeregister




namespace
{

	// DESCRIPTION: Parses the Voice frame to find its size (in bytes) NOT including the user data
	//
	// ARGUMENTS: i_pbVoiceFrame - Pointer to the start of the unclaimed data. Typically, the unclaimed data is located
	//						    in the middle of a frame because a previous parser has claimed data before this parser.
	//
	// RETURNS: Size of the Voiceecified Voice frame (in bytes)
	//
	int VoiceHeaderSize( LPBYTE i_pbVoiceFrame )
	{

		// Check what Voice frame we are dealing with
		const DVPROTOCOLMSG_FULLMESSAGE&	rVoiceFrame = *reinterpret_cast<DVPROTOCOLMSG_FULLMESSAGE*>(i_pbVoiceFrame);

		switch ( rVoiceFrame.dvGeneric.dwType )
		{
		case DVMSGID_CONNECTREQUEST:
			{
				return sizeof(rVoiceFrame.dvConnectRequest);
			}

		case DVMSGID_CONNECTREFUSE:
			{
				return sizeof(rVoiceFrame.dvConnectRefuse);
			}

		case DVMSGID_CONNECTACCEPT:
			{
				return sizeof(rVoiceFrame.dvConnectAccept);
			}

		case DVMSGID_SETTINGSCONFIRM:
			{
				return sizeof(rVoiceFrame.dvSettingsConfirm);
			}

		case DVMSGID_PLAYERLIST:
			{
				return sizeof(rVoiceFrame.dvPlayerList);
			}

		case DVMSGID_SPEECH:
			{
				return sizeof(rVoiceFrame.dvSpeech);
			}

		case DVMSGID_SPEECHWITHTARGET:
			{
				return sizeof(rVoiceFrame.dvSpeechWithTarget);
			}

		case DVMSGID_SPEECHWITHFROM:
			{
				return sizeof(rVoiceFrame.dvSpeechWithFrom);
			}

		case DVMSGID_SETTARGETS:
			{
				return sizeof(rVoiceFrame.dvSetTarget);
			}

		case DVMSGID_CREATEVOICEPLAYER:
			{
				return sizeof(rVoiceFrame.dvPlayerJoin);
			}

		case DVMSGID_DELETEVOICEPLAYER:
			{
				return sizeof(rVoiceFrame.dvPlayerQuit);
			}

		case DVMSGID_SESSIONLOST:
			{
				return sizeof(rVoiceFrame.dvSessionLost);
			}

		case DVMSGID_DISCONNECTCONFIRM:
		case DVMSGID_DISCONNECT:
			{
				return sizeof(rVoiceFrame.dvDisconnect);
			}

		default:
			{
				return -1;	 // TODO:		DPF(0, "Unknown voice frame!");
			}
		}

	} // VoiceHeaderSize

} // Anonymous namespace



// DESCRIPTION: Indicates whether a piece of data is recognized as the protocol that the parser detects.
//
// ARGUMENTS: i_hFrame	          - The handle to the frame that contains the data.
//			  i_pbMacFrame        - The pointer to the first byte of the frame; the pointer provides a way to view
//							        the data that the other parsers recognize.
//			  i_pbVoiceFrame      - Pointer to the start of the unclaimed data. Typically, the unclaimed data is located
//								    in the middle of a frame because a previous parser has claimed data before this parser.
//			  i_dwMacType         - MAC value of the first protocol in a frame. Typically, the i_dwMacType value is used
//							        when the parser must identify the first protocol in the frame. Can be one of the following:
//							   	    MAC_TYPE_ETHERNET = 802.3, MAC_TYPE_TOKENRING = 802.5, MAC_TYPE_FDDI ANSI = X3T9.5.
//			  i_dwBytesLeft       - The remaining number of bytes from a location in the frame to the end of the frame.
//			  i_hPrevProtocol     - Handle of the previous protocol.
//			  i_dwPrevProtOffset  - Offset of the previous protocol (from the beginning of the frame).
//			  o_pdwProtocolStatus - Protocol status indicator. Must be one of the following: PROTOCOL_STATUS_RECOGNIZED,
//								    PROTOCOL_STATUS_NOT_RECOGNIZED, PROTOCOL_STATUS_CLAIMED, PROTOCOL_STATUS_NEXT_PROTOCOL.
//			  o_phNextProtocol    - Placeholder for the handle of the next protocol. This parameter is set when the parser identifies
//								    the protocol that follows its own protocol.
//			  io_pdwptrInstData   - On input, a pointer to the instance data from the previous protocol. 
//									On output, a pointer to the instance data for the current protocol. 
//
// RETURNS: If the function is successful, the return value is a pointer to the first byte after the recognized parser data.
//			If the parser claims all the remaining data, the return value is NULL. If the function is unsuccessful, the return
//		    value is the initial value of the i_pbVoiceFrame parameter.
//
DPLAYPARSER_API LPBYTE BHAPI VoiceRecognizeFrame( HFRAME        i_hFrame,
												  ULPBYTE        i_upbMacFrame,	
												  ULPBYTE        i_upbyVoiceFrame,
												  DWORD         i_dwMacType,        
												  DWORD         i_dwBytesLeft,      
												  HPROTOCOL     i_hPrevProtocol,  
												  DWORD         i_dwPrevProtOffset,
												  LPDWORD       o_pdwProtocolStatus,
												  LPHPROTOCOL   o_phNextProtocol,
												  PDWORD_PTR    io_pdwptrInstData )
{

	// Validate the amount of unclaimed data
	enum
	{
		// TODO: CHANGE TO PROPER MIN SIZE
		nMIN_VoiceHeaderSize = sizeof(_DVPROTOCOLMSG_GENERIC),
		nNUMBER_OF_MSG_TYPES = sizeof(g_arr_MessageTypeByteLabels) / sizeof(LABELED_BYTE)
	};

	for ( int nTypeIndex = 0; nTypeIndex < nNUMBER_OF_MSG_TYPES; ++nTypeIndex )
	{
		if ( g_arr_MessageTypeByteLabels[nTypeIndex].Value == *i_upbyVoiceFrame )
		{
			break;
		}
	}

	
	// Validate the packet as DPlay Session type
	if ( ((i_dwBytesLeft >= nMIN_VoiceHeaderSize)  &&  (nTypeIndex < nNUMBER_OF_MSG_TYPES))  ||  (*io_pdwptrInstData == 0) )
	{
		// Claim the remaining data
	    *o_pdwProtocolStatus = PROTOCOL_STATUS_CLAIMED;
	    return NULL;
	}

	// Assume the unclaimed data is not recognizable
	*o_pdwProtocolStatus = PROTOCOL_STATUS_NOT_RECOGNIZED;
	return i_upbyVoiceFrame;

} // VoiceRecognizeFrame



// DESCRIPTION: Maps the properties that exist in a piece of recognized data to Voiceecific locations.
//
// ARGUMENTS: i_hFrame           - Handle of the frame that is being parsed.
//			  i_pbMacFram        - Pointer to the first byte in the frame.
//			  i_pbVoiceFrame     - Pointer to the start of the recognized data.
//			  i_dwMacType        - MAC value of the first protocol in a frame. Typically, the i_dwMacType value is used
//							       when the parser must identify the first protocol in the frame. Can be one of the following:
//							       MAC_TYPE_ETHERNET = 802.3, MAC_TYPE_TOKENRING = 802.5, MAC_TYPE_FDDI ANSI = X3T9.5.
//			  i_dwBytesLeft      - The remaining number of bytes in a frame (starting from the beginning of the recognized data).
//			  i_hPrevProtocol    - Handle of the previous protocol.
//			  i_dwPrevProtOffset - Offset of the previous protocol (starting from the beginning of the frame).
//			  i_dwptrInstData    - Pointer to the instance data that the previous protocol provides.
//
// RETURNS: Must return NULL
//
DPLAYPARSER_API LPBYTE BHAPI VoiceAttachProperties( HFRAME      i_hFrame,
													ULPBYTE      i_upbyMacFrame,
													ULPBYTE      i_upbyVoiceFrame,
													DWORD       i_dwMacType,
													DWORD       i_dwBytesLeft,
													HPROTOCOL   i_hPrevProtocol,
													DWORD       i_dwPrevProtOffset,
												    DWORD_PTR   i_dwptrInstData )
{
    //===================//
    // Attach Properties //
    //===================//

    if ( i_dwptrInstData == 0 )
    {
    	AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_UNPARSABLEFRAGMENT].hProperty,
    						i_dwBytesLeft, i_upbyVoiceFrame, 0, 0, 0);
    	return NULL;
    }
    
    // Summary line
    AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_SUMMARY].hProperty,
                           VoiceHeaderSize(i_upbyVoiceFrame), i_upbyVoiceFrame, 0, 0, 0);

    // Check what Voice frame we are dealing with
	DVPROTOCOLMSG_FULLMESSAGE&	rVoiceFrame = *reinterpret_cast<DVPROTOCOLMSG_FULLMESSAGE*>(i_upbyVoiceFrame);

	// Message type field
	AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_MESSAGETYPE].hProperty,
						   sizeof(rVoiceFrame.dvGeneric.dwType), &rVoiceFrame.dvGeneric.dwType, 0, 1, 0);

	__try
	{

		switch ( rVoiceFrame.dvGeneric.dwType )
		{
		case DVMSGID_CONNECTREQUEST:
			{
				// Major Version field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_MAJORVERSION].hProperty,
									   sizeof(rVoiceFrame.dvConnectRequest.ucVersionMajor), &rVoiceFrame.dvConnectRequest.ucVersionMajor, 0, 1, 0);

				// Minor Version field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_MINORVERSION].hProperty,
									   sizeof(rVoiceFrame.dvConnectRequest.ucVersionMinor), &rVoiceFrame.dvConnectRequest.ucVersionMinor, 0, 1, 0);

				// Build Version field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_BUILDVERSION].hProperty,
									   sizeof(rVoiceFrame.dvConnectRequest.dwVersionBuild), &rVoiceFrame.dvConnectRequest.dwVersionBuild, 0, 1, 0);

				break;
			}

		case DVMSGID_CONNECTREFUSE:
			{
				// Result Code field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_RESULTCODE].hProperty,
									   sizeof(rVoiceFrame.dvConnectRefuse.hresResult), &rVoiceFrame.dvConnectRefuse.hresResult, 0, 1, 0);

				// Major Version field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_MAJORVERSION].hProperty,
									   sizeof(rVoiceFrame.dvConnectRefuse.ucVersionMajor), &rVoiceFrame.dvConnectRefuse.ucVersionMajor, 0, 1, 0);

				// Minor Version field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_MINORVERSION].hProperty,
									   sizeof(rVoiceFrame.dvConnectRefuse.ucVersionMinor), &rVoiceFrame.dvConnectRefuse.ucVersionMinor, 0, 1, 0);

				// Build Version field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_BUILDVERSION].hProperty,
									   sizeof(rVoiceFrame.dvConnectRefuse.dwVersionBuild), &rVoiceFrame.dvConnectRefuse.dwVersionBuild, 0, 1, 0);

				break;
			}

		case DVMSGID_CONNECTACCEPT:
			{
				// Session Type field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_SESSIONTYPE].hProperty,
									   sizeof(rVoiceFrame.dvConnectAccept.dwSessionType), &rVoiceFrame.dvConnectAccept.dwSessionType, 0, 1, 0);

				// Major Version field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_MAJORVERSION].hProperty,
									   sizeof(rVoiceFrame.dvConnectAccept.ucVersionMajor), &rVoiceFrame.dvConnectAccept.ucVersionMajor, 0, 1, 0);

				// Minor Version field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_MINORVERSION].hProperty,
									   sizeof(rVoiceFrame.dvConnectAccept.ucVersionMinor), &rVoiceFrame.dvConnectAccept.ucVersionMinor, 0, 1, 0);

				// Build Version field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_BUILDVERSION].hProperty,
									   sizeof(rVoiceFrame.dvConnectAccept.dwVersionBuild), &rVoiceFrame.dvConnectAccept.dwVersionBuild, 0, 1, 0);

				// Session Flags summary
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_SESSIONFLAGS_SUMMARY].hProperty,
									   sizeof(rVoiceFrame.dvConnectAccept.dwSessionFlags), &rVoiceFrame.dvConnectAccept.dwSessionFlags, 0, 1, 0);

				// Session Flags field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_SESSIONFLAGS].hProperty,
									   sizeof(rVoiceFrame.dvConnectAccept.dwSessionFlags), &rVoiceFrame.dvConnectAccept.dwSessionFlags, 0, 2, 0);

				// Compression Type field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_COMPRESSIONTYPE].hProperty,
									   sizeof(rVoiceFrame.dvConnectAccept.guidCT), &rVoiceFrame.dvConnectAccept.guidCT, 0, 1, 0);

				break;
			}

		case DVMSGID_SETTINGSCONFIRM:
			{
				// Player's Flags summary
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_PLAYERFLAGS_SUMMARY].hProperty,
									   sizeof(rVoiceFrame.dvSettingsConfirm.dwFlags), &rVoiceFrame.dvSettingsConfirm.dwFlags, 0, 1, 0);

				// Client Flags field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_PLAYERFLAGS].hProperty,
									   sizeof(rVoiceFrame.dvSettingsConfirm.dwFlags), &rVoiceFrame.dvSettingsConfirm.dwFlags, 0, 2, 0);

				// Host Migration Sequence Number field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_HOSTORDERID].hProperty,
									   sizeof(rVoiceFrame.dvSettingsConfirm.dwHostOrderID), &rVoiceFrame.dvSettingsConfirm.dwHostOrderID, 0, 1, 0);

				break;
			}

		case DVMSGID_PLAYERLIST:
			{
				// Host Order ID field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_HOSTORDERID].hProperty,
									   sizeof(rVoiceFrame.dvPlayerList.dwHostOrderID), &rVoiceFrame.dvPlayerList.dwHostOrderID, 0, 1, 0);

				// Number of Players field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_NUMBEROFPLAYERS].hProperty,
									   sizeof(rVoiceFrame.dvPlayerList.dwNumEntries), &rVoiceFrame.dvPlayerList.dwNumEntries, 0, 1, 0);


				// Player entries are following after the header
				DVPROTOCOLMSG_PLAYERLIST_ENTRY* pPlayerEntry =
					reinterpret_cast<DVPROTOCOLMSG_PLAYERLIST_ENTRY*>(&rVoiceFrame.dvPlayerList + 1);

				// Make sure the list doesn't overflow the boundaries of the frame
				DWORD dwNumEntries = rVoiceFrame.dvPlayerList.dwNumEntries;
				if ( reinterpret_cast<LPBYTE>(pPlayerEntry + dwNumEntries) - i_upbyVoiceFrame  >  static_cast<int>(i_dwBytesLeft) )
				{
					break;
				}

				// Player list summary
				AttachPropertyInstanceEx(i_hFrame, g_arr_VoiceProperties[VOICE_PLAYERLIST_SUMMARY].hProperty,
									     dwNumEntries * sizeof(*pPlayerEntry), pPlayerEntry,
									     sizeof(DWORD), &dwNumEntries,
									     0, 1, 0);

				// For every player entry in the list
				for ( int nEntry = 1; nEntry <= dwNumEntries; ++nEntry, ++pPlayerEntry )
				{
					// Player's summary
					struct
					{
						DWORD dwPlayerNum;
						DWORD dwTotalPlayer;
					}
					PlayerEntryData = { nEntry, dwNumEntries };

					AttachPropertyInstanceEx(i_hFrame, g_arr_VoiceProperties[VOICE_PLAYERSUMMARY].hProperty,
											 sizeof(*pPlayerEntry), pPlayerEntry,
											 sizeof(PlayerEntryData), &PlayerEntryData,
											 0, 2, 0);

					// Player's ID field
					AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_PLAYERID].hProperty,
										   sizeof(pPlayerEntry->dvidID), &pPlayerEntry->dvidID, 0, 3, 0);

					// Player's Flags summary
					AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_PLAYERFLAGS_SUMMARY].hProperty,
										   sizeof(pPlayerEntry->dwPlayerFlags), &pPlayerEntry->dwPlayerFlags, 0, 3, 0);

					// Player's Flags field
					AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_PLAYERFLAGS].hProperty,
										   sizeof(pPlayerEntry->dwPlayerFlags), &pPlayerEntry->dwPlayerFlags, 0, 4, 0);

					// Host Migration Sequence Number field
					AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_HOSTORDERID].hProperty,
										   sizeof(pPlayerEntry->dwHostOrderID), &pPlayerEntry->dwHostOrderID, 0, 3, 0);
				}

				break;
			}

		case DVMSGID_SPEECH:
			{
				// Message Number field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_MESSAGENUMBER].hProperty,
									   sizeof(rVoiceFrame.dvSpeech.bMsgNum), &rVoiceFrame.dvSpeech.bMsgNum, 0, 1, 0);

				// Sequence Number field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_FRAGMENTNUMBER].hProperty,
									   sizeof(rVoiceFrame.dvSpeech.bSeqNum), &rVoiceFrame.dvSpeech.bSeqNum, 0, 1, 0);

				// Audio Data
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_AUDIODATA].hProperty,
									   i_dwBytesLeft-sizeof(rVoiceFrame.dvSpeech), &rVoiceFrame.dvSpeech + 1, 0, 1, 0);

				break;
			}

		case DVMSGID_SPEECHWITHTARGET:
			{
				// Message Number field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_MESSAGENUMBER].hProperty,
									   sizeof(rVoiceFrame.dvSpeechWithTarget.dvHeader.bMsgNum), &rVoiceFrame.dvSpeechWithTarget.dvHeader.bMsgNum, 0, 1, 0);

				// Sequence Number field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_FRAGMENTNUMBER].hProperty,
									   sizeof(rVoiceFrame.dvSpeechWithTarget.dvHeader.bSeqNum), &rVoiceFrame.dvSpeechWithTarget.dvHeader.bSeqNum, 0, 1, 0);

				// Number of Targets field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_NUMBEROFTARGETS].hProperty,
									   sizeof(rVoiceFrame.dvSpeechWithTarget.dwNumTargets), &rVoiceFrame.dvSpeechWithTarget.dwNumTargets, 0, 1, 0);


				// Target ID entries are following after the header
				DVID* pTargetID = reinterpret_cast<DVID*>(&rVoiceFrame.dvSpeechWithTarget + 1);

				// Make sure the list doesn't overflow the boundaries of the frame
				int nNumTargets = rVoiceFrame.dvSpeechWithTarget.dwNumTargets;
				if ( reinterpret_cast<LPBYTE>(pTargetID + nNumTargets) - i_upbyVoiceFrame  >  static_cast<int>(i_dwBytesLeft) )
				{
					break;
				}

				// For every target ID entry in the list...
				for ( ; nNumTargets; --nNumTargets, ++pTargetID )
				{
					// Target's ID field
					AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_TARGETID].hProperty,
										   sizeof(*pTargetID), pTargetID, 0, 1, 0);
				}

				// Audio Data
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_AUDIODATA].hProperty,
									   i_dwBytesLeft-sizeof(rVoiceFrame.dvSpeechWithTarget), &rVoiceFrame.dvSpeechWithTarget + 1, 0, 1, 0);
				
				break;
			}

		case DVMSGID_SPEECHWITHFROM:
			{
				// Message Number field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_MESSAGENUMBER].hProperty,
									   sizeof(rVoiceFrame.dvSpeechWithFrom.dvHeader.bMsgNum), &rVoiceFrame.dvSpeechWithFrom.dvHeader.bMsgNum, 0, 1, 0);

				// Sequence Number field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_FRAGMENTNUMBER].hProperty,
									   sizeof(rVoiceFrame.dvSpeechWithFrom.dvHeader.bSeqNum), &rVoiceFrame.dvSpeechWithFrom.dvHeader.bSeqNum, 0, 1, 0);

				// Speaking Player's ID field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_PLAYERID].hProperty,
									   sizeof(rVoiceFrame.dvSpeechWithFrom.dvidFrom), &rVoiceFrame.dvSpeechWithFrom.dvidFrom, 0, 1, 0);

				// Audio Data
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_AUDIODATA].hProperty,
									   i_dwBytesLeft-sizeof(rVoiceFrame.dvSpeechWithFrom),
									   &rVoiceFrame.dvSpeechWithFrom + 1, 0, 1, 0);
				
				break;
			}

		case DVMSGID_SETTARGETS:
			{
				// Number of Targets field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_NUMBEROFTARGETS].hProperty,
									   sizeof(rVoiceFrame.dvSetTarget.dwNumTargets), &rVoiceFrame.dvSetTarget.dwNumTargets, 0, 1, 0);

				break;
			}

		case DVMSGID_CREATEVOICEPLAYER:
			{
				// Player ID field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_PLAYERID].hProperty,
									   sizeof(rVoiceFrame.dvPlayerJoin.dvidID), &rVoiceFrame.dvPlayerJoin.dvidID, 0, 1, 0);

				// Player's Flags summary
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_PLAYERFLAGS_SUMMARY].hProperty,
									   sizeof(rVoiceFrame.dvPlayerJoin.dwFlags), &rVoiceFrame.dvPlayerJoin.dwFlags, 0, 1, 0);

				// Player's Flags field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_PLAYERFLAGS].hProperty,
									   sizeof(rVoiceFrame.dvPlayerJoin.dwFlags), &rVoiceFrame.dvPlayerJoin.dwFlags, 0, 2, 0);

				// Host Order ID field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_HOSTORDERID].hProperty,
									   sizeof(rVoiceFrame.dvPlayerJoin.dwHostOrderID), &rVoiceFrame.dvPlayerJoin.dwHostOrderID, 0, 1, 0);

				break;
			}

		case DVMSGID_DELETEVOICEPLAYER:
			{
				// Player ID field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_PLAYERID].hProperty,
									   sizeof(rVoiceFrame.dvPlayerJoin.dvidID), &rVoiceFrame.dvPlayerJoin.dvidID, 0, 1, 0);

				break;
			}

		case DVMSGID_SESSIONLOST:
			{
				// Result Code field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_RESULTCODE].hProperty,
									   sizeof(rVoiceFrame.dvSessionLost.hresReason), &rVoiceFrame.dvSessionLost.hresReason, 0, 1, 0);

				break;
			}

		case DVMSGID_DISCONNECTCONFIRM:
		case DVMSGID_DISCONNECT:
			{
				// Result Code field
				AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_RESULTCODE].hProperty,
									   sizeof(rVoiceFrame.dvDisconnect.hresDisconnect), &rVoiceFrame.dvDisconnect.hresDisconnect, 0, 1, 0);

				break;
			}

		default:
			{
				break; // TODO:		DPF(0, "Unknown voice frame!");
			}
		}

	}
	__except ( GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH )
	{
    	AttachPropertyInstance(i_hFrame, g_arr_VoiceProperties[VOICE_INCOMPLETEMESSAGE].hProperty,
    						i_dwBytesLeft, i_upbyVoiceFrame, 0, 1, 0);
	}
	

	return NULL;

} // VoiceAttachProperties





// DESCRIPTION: Formats the data that is diVoicelayed in the details pane of the Network Monitor UI.
//
// ARGUMENTS: i_hFrame          - Handle of the frame that is being parsed.
//			  i_pbMacFrame		- Pointer to the first byte of a frame.
//			  i_pbCoreFrame		- Pointer to the beginning of the protocol data in a frame.
//            i_dwPropertyInsts - Number of PROPERTYINST structures provided by lpPropInst.
//			  i_pPropInst		- Pointer to an array of PROPERTYINST structures.
//
// RETURNS: If the function is successful, the return value is a pointer to the first byte after the recognized data in a frame,
//          or NULL if the recognized data is the last piece of data in a frame. If the function is unsuccessful, the return value
//			is the initial value of i_upbyVoiceFrame.
//
DPLAYPARSER_API DWORD BHAPI VoiceFormatProperties( HFRAME          i_hFrame,
												   ULPBYTE          i_upbyMacFrame,
												   ULPBYTE          i_upbyVoiceFrame,
												   DWORD           i_dwPropertyInsts,
												   LPPROPERTYINST  i_pPropInst )
{

    // Loop through the property instances...
    while( i_dwPropertyInsts-- > 0)
    {
        // ...and call the formatter for each
        reinterpret_cast<FORMAT>(i_pPropInst->lpPropertyInfo->InstanceData)(i_pPropInst);
        ++i_pPropInst;
    }

	// TODO: MAKE SURE THIS SHOULD NOT BE TRUE
    return NMERR_SUCCESS;

} // VoiceFormatProperties




// DESCRIPTION: Notifies Network Monitor that DNET protocol parser exists.
//
// ARGUMENTS: NONE
//
// RETURNS: TRUE - success, FALSE - failure
//
bool CreateVoiceProtocol( void )
{

	// The entry points to the export functions that Network Monitor uses to operate the parser
	ENTRYPOINTS VoiceEntryPoints =
	{
		// VoiceParser Entry Points
		VoiceRegister,
		VoiceDeregister,
		VoiceRecognizeFrame,
		VoiceAttachProperties,
		VoiceFormatProperties
	};

    // The first active instance of this parser needs to register with the kernel
    g_hVoiceProtocol = CreateProtocol("DPLAYVOICE", &VoiceEntryPoints, ENTRYPOINTS_SIZE);
	
	return (g_hVoiceProtocol ? TRUE : FALSE);

} // CreateVoiceProtocol



// DESCRIPTION: Removes the DNET protocol parser from the Network Monitor's database of parsers
//
// ARGUMENTS: NONE
//
// RETURNS: NOTHING
//
void DestroyVoiceProtocol( void )
{

	DestroyProtocol(g_hVoiceProtocol);

} // DestroyVoiceProtocol