image

Open All  |  Close All
X|Mode Wireshark Plugin
Please click on the links below to view more information.

Screen Shot

Code
/* Rev: $Id: $ */
/* stephen.keohane@nuance.com */
/* May 2008 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <gmodule.h>

#include <epan/packet.h>
#include <epan/emem.h>
#include <epan/dissectors/packet-tcp.h>

#include <epan/prefs.h>
#include <epan/expert.h>

/* forward reference */
void proto_register_xmode(void);
void proto_reg_handoff_xmode(void);

/* dissectors */
void dissect_xmode(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree);
guint get_xmode_message_len(packet_info* pinfo, tvbuff_t* tvb, int offset);
void dissect_xmode_message(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree);

/* cop dissectors */
void dissect_xmode_cop(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version);
void dissect_xmode_cop_connect_0x12(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);
void dissect_xmode_cop_connect_0x13(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);
void dissect_xmode_cop_connect_0x15(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);
void dissect_xmode_cop_connect_0x16(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);
void dissect_xmode_cop_connected(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version);
void dissect_xmode_cop_disconnect(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version);
void dissect_xmode_cop_ping_request(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);
void dissect_xmode_cop_ping_response(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);
void dissect_xmode_cop_connect_failed(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version);
void dissect_xmode_cop_route_request(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);
void dissect_xmode_cop_route_response(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);

/* bcp dissectors */
void dissect_xmode_bcp(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version);
void dissect_xmode_bcp_recognition(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version);
void dissect_xmode_bcp_generate_audio_complete(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version);
void dissect_xmode_bcp_with_request_id(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version);
void dissect_xmode_bcp_xml(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version);
void dissect_xmode_bcp_data(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version);
void dissect_xmode_bcp_response(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version);
void dissect_xmode_bcp_params_complete(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version);
void dissect_xmode_bcp_cancel(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version);
void dissect_xmode_bcp_cancel_response(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version);
void dissect_xmode_bcp_pdx(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);
void dissect_xmode_bcp_connect(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);
void dissect_xmode_bcp_connected(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);
void dissect_xmode_bcp_connect_failed(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);
void dissect_xmode_bcp_device_disconnect(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);

void dissect_xmode_bcp_begin(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);
void dissect_xmode_bcp_end(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);
void dissect_xmode_bcp_event(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);
void dissect_xmode_bcp_load_resource(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);
void dissect_xmode_bcp_load_grammar(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version);

/* vap dissectors */
void dissect_xmode_vap(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version);
void dissect_xmode_vap_begin(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version);
void dissect_xmode_vap_end(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version);
void dissect_xmode_vap_audio(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version);
/* 1.1 */
void dissect_xmode_vap_backup_request(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);
void dissect_xmode_vap_recover_request(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);
void dissect_xmode_vap_backup_response(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);
void dissect_xmode_vap_recover_response(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);
void dissect_xmode_vap_release(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree);

/* nmsp dissector */
void dissect_nmsp(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version);


/* Define version if we are not building Wireshark statically */
#ifndef ENABLE_STATIC
G_MODULE_EXPORT const gchar version[] = "0.1.10";
#endif

/* define the xmode proto */
static int proto_xmode = -1;

static int global_xmode_port = 8911; 

static dissector_handle_t xmode_handle;

/* define the headers for xmode */
static int hf_xmode_protocol_type = -1;
static int hf_xmode_protocol_version = -1;
static int hf_xmode_cop_command = -1;
static int hf_xmode_bcp_command = -1;
static int hf_xmode_vap_command = -1;
static int hf_nmsp_command = -1;
static int hf_xmode_payload_length = -1;
static int hf_xmode_payload_length_long = -1;
static int hf_xmode_payload = -1;
static int hf_xmode_length = -1;
static int hf_xmode_length_long = -1;
static int hf_xmode_int32 = -1;
static int hf_xmode_int16 = -1;
static int hf_xmode_uint32 = -1;
static int hf_xmode_session_id = -1;
static int hf_xmode_uuid = -1;

/* COP 0x012 Parameters */
static int hf_xmode_device_type = -1;
static int hf_xmode_sdk_version = -1;
static int hf_xmode_to_server_audio_type = -1;
static int hf_xmode_to_server_audio_subtype = -1;
static int hf_xmode_from_server_audio_type = -1;
static int hf_xmode_from_server_audio_subtype = -1;
static int hf_xmode_from_server_audio_buffer_size = -1;
static int hf_xmode_cop_disconnect_reason = -1;

/* COP 0x014 Parameters - ping */
static int hf_xmode_ping_request = -1;
static int hf_xmode_ping_previous_rtt = -1;
static int hf_xmode_ping_latency = -1;
static int hf_xmode_cop_connect_failure_code = -1;

/* COP 0x016 Parameters */
static int hf_xmode_cop_connect_data_type = -1;

static int hf_xmode_string = -1;

/* static int hf_xmode_xml = -1; */
static int hf_xmode_request_id = -1;
static int hf_xmode_status_code = -1;
static int hf_xmode_request_state = -1;
static int hf_xmode_completion_cause = -1;
static int hf_xmode_bcp_event = -1;
static int hf_xmode_bcp_tid = -1;
static int hf_xmode_bcp_timeout = -1;


static int hf_xmode_audio_id = -1;
static int hf_xmode_audio_type_0x10 = -1;
static int hf_xmode_audio_subtype = -1;
static int hf_xmode_audio_type_0x11 = -1;

static gint ett_xmode = -1;
static gint ett_xmode_cop = -1;
static gint ett_xmode_bcp = -1;
static gint ett_xmode_vap = -1;
static gint ett_nmsp = -1;

#define NMSP 0x0f 

#define XMODE_COP 3
#define XMODE_BCP 2
#define XMODE_VAP 1

#define COP_CONNECT            0x0100
#define COP_CONNECTED           0x0101
#define COP_PING_REQUEST         0x0102
#define COP_PING_RESPONSE         0x0103
#define COP_DISCONNECT          0x0200
#define COP_CONNECT_FAILED        0x0300
#define COP_ROUTE_REQUEST        0x0104 /* deprecated in 1.6 */
#define COP_ROUTE_RESPONSE        0x0105 /* deprecated in 1.6 */


#define BCP_EXTENSION          0x0E01 /* deprecated in 1.6 */

#define BCP_RESPONSE          0x0A10
#define BCP_GENERATE_AUDIO_COMPLETE    0x0A11
#define BCP_START_OF_SPEECH        0x0A12
#define BCP_RECOGNITION_COMPLETE    0x0A13
#define BCP_SET_PARAMS_COMPLETE      0x0A16
#define BCP_GET_PARAMS_COMPLETE      0x0A18
#define BCP_DATA            0x0A19

#define BCP_GENERATE_AUDIO        0x0A00
#define BCP_LOAD_GRAMMAR        0x0A01
#define BCP_RECOGNIZE          0x0A02
#define BCP_CANCEL            0x0A03
#define BCP_CANCEL_RESPONSE        0x0A20
#define BCP_UNLOAD_ALL_GRAMMARS      0x0A04 /* deprecated? */
#define BCP_RECOGNITION_INTERMRESULTS  0x0A14 
#define BCP_SET_PARAMS          0x0A15
#define BCP_GET_PARAMS          0x0A17

#define BCP_CONNECT            0x0A21 /* version BCP 2.1 0nly */
#define BCP_DEVICE_DISCONNECT      0x0A22 /* version BCP 2.1 0nly */
#define BCP_CONNECTED          0x0A23 /* version BCP 2.1 0nly */
#define BCP_CONNECT_FAILED        0x0A24 /* version BCP 2.1 0nly */

#define BCP_BEGIN            0x0A25 /* BCP 2.2 Add */
#define BCP_END              0x0A26 /* BCP 2.2 Add */
#define BCP_LOAD_RESOURCE        0x0A27 /* BCP 2.2 Add */
#define BCP_EVENT            0x0A28 /* BCP 2.2 Add */

#define VAP_PLAY_BEGIN          0x0210
/* #define VAP_PLAY_BEGIN_NONBARGEABLE    0x0220 */
#define VAP_PLAY            0x0200
#define VAP_PLAY_END          0x0400
#define VAP_UNKNOWN            0x0FF0

#define VAP_RECORD_BEGIN        0x0101
#define VAP_RECORD_END          0x0100
#define VAP_RECORD            0x0201

/* VAP 1.1 */
#define VAP_BACKUP_REQUEST        0x0440
#define VAP_BACKUP_RESPONSE        0x0441
#define VAP_RECOVER_REQUEST        0x0442
#define VAP_RECOVER_RESPONSE      0x0443
#define VAP_RELEASE            0x0444


static const value_string protocol_type_names[] =
{
  { XMODE_VAP, "VAP" },
  { XMODE_BCP, "BCP" },
  { XMODE_COP, "COP" },
  { NMSP, "NMSP" },
  { 0, NULL }
};

static const value_string xmode_cop_command_names[] =
{
  { COP_CONNECT, "COP_CONNECT" },
  { COP_CONNECTED, "COP_CONNECTED" },
  { COP_PING_REQUEST, "COP_PING_REQUEST" },
  { COP_PING_RESPONSE, "COP_PING_RESPONSE" },
  { COP_ROUTE_REQUEST, "COP_ROUTE_REQUEST" },
  { COP_ROUTE_RESPONSE, "COP_ROUTE_RESPONSE" },
  { COP_DISCONNECT, "COP_DISCONNECT" },
  { COP_CONNECT_FAILED, "COP_CONNECT_FAILED" },
  { 0, NULL }
};

/* COP 0x12 Parameters */
static const value_string xmode_device_type_names[] =
{
  { 1, "Brew Device" },
  { 2, "Symbian Device" },
  { 3, "Pocket PC Device" },
  { 4, "Smart Phone Device" },
  { 5, "Win32 Device" },
  { 6, "J2SE Device" },
  { 7, "J2ME Device" },
  { 8, "Trinity Device" },
  { 9, "Palm " },
  { 0, NULL }
};

static const value_string xmode_audio_type_names_0x10[] =
{
  { 1, "PCM-16" },
  { 2, "G.711 mu-law" },
  { 3, "DSR" },
  { 4, "G.729" },
  { 5, "AMR" },
  { 6, "QCP" },
  { 0, NULL }
};

static const value_string xmode_audio_type_names_0x11[] =
{
  { 0x001, "Unknown" },
  { 0x002, "PCM-16" },
  { 0x004, "PCM-16-11Khz" },
  { 0x008, "ULAW" },
  { 0x030, "DSR-ES201108" },
  { 0x031, "DSR-ES201108-11Khz" },
  { 0x040, "G729-R0" },
  { 0x041, "G729-R1" },
  { 0x042, "G729-R2" },
  { 0x050, "AMR-R0" },
  { 0x051, "AMR-R1" },
  { 0x052, "AMR-R2" },
  { 0x053, "AMR-R3" },
  { 0x054, "AMR-R4" },
  { 0x055, "AMR-R5" },
  { 0x056, "AMR-R6" },
  { 0x057, "AMR-R7" },
  { 0x062, "QCELP-FF13K" },
  { 0x063, "QCELP-VARIABLE" },
  { 0x070, "EVRC-VARIABLE" },
  { 0, NULL }
};

static const value_string xmode_cop_disconnect_reason_names[] =
{
  { 0, "Normal" },
  { 1, "Timeout" },
  { 2, "Server Shutdown" },
  { 3, "Critical Error" },
  { 4, "Ping Timeout" },
  { 5, "Server Unavailable" },
  { 6, "Endpoint Network Exception" },
  { 7, NULL }
};

static const value_string xmode_cop_connect_failure_code_names[] =
{
  { 0, "Busy" },
  { 1, "Speech Server Unavailable" },
  { 2, "Unknown" },
  { 3, "Unknown Command" },
  { 4, "Record Codec Unsupported" },
  { 5, "Play Codec Unsupported" },
  { 6, "Play and Record Codecs Unsupported" },
  { 403, "Connect Forbidden" },
  { 404, "Connect Not Found" },
  { 412, "Connection Precondition Failed" },
  { 999, NULL }
};

static const value_string xmode_cop_connect_data_type_names[] =
{
  { 0, "NMSP" },
  { 1, "Application Server" },
  { 2, NULL }
};

static const value_string xmode_bcp_command_names[] =
{
  { BCP_EXTENSION, "BCP_EXTENSION" },
  { BCP_RESPONSE, "BCP_RESPONSE" },
  { BCP_GENERATE_AUDIO, "BCP_GENERATE_AUDIO" },
  { BCP_START_OF_SPEECH, "BCP_START_OF_SPEECH" },
  { BCP_RECOGNITION_COMPLETE, "BCP_RECOGNITION_COMPLETE" },
  { BCP_GENERATE_AUDIO_COMPLETE, "BCP_GENERATE_AUDIO_COMPLETE" },
  { BCP_DATA, "BCP_DATA" },
  { BCP_LOAD_GRAMMAR, "BCP_LOAD_GRAMMAR" },
  { BCP_RECOGNIZE, "BCP_RECOGNIZE" },
  { BCP_CANCEL, "BCP_CANCEL" },
  { BCP_CANCEL_RESPONSE, "BCP_CANCEL_RESPONSE" },
  { BCP_UNLOAD_ALL_GRAMMARS, "BCP_UNLOAD_ALL_GRAMMARS" },
  { BCP_RECOGNITION_INTERMRESULTS, "BCP_RECOGNITION_ITERMRESULTS" },
  { BCP_SET_PARAMS, "BCP_SET_PARAMS" },
  { BCP_SET_PARAMS_COMPLETE, "BCP_SET_PARAMS_COMPLETE" },
  { BCP_GET_PARAMS, "BCP_GET_PARAMS" },
  { BCP_GET_PARAMS_COMPLETE, "BCP_GET_PARAMS_COMPLETE" },
  { BCP_CONNECT, "BCP_CONNECT" },
  { BCP_CONNECTED, "BCP_CONNECTED" },
  { BCP_CONNECT_FAILED, "BCP_CONNECT_FAILED" },
  { BCP_DEVICE_DISCONNECT, "BCP_DEVICE_DISCONNECT" },
  { BCP_BEGIN, "BCP_BEGIN" },
  { BCP_END, "BCP_END" },
  { BCP_LOAD_RESOURCE, "BCP_LOAD_RESOURCE" },
  { BCP_EVENT, "BCP_EVENT" },
  { 0, NULL }
};

static const value_string xmode_status_code_names[] =
{
  { 200, "Success"},
  { 201, "Success with some optional parameters"},
  { 401, "Method not allowed"},
  { 402, "Method not valid in this state"},
  { 403, "Unsupported parameters"},
  { 404, "Illegal value for parameters"},
  { 405, "Not found"},
  { 406, "Mandatory parameter Missing"},
  { 407, "Method or operation failed"},
  { 408, "Unrecognized unsupported message entity"},
  { 409, "Unsupported parameter value"},
  { 421, "Resoure specific failure codes"},
  { 599, "Resoure specific failure codes"},
  { 0, NULL }
};

static const value_string xmode_request_state_names[] =
{
  { 0, NULL }
};

static const value_string xmode_completion_cause_names[] =
{
  { 0x0000, "Success"},
  { 0x0001, "No Match"},
  { 0x0002, "No Input Timeout"},
  { 0x0003, "Recognition Timeout"},
  { 0x0004, "Grammar Load Failure"},
  { 0x0005, "Grammar Compilation Failure"},
  { 0x0006, "Error"},
  { 0x0007, "Speech Too Early"},
  { 0x0008, "Too Much Speech"},
  { 0x0009, "URI Failure"},
  { 0x000A, "Language Unsupported"},
  { 0x000B, "Cancelled"},
  { 0x000C, "Semantics Failure"},
  { 0x000D, "No Grammar Defined"},
  { 0x000E, "3rd Party Application Error"},
  { 0x000F, "Unknown"},
  { 0xFFFF, NULL}
};

static const value_string xmode_bcp_event_names[] =
{
   { 0x000, "Client Disconnect" },
     { 0x001, "Client Socket Disconnect" },
     { 0x002, "Client Ping Timeout" },
     { 0x003, "Client Idle Timeout" },
     { 0x004, "Server Disconnect" },
     { 0x005, "Server Unavailable" },
     { 0x006, "Server Socket Disconnect" },
   { 0x007, "Appserver Disconnect" },
   { 0x008, "Appserver Socket Disconnect" },
   { 0x009, "Appserver Unavailable" },
  { 0, NULL }
};

static const value_string xmode_vap_command_names[] =
{
  { VAP_PLAY_BEGIN, "VAP_PLAY_BEGIN" },
  { VAP_PLAY, "VAP_PLAY" },
  { VAP_PLAY_END, "VAP_PLAY_END" },
  { VAP_RECORD, "VAP_RECORD" },
  { VAP_RECORD_BEGIN, "VAP_RECORD_BEGIN" },
  { VAP_RECORD_END, "VAP_RECORD_END" },
  { VAP_BACKUP_REQUEST, "VAP_BACKUP_REQUEST" },
  { VAP_RECOVER_REQUEST, "VAP_RECOVER_REQUEST" },
  { VAP_BACKUP_RESPONSE, "VAP_BACKUP_RESPONSE" },
  { VAP_RECOVER_RESPONSE, "VAP_RECOVER_RESPONSE" },
  { VAP_RELEASE, "VAP_RELEASE" },
  { 0, NULL }
};

#ifndef ENABLE_STATIC
G_MODULE_EXPORT void plugin_register(void)
{
  /* register the new protocol, protocol fields, and subtrees */
  if (proto_xmode == -1) { /* execute protocol initialization only once */
    proto_register_xmode();
  }
}

G_MODULE_EXPORT void plugin_reg_handoff(void)
{
  proto_reg_handoff_xmode();
}
#endif


/* setup protocol subtree array */
static gint *ett[] = {
  &ett_xmode,
  &ett_xmode_cop,
  &ett_xmode_bcp,
  &ett_xmode_vap,
  &ett_nmsp
};

/*
 * plugin initialization
 */
void proto_register_xmode(void)
{
  static hf_register_info hf[] = 
  {
    { &hf_xmode_protocol_type,
      { "Protocol Type", "xmode.protocol_type", FT_UINT8, BASE_DEC, 
        VALS(protocol_type_names), 0x0, "Protocol Type", HFILL }},
    { &hf_xmode_protocol_version,
      { "Protocol Version", "xmode.protocol_version", FT_UINT8, BASE_HEX, 
        NULL, 0x0, "Protocol Version", HFILL }},
    { &hf_xmode_cop_command,
      { "Command", "xmode.cop.command", FT_UINT16, BASE_HEX, 
        VALS(xmode_cop_command_names), 0x0, "Command", HFILL }},
    { &hf_xmode_bcp_command,
      { "Command", "xmode.bcp.command", FT_UINT16, BASE_HEX, 
        VALS(xmode_bcp_command_names), 0x0, "Command", HFILL }},

    { &hf_xmode_vap_command,
      { "Command", "xmode.vap.command", FT_UINT16, BASE_HEX, 
        VALS(xmode_vap_command_names), 0x0, "Command", HFILL }},

    { &hf_nmsp_command,
      { "Command", "xmode.nmsp.command", FT_UINT16, BASE_HEX, 
        NULL, 0x0, "Command", HFILL }},
    
    { &hf_xmode_payload_length,
      { "Payload Length", "xmode.payload_length", FT_UINT16, BASE_DEC, 
        NULL, 0x0, "Payload Length", HFILL }},

    { &hf_xmode_payload_length_long,
      { "Payload Length Long", "xmode.payload_length_long", FT_UINT32, BASE_DEC, 
        NULL, 0x0, "Payload Length Long", HFILL }},

    { &hf_xmode_payload,
      { "Payload", "xmode.payload", FT_BYTES, BASE_DEC, 
        NULL, 0x0, "Payload", HFILL }},
    { &hf_xmode_length,
      { "Length", "xmode.length", FT_UINT16, BASE_DEC, 
        NULL, 0x0, "Length", HFILL }},
    { &hf_xmode_length_long,
      { "Length Long", "xmode.length_long", FT_UINT32, BASE_DEC, 
        NULL, 0x0, "Length Long", HFILL }},
    { &hf_xmode_int16,
      { "short", "xmode.int16", FT_INT16, BASE_DEC, 
        NULL, 0x0, "int16", HFILL }},
    { &hf_xmode_int32,
      { "int", "xmode.int32", FT_INT32, BASE_DEC, 
        NULL, 0x0, "int32", HFILL }},
    { &hf_xmode_uint32,
      { "unsigned int32", "xmode.uint32", FT_UINT32, BASE_DEC, 
        NULL, 0x0, "uint32", HFILL }},
    /* cop */
    { &hf_xmode_session_id,
      { "Session ID", "xmode.session_id", FT_UINT32, BASE_DEC, 
        NULL, 0x0, "Session ID", HFILL }},


    /* there is a e_guid_t but it is long, short, short, char[8] 
     * rather then get into endian problems with client as these 
     * would all be le, we use char[16]
     */

    { &hf_xmode_uuid,
      { "Uuid", "xmode.uuid", 
        FT_BYTES,  /* ftypes.h - FT_PROTOCOL */
        BASE_HEX,  /* one of BASE_ */
        NULL,     /* vals() ,protocol_t struct? */
        0x0,     /* bitmask */
        "Uuid", 
        HFILL }},

    /* COP 0x12 Parmeters */

    { &hf_xmode_device_type,
      { "Device Type", "xmode.device_type", FT_UINT16, BASE_DEC, 
        VALS(xmode_device_type_names), 0x0, "Device Type", HFILL }},
    { &hf_xmode_sdk_version,
      { "SDK Version", "xmode.sdk_version", FT_UINT16, BASE_DEC, 
        NULL, 0x0, "SDK Version", HFILL }},
    { &hf_xmode_to_server_audio_type,
      { "To Server Audio Type", "xmode.to_server_audio_type", FT_UINT16, BASE_DEC, 
        VALS(xmode_audio_type_names_0x10), 0x0, "To Server Audio Type", HFILL }},
    { &hf_xmode_to_server_audio_subtype,
      { "To Server Audio Subtype", "xmode.to_server_audio_subtype", FT_UINT16, BASE_DEC, 
        NULL, 0x0, "To Server Audio Subype", HFILL }},
    { &hf_xmode_from_server_audio_type,
      { "From Server Audio Type", "xmode.from_server_audio_type", FT_UINT16, BASE_DEC, 
        VALS(xmode_audio_type_names_0x10), 0x0, "From Server Audio Type", HFILL }},
    { &hf_xmode_from_server_audio_subtype,
      { "From Server Audio Subtype", "xmode.from_server_audio_subtype", FT_UINT16, BASE_DEC, 
        NULL, 0x0, "From Server Audio Subtype", HFILL }},
    { &hf_xmode_from_server_audio_buffer_size,
      { "To Server Audio Buffer Size", "xmode.from_server_audio_buffer_size", FT_UINT16, BASE_DEC, 
        NULL, 0x0, "From Server Audio Buffer Size", HFILL }},

    /* COP 0x14 Parameters */
    { &hf_xmode_ping_request,
      { "Ping Request Id", "xmode.ping_request", FT_UINT32, BASE_DEC, 
        NULL, 0x0, "Ping Request Id", HFILL }},
    { &hf_xmode_ping_previous_rtt,
      { "Ping Previous RTT", "xmode.ping_previous_rtt", FT_UINT32, BASE_DEC, 
        NULL, 0x0, "Ping Previous RTT", HFILL }},
    { &hf_xmode_ping_latency,
      { "Ping Latency", "xmode.ping_latency", FT_UINT32, BASE_DEC, 
        NULL, 0x0, "Ping Latency", HFILL }},

    /* COP 0x15 Parameters */

    { &hf_xmode_cop_disconnect_reason,
      { "Disconnect Reason", "xmode.disconnect_reason", FT_UINT16, BASE_HEX, 
        VALS(xmode_cop_disconnect_reason_names), 0x0, "Disconnect Reason", HFILL }},
    { &hf_xmode_cop_connect_failure_code,
      { "Connect Failure Code", "xmode.cop_connect_failure_code", FT_UINT16, BASE_HEX, 
        VALS(xmode_cop_connect_failure_code_names), 0x0, "Connect Failure Code", HFILL }},

    /* COP 0x16 Parameters */
    { &hf_xmode_cop_connect_data_type,
      { "Connect Data Type", "xmode.cop_connect_data_type", FT_UINT8, BASE_DEC, 
        VALS(xmode_cop_connect_data_type_names), 0x0, "Connect Data Type", HFILL }},

    { &hf_xmode_string,
      { "Text", "xmode.text", FT_STRING, BASE_DEC, 
        NULL, 0x0, "Text", HFILL }},
    /*
    { &hf_xmode_xml,
      { "Xml", "xmode.xml", FT_STRING, BASE_DEC, 
        NULL, 0x0, "Xml", HFILL }},
    */

    /* bcp */
    { &hf_xmode_request_id,
      { "Request ID", "xmode.request_id", FT_UINT32, BASE_DEC, 
        NULL, 0x0, "Request ID", HFILL }},
    { &hf_xmode_status_code,
      { "Status Code", "xmode.status_code", FT_UINT16, BASE_HEX, 
        VALS(xmode_status_code_names), 0x0, "Status Code", HFILL }},
    { &hf_xmode_request_state,
      { "Request State", "xmode.request_state", FT_UINT16, BASE_DEC, 
        VALS(xmode_request_state_names), 0x0, "Request State", HFILL }},
    { &hf_xmode_completion_cause,
      { "Completion Cause", "xmode.completion_cause", FT_UINT16, BASE_DEC, 
        VALS(xmode_completion_cause_names), 0x0, "Completion Cause", HFILL }},

    { &hf_xmode_bcp_event,
      { "Event", "xmode.bcp_event", FT_UINT16, BASE_DEC, 
        VALS(xmode_bcp_event_names), 0x0, "Event", HFILL }},

    { &hf_xmode_bcp_tid,
      { "Transaction ID", "xmode.bcp_transaction_id", FT_UINT8, BASE_HEX, 
        NULL, 0x0, "Transaction ID", HFILL }},

    { &hf_xmode_bcp_timeout,
      { "Timeout", "xmode.bcp_timeout", FT_UINT32, BASE_DEC, 
        NULL, 0x0, "Timeout", HFILL }},

    /* vap & bcp */
    { &hf_xmode_audio_id,
      { "Audio ID", "xmode.audio_id", FT_UINT32, BASE_DEC, 
        NULL, 0x0, "Audio ID", HFILL }},

    /* vap */
    { &hf_xmode_audio_type_0x10,
      { "Audio Type", "xmode.audio_type", FT_UINT16, BASE_DEC, 
        VALS(xmode_audio_type_names_0x10), 0x0, "Audio Type", HFILL }},
    { &hf_xmode_audio_subtype,
      { "Audio Subtype", "xmode.audio_subtype", FT_UINT16, BASE_DEC, 
        NULL, 0x0, "Audio Subtype", HFILL }},
    { &hf_xmode_audio_type_0x11,
      { "Audio Type", "xmode.audio_type", FT_UINT16, BASE_DEC, 
        VALS(xmode_audio_type_names_0x11), 0x0, "Audio Type", HFILL }},
  };

  module_t *xmode_module;

  if (proto_xmode == -1) 
  {
    proto_xmode = proto_register_protocol (
      "XMode Protocol",    /* name */
      "XMode",    /* short name */
      "xmode"    /* abbrev */
      );
  }

  xmode_module = prefs_register_protocol(proto_xmode, proto_reg_handoff_xmode);
  proto_register_field_array(proto_xmode, hf, array_length(hf));
  proto_register_subtree_array(ett, array_length(ett));
}  


/*
 * plugin handoff
 */
void proto_reg_handoff_xmode(void)
{
  static int Initialized=FALSE;


  /* TODO: FIXME: add mulitple ports
   */

  if (!Initialized) {
    xmode_handle = create_dissector_handle(dissect_xmode, proto_xmode);
    dissector_add("tcp.port", global_xmode_port, xmode_handle);

    dissector_add("tcp.port", 8941, xmode_handle);
    dissector_add("tcp.port", 8961, xmode_handle);
    dissector_add("tcp.port", 8963, xmode_handle);
    dissector_add("tcp.port", 8973, xmode_handle);
    dissector_add("tcp.port", 8977, xmode_handle);
    dissector_add("tcp.port", 8971, xmode_handle);
    dissector_add("tcp.port", 2086, xmode_handle);
    dissector_add("tcp.port", 8911, xmode_handle);
  }
}  

static int count = 0;

#define FRAME_HEADER_LEN (6) 

/*
 * plugin dissection
 */
static void dissect_xmode(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{

  tcp_dissect_pdus(tvb, pinfo, tree, TRUE, FRAME_HEADER_LEN, get_xmode_message_len, dissect_xmode_message);
}

/*
 * dissect fully reassembled xmode messages 
 */
static void dissect_xmode_message(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree)
{
  /* for the info column data */
  guint protocol_type = 0; 
  proto_item* hdr_item = NULL;

  count = 0;

  /* Clear out stuff in the info column */
  if (check_col(pinfo->cinfo,COL_INFO))
  {
    col_clear(pinfo->cinfo,COL_INFO);
  }

  protocol_type = tvb_get_guint8(tvb, 0);

  /* only do VAP, BCP, COP and NMSP */
  if ( protocol_type != XMODE_VAP  && 
      protocol_type != XMODE_BCP && 
      protocol_type != XMODE_COP &&
      protocol_type != NMSP) return;

  if (check_col(pinfo->cinfo, COL_PROTOCOL)) 
  {
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "XMODE");
  }

  if (check_col(pinfo->cinfo, COL_INFO))
  {
    col_clear(pinfo->cinfo, COL_INFO);
  }
#if 0
  if (check_col(pinfo->cinfo, COL_INFO))
  {
    col_add_fstr(pinfo->cinfo, COL_INFO,
        val_to_str(protocol_type, protocol_type_names, "Unknown (0x%02x)"));
  }
#endif
  if (tree)
  {
    /* detailed dissection */
    proto_item* ti = NULL;
    proto_tree* xmode_tree = NULL;
    gint offset = 0;
    guint8 version;

    ti = proto_tree_add_item(tree, proto_xmode, tvb, 0, -1, FALSE);

    expert_add_info_format(pinfo, NULL, PI_SEQUENCE, PI_CHAT, "%s",
               val_to_str(protocol_type, protocol_type_names, "Unknown (0x%02x)"));

    xmode_tree = proto_item_add_subtree(ti, ett_xmode);

    // hdr_item = proto_tree_add_text(xmode_tree, tvb, offset, 11, "%s", "Hello World");
    /* expert_add_info_format(pinfo, NULLhdr_item, PI_SEQUENCE, PI_CHAT, "%s", "Hello World"); */

    /* loop here for many xmode messages in a tcp packet */
    while (tvb_length_remaining(tvb, offset) > 0)
    {
      /* protocol type*/
      proto_tree_add_item(xmode_tree, hf_xmode_protocol_type, tvb, offset, 1, FALSE); offset += 1;

      /* protocol version */
      version = tvb_get_guint8(tvb, offset);
      proto_tree_add_item(xmode_tree, hf_xmode_protocol_version, tvb, offset, 1, FALSE); offset += 1;

      switch(protocol_type) {
        case XMODE_COP:
          dissect_xmode_cop(tvb, &offset, pinfo, xmode_tree, version);
          break;
        case XMODE_BCP:
          dissect_xmode_bcp(tvb, &offset, pinfo, xmode_tree, version);
          break;
        case XMODE_VAP:
          dissect_xmode_vap(tvb, &offset, pinfo, xmode_tree, version);
          break;
        case NMSP:
          dissect_nmsp(tvb, &offset, pinfo, xmode_tree, version);
          break;
        default:
          break;
      }
      
#if 0
      {
        if (tvb_length_remaining(tvb, offset) > 0)
        {
          FILE* fp = fopen("debug.txt", "a");
          fprintf(fp, "VAP: %-4d remaining length of tvb:%d\n", ++count, tvb_length_remaining(tvb, offset));
          fprintf(fp, "\toffset:%d\n", offset);
          fclose(fp);
        }
      }
#endif
    }
  }
}  

/* 
 * determine PDU length of xmode message
 */
static guint get_xmode_message_len(packet_info* pinfo, tvbuff_t* tvb, int offset)
{
  // this works for those that are short
  // return tvb_get_letohs(tvb, offset + 1 + 1 + 2) + 6; /* skip P, V, cmd */
  // or we figure out the length based upon p and v
   guint8 p, v;
   guint8 header_len = 8;

   p = tvb_get_guint8(tvb, offset);
   v = tvb_get_guint8(tvb, offset + 1);

   if ( (p == XMODE_COP && v < 0x16) || 
       (p == XMODE_VAP && v < 0x12) ||
       (p == XMODE_BCP && v < 0x21)  )
   {
     // len is 16 bits (1 + 1 + 2 + 2)
     header_len = 6;
   }

  return tvb_get_letohs(tvb, offset + 1 + 1 + 2) + header_len; /* skip P, V, cmd */
}

/* 
 * dissect_xmode_cop
 */
static void dissect_xmode_cop(tvbuff_t *tvb, gint* offset, packet_info* pinfo, proto_tree *tree, guint8 version)
{
  guint32 payload_length = 0;
  guint16 command = 0;
  proto_tree* cop_payload_ti, * cop_payload_tree;

  if (tree)
  {
    /* protocol command */
    command = tvb_get_letohs(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_cop_command, tvb, *offset, 2, TRUE); *offset += 2;

    /* add protocol command to info column */
    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO,
          val_to_str(command, xmode_cop_command_names, "COP Unknown (0x%04x)"));
    }

    /* payload length */
    payload_length = tvb_get_letohl(tvb, *offset);

    if (version < 0x16) {
      proto_tree_add_item(tree, hf_xmode_payload_length, tvb, *offset, 2, TRUE); *offset +=2; 
    } else {
      proto_tree_add_item(tree, hf_xmode_payload_length_long, tvb, *offset, 4, TRUE); *offset +=4; 
    }

    {
      cop_payload_ti = proto_tree_add_item(tree, hf_xmode_payload, tvb, *offset, -1, FALSE);  

      /* set up the subtree for the contents */
      proto_item_set_text(cop_payload_ti, "Payload");
      cop_payload_tree = proto_item_add_subtree(cop_payload_ti, ett_xmode_cop);

      switch(command) {
      case COP_CONNECT:
        switch(version) {
          case 0x12:
            dissect_xmode_cop_connect_0x12(tvb, offset, pinfo, cop_payload_tree);
            break;
          case 0x13:
          case 0x14:
            dissect_xmode_cop_connect_0x13(tvb, offset, pinfo, cop_payload_tree);
            break;
          case 0x15:
            dissect_xmode_cop_connect_0x15(tvb, offset, pinfo, cop_payload_tree);
            break;
          case 0x16:
          default:
            dissect_xmode_cop_connect_0x16(tvb, offset, pinfo, cop_payload_tree);
            break;
        }
        break;
      case COP_CONNECTED:
        dissect_xmode_cop_connected(tvb, offset, pinfo, cop_payload_tree, version);
        break;
      case COP_DISCONNECT:
        dissect_xmode_cop_disconnect(tvb, offset, pinfo, cop_payload_tree, version);
        break;
      case COP_CONNECT_FAILED:
        dissect_xmode_cop_connect_failed(tvb, offset, pinfo, cop_payload_tree, version);
        break;
      case COP_PING_REQUEST:
        dissect_xmode_cop_ping_request(tvb, offset, pinfo, cop_payload_tree);
        break;
      case COP_PING_RESPONSE:
        dissect_xmode_cop_ping_response(tvb, offset, pinfo, cop_payload_tree);
        break;
      default:
        break;
      }
    }

  }
}

/* 
 * dissect_xmode_cop_connect_0x12
 */
static void dissect_xmode_cop_connect_0x12(tvbuff_t *tvb, gint* offset, packet_info* pinfo, proto_tree* tree)
{

  guint32 session_id = 0;
  guint16 device_type, codec_type;

  if (tree)
  {
    /* session id */
    session_id = tvb_get_letohl(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_session_id, tvb, *offset, 4, TRUE); *offset += 4; 

    /* device type */
    device_type = tvb_get_letohs(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_device_type, tvb, *offset, 2, TRUE); *offset += 2;

    /* sdk version */
    proto_tree_add_item(tree, hf_xmode_sdk_version, tvb, *offset, 2, TRUE); *offset += 2;

    /* to server codec type */
    codec_type = tvb_get_letohs(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_to_server_audio_type, tvb, *offset, 2, TRUE); *offset += 2;

    /* to server codec subtype */
    proto_tree_add_item(tree, hf_xmode_to_server_audio_subtype, tvb, *offset, 2, TRUE); *offset += 2;

    /* from server codec type */
    codec_type = tvb_get_letohs(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_from_server_audio_type, tvb, *offset, 2, TRUE); *offset += 2;

    /* from server codec sub type */
    proto_tree_add_item(tree, hf_xmode_from_server_audio_subtype, tvb, *offset, 2, TRUE); *offset += 2;
    
    /* from server audio buffer size */
    proto_tree_add_item(tree, hf_xmode_from_server_audio_buffer_size, tvb, *offset, 2, TRUE); *offset += 2;

    /* at this point offset is pointing to a char* in the tvb which is null terminated */
    proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, 
      tvb_length_remaining(tvb, *offset), FALSE); 

    /* FIXME - this needs to be computed fromt the message header payload length */
    *offset += tvb_length_remaining(tvb, *offset);

  }
}

/* 
 * dissect_xmode_cop_connect_0x13
 */
static void dissect_xmode_cop_connect_0x13(tvbuff_t *tvb, gint* offset, packet_info* pinfo, proto_tree* tree)
{
  guint32 length = 0;

  if (tree)
  {
    /* session id */
    length = tvb_get_letohl(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_session_id, tvb, *offset, 4, TRUE); *offset += 4; 

    /* at this point offset is pointing to a char* in the tvb which is null terminated */
    proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, tvb_length_remaining(tvb, *offset), FALSE); 
    /* FIXME: the length should be checked from message header payload length */
    *offset += tvb_length_remaining(tvb, *offset);
  }
}
/* 
 * dissect_xmode_cop_connect_0x15
 */
static void dissect_xmode_cop_connect_0x15(tvbuff_t *tvb, gint* offset, packet_info* pinfo, proto_tree* tree)
{

  if (tree)
  {
    /* this is the length of the data */
    proto_tree_add_item(tree, hf_xmode_length, tvb, *offset, 2, TRUE); *offset += 2;

    // then take the data - which may not be string -----
    // proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, tvb_length_remaining(tvb, *offset), FALSE); 
    proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, -1, FALSE); 

    // move the offset up
    *offset += tvb_length_remaining(tvb, *offset);
  }
}
/* 
 * dissect_xmode_cop_connect_0x16
 */
static void dissect_xmode_cop_connect_0x16(tvbuff_t *tvb, gint* offset, packet_info* pinfo, proto_tree* tree)
{
  guint32 length;

  if (tree)
  {
#if 1
    while (tvb_length_remaining(tvb, *offset) > 0)
    {
      /* length */
      length = tvb_get_letohl(tvb, *offset);
      proto_tree_add_item(tree, hf_xmode_length_long, tvb, *offset, 4, TRUE); *offset += 4;

      /* type*/
      proto_tree_add_item(tree, hf_xmode_cop_connect_data_type, tvb, *offset, 1, FALSE); *offset += 1;

      /* data */
      // proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, MIN(((gint) length), tvb_length_remaining(tvb, *offset)), FALSE); 
      proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, length-1, FALSE); 
      
      *offset +=  length - 1;
    }

#endif
  }
}

/* 
 * dissect_xmode_cop_connected
 */
static void dissect_xmode_cop_connected(tvbuff_t *tvb, gint* offset, packet_info* pinfo, 
    proto_tree* tree, unsigned char version)
{
  guint32 session_id = 0;

  if (tree)
  {

    if (version < 0x16)
    {
      session_id = tvb_get_letohl(tvb, *offset);
      proto_tree_add_item(tree, hf_xmode_session_id, tvb, *offset, 4, TRUE); *offset += 4;

      if (check_col(pinfo->cinfo, COL_INFO))
      {
        col_append_fstr(pinfo->cinfo, COL_INFO, " Session ID:%08x", session_id);
      }
    }
    else
    { 
      proto_tree_add_item(tree, hf_xmode_uuid, tvb, *offset, 16, TRUE); *offset += 16;
#if 0
      if (check_col(pinfo->cinfo, COL_INFO))
      {
        col_append_fstr(pinfo->cinfo, COL_INFO, " %s", session_id);
      }
#endif
    }

  
    if (version == 0x13) 
    {
      /* at this point offset is pointing to a char* in the tvb which is null terminated */
      proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, 
        tvb_length_remaining(tvb, *offset), FALSE); 
      /* FIXME: the length should be checked from message header payload length */
      *offset += tvb_length_remaining(tvb, *offset);
    }
    else if (version == 0x14)
    {
      /* offset is pointing to a char* in the tvb but it is not null terminated */
      proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, tvb_length_remaining(tvb, *offset) , FALSE); 
      /* FIXME: the length should be checked from message header payload length */
      *offset += tvb_length_remaining(tvb, *offset);
    }
    else if (version == 0x15)
    {
      /* this is the length of the data */
      proto_tree_add_item(tree, hf_xmode_length, tvb, *offset, 2, TRUE); *offset += 2;

      // then take the data - which may not be string -----
      proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, tvb_length_remaining(tvb, *offset), FALSE); 

      // move the offset up
      *offset += tvb_length_remaining(tvb, *offset);
    }
    else if (version == 0x16)
    {
      /* this is the length of the data */
      proto_tree_add_item(tree, hf_xmode_length_long, tvb, *offset, 4, TRUE); *offset += 4;

      // then take the data - which may not be string -----
      proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, tvb_length_remaining(tvb, *offset), FALSE); 

      // move the offset up
      *offset += tvb_length_remaining(tvb, *offset);
    }
  }
}

/* 
 * dissect_xmode_cop_disconnect
 */
static void dissect_xmode_cop_disconnect(tvbuff_t *tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version)
{
  guint16 reason = 0;
  guint32 payload_length = 0;
  proto_item* ti = NULL;

  if (tree)
  {
    reason = tvb_get_letohs(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_cop_disconnect_reason, tvb, *offset, 2, TRUE); *offset += 2;

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " %s",
          val_to_str(reason, xmode_cop_disconnect_reason_names, "Unknown (0x%02x)"));
    }
    if (version > 0x15 && tvb_length_remaining(tvb, *offset) > 0)
    {
      // length
      payload_length = tvb_get_letohl(tvb, *offset);
      proto_tree_add_item(tree, hf_xmode_length_long, tvb, *offset, 4, TRUE); *offset += 4;

      if (payload_length > 0) 
      {
        // payload
        proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, tvb_length_remaining(tvb, *offset), FALSE); 
        offset += tvb_length_remaining(tvb, *offset);
      }
    }
  }
}

/* 
 * dissect_xmode_cop_connect_failure
 */
static void dissect_xmode_cop_connect_failed(tvbuff_t *tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version)
{
  guint16 failure_code = 0;
  guint32 payload_length = 0;

  if (tree)
  {
    failure_code = tvb_get_letohs(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_cop_connect_failure_code, tvb, *offset, 2, TRUE); *offset += 2;

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " %s",
          val_to_str(failure_code, xmode_cop_connect_failure_code_names, "Unknown (0x%02x)"));
    }
    if (version > 0x15)

    {
      // length
      payload_length = tvb_get_letohl(tvb, *offset);
      proto_tree_add_item(tree, hf_xmode_length_long, tvb, *offset, 4, TRUE); *offset += 4;

      if (payload_length > 0) 
      {
        // payload
        proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, tvb_length_remaining(tvb, *offset), FALSE); 
        offset += tvb_length_remaining(tvb, *offset);
      }
    }
  }
}

/* 
 * dissect_xmode_cop_ping_request
 */
static void dissect_xmode_cop_ping_request(tvbuff_t *tvb, gint* offset, packet_info* pinfo, proto_tree* tree)
{
  guint32 request_id = 0;
  guint32 previous_rtt = 0;

  if (tree)
  {
    request_id = tvb_get_letohl(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_ping_request, tvb, *offset, 4, TRUE); *offset += 4;

    previous_rtt = tvb_get_letohl(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_ping_previous_rtt, tvb, *offset, 4, TRUE); *offset += 4;
  }
}

/* 
 * dissect_xmode_cop_ping_response
 */
static void dissect_xmode_cop_ping_response(tvbuff_t *tvb, gint* offset, packet_info* pinfo, proto_tree* tree)
{
  guint32 request_id = 0;
  guint32 latency = 0;

  if (tree)
  {
    request_id = tvb_get_letohl(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_ping_request, tvb, *offset, 4, TRUE); *offset += 4;

    latency = tvb_get_letohl(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_ping_latency, tvb, *offset, 4, TRUE); *offset += 4;

  }
}

/* 
 * dissect_xmode_bcp
 */
static void dissect_xmode_bcp(tvbuff_t *tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version)
{
  guint32 payload_length = 0;
  guint16 command = 0;

  proto_tree* bcp_payload_ti, * bcp_payload_tree;

  if (tree)
  {
    command = tvb_get_letohs(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_bcp_command, tvb, *offset, 2, TRUE); *offset += 2;

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO,
          val_to_str(command, xmode_bcp_command_names, "BCP Unknown (0x%04x)"));
    }

    if (version < 0x21)
    {
      /* payload length */
      payload_length = tvb_get_letohl(tvb, *offset);
      proto_tree_add_item(tree, hf_xmode_payload_length, tvb, *offset, 2, TRUE); *offset +=2;
    } else {
      /* payload length */
      payload_length = tvb_get_letohl(tvb, *offset);
      proto_tree_add_item(tree, hf_xmode_payload_length_long, tvb, *offset, 4, TRUE); *offset +=4;
    
    }


    {
      gint tvb_current_len;
      tvb_current_len = tvb_length_remaining(tvb, *offset);

      /* bcp_payload_ti = proto_tree_add_item(tree, hf_xmode_payload, tvb, *offset, tvb_current_len, FALSE); */
      /* bcp_payload_ti = proto_tree_add_item(tree, hf_xmode_payload, tvb, *offset, payload_length, FALSE); */

      /* uuid */
      proto_tree_add_item(tree, hf_xmode_uuid, tvb, *offset, 16, TRUE); *offset +=16;


      bcp_payload_ti = proto_tree_add_item(tree, hf_xmode_payload, tvb, *offset, 
          MIN(payload_length-16, ((guint32)tvb_current_len)), FALSE);

      /* set up the subtree for the contents */
      proto_item_set_text(bcp_payload_ti, "Payload");
      bcp_payload_tree = proto_item_add_subtree(bcp_payload_ti, ett_xmode_bcp);


      switch(command) {
      case BCP_RESPONSE:
        dissect_xmode_bcp_response(tvb, offset, pinfo, bcp_payload_tree, version);
        break;
      case BCP_GENERATE_AUDIO_COMPLETE:
        dissect_xmode_bcp_generate_audio_complete(tvb, offset, pinfo, bcp_payload_tree, version);
        break;
      case BCP_START_OF_SPEECH:
        dissect_xmode_bcp_with_request_id(tvb, offset, pinfo, bcp_payload_tree, version);
        break;
      case BCP_RECOGNITION_COMPLETE:
      case BCP_RECOGNITION_INTERMRESULTS:
        dissect_xmode_bcp_recognition(tvb, offset, pinfo, bcp_payload_tree, version);
        break;
      case BCP_DATA:
        dissect_xmode_bcp_data(tvb, offset, pinfo, bcp_payload_tree, version);
        break;
      case BCP_CANCEL:
        dissect_xmode_bcp_cancel(tvb, offset, pinfo, bcp_payload_tree, version);
        break;
      case BCP_CANCEL_RESPONSE:
        dissect_xmode_bcp_cancel_response(tvb, offset, pinfo, bcp_payload_tree, version);
        break;
      case BCP_GENERATE_AUDIO:
      case BCP_EXTENSION:
      case BCP_RECOGNIZE:
      case BCP_SET_PARAMS:
      case BCP_GET_PARAMS:
      case BCP_UNLOAD_ALL_GRAMMARS:
        dissect_xmode_bcp_xml(tvb, offset, pinfo, bcp_payload_tree, version);
        break;
      case BCP_GET_PARAMS_COMPLETE:
      case BCP_SET_PARAMS_COMPLETE:
        dissect_xmode_bcp_params_complete(tvb, offset, pinfo, bcp_payload_tree, version);
      case BCP_CONNECT:
        dissect_xmode_bcp_connect(tvb, offset, pinfo, bcp_payload_tree);
        break;
      case BCP_CONNECTED:
        dissect_xmode_bcp_connected(tvb, offset, pinfo, bcp_payload_tree);
        break;
      case BCP_CONNECT_FAILED:
        dissect_xmode_bcp_connect_failed(tvb, offset, pinfo, bcp_payload_tree);
        break;
      case BCP_DEVICE_DISCONNECT:
        dissect_xmode_bcp_device_disconnect(tvb, offset, pinfo, bcp_payload_tree);
        break;
      case BCP_BEGIN:
        dissect_xmode_bcp_begin(tvb, offset, pinfo, bcp_payload_tree);
        break;
      case BCP_END:
        dissect_xmode_bcp_end(tvb, offset, pinfo, bcp_payload_tree);
        break;
      case BCP_EVENT:
        dissect_xmode_bcp_event(tvb, offset, pinfo, bcp_payload_tree);
        break;
      case BCP_LOAD_RESOURCE:
        dissect_xmode_bcp_load_resource(tvb, offset, pinfo, bcp_payload_tree);
        break;
      case BCP_LOAD_GRAMMAR:
        dissect_xmode_bcp_load_grammar(tvb, offset, pinfo, bcp_payload_tree, version);
        break;
      default:
        break;
      }
    }

  }
}
/* 
 * dissect_xmode_bcp_recognition
 */
static void dissect_xmode_bcp_recognition(tvbuff_t *tvb, gint* offset, packet_info* pinfo,  proto_tree* tree, guint8 version)
{
  guint32 requestId;
  guint16 completion_cause;
  guint32 length;
  guint8 tid;

  proto_item* ti = NULL;
  if (tree)
  {
    if (0x21 < version)
    {
      /* tid */
      tid = tvb_get_guint8(tvb, *offset);

      proto_tree_add_item(tree, hf_xmode_bcp_tid, tvb, *offset, 1, TRUE); *offset += 1;

      if (check_col(pinfo->cinfo, COL_INFO))
      {
        col_append_fstr(pinfo->cinfo, COL_INFO, " Transaction ID: %d", tid);
      }
    }

    /* request Id */
    requestId = tvb_get_letohl(tvb, *offset);

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " Request ID:%d", requestId);
    }
    proto_tree_add_item(tree, hf_xmode_request_id, tvb, *offset, 4, TRUE); *offset += 4;

    /* completion cause */
    completion_cause = tvb_get_letohs(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_completion_cause, tvb, *offset, 2, TRUE); *offset += 2;

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " %s",
          val_to_str(completion_cause, xmode_completion_cause_names, "Unknown (0x%04x)"));
    }

    if (tvb_length_remaining(tvb, *offset) > 0)
    {
      if (version < 0x21)
      {
        /* this is the length of the data */
        length = tvb_get_letohs(tvb, *offset);
        proto_tree_add_item(tree, hf_xmode_length, tvb, *offset, 2, TRUE); 
        *offset += 2;
      } else {
        length = tvb_get_letohl(tvb, *offset);
        proto_tree_add_item(tree, hf_xmode_length, tvb, *offset, 4, TRUE); 
        *offset += 4;
      }

      proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, -1, FALSE); 
      *offset += tvb_length_remaining(tvb, *offset);

    }
  }
}

/* 
 * dissect_xmode_bcp_generate_audio_complete
 */
static void dissect_xmode_bcp_generate_audio_complete(tvbuff_t *tvb, gint* offset, packet_info* pinfo,  proto_tree* tree, guint8 version)
{
  guint32 requestId;
  guint32 audioId;
  guint8 tid;

  if (tree)
  {
    if (0x21 < version)
    {
      /* tid */
      tid = tvb_get_guint8(tvb, *offset);

      proto_tree_add_item(tree, hf_xmode_bcp_tid, tvb, *offset, 1, TRUE); *offset += 1;

      if (check_col(pinfo->cinfo, COL_INFO))
      {
        col_append_fstr(pinfo->cinfo, COL_INFO, " Transaction ID: %d", tid);
      }
    }

    /* request Id */
    requestId = tvb_get_letohl(tvb, *offset);

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " Request ID:%d", requestId);
    }
    proto_tree_add_item(tree, hf_xmode_request_id, tvb, *offset, 4, TRUE); *offset += 4;

    /* audio Id */
    audioId = tvb_get_letohl(tvb, *offset);

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " Audio ID:%d", audioId);
    }
    proto_tree_add_item(tree, hf_xmode_audio_id, tvb, *offset, 4, TRUE); *offset += 4;

    /* completion cause */
    proto_tree_add_item(tree, hf_xmode_completion_cause, tvb, *offset, 2, TRUE); *offset += 2;
  }
}

/* 
 * dissect_xmode_bcp_with_request_id
 */
static void dissect_xmode_bcp_with_request_id(tvbuff_t *tvb, gint* offset, packet_info* pinfo,  proto_tree* tree, guint8 version)
{
  guint32 requestId;
  guint8 tid;

  if (tree)
  {
    if (0x21 < version)
    {
      /* tid */
      tid = tvb_get_guint8(tvb, *offset);

      proto_tree_add_item(tree, hf_xmode_bcp_tid, tvb, *offset, 1, TRUE); *offset += 1;

      if (check_col(pinfo->cinfo, COL_INFO))
      {
        col_append_fstr(pinfo->cinfo, COL_INFO, " Transaction ID: %d", tid);
      }
    }

    /* request Id */
    requestId = tvb_get_letohl(tvb, *offset);

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " Request ID:%d", requestId);
    }
    proto_tree_add_item(tree, hf_xmode_request_id, tvb, *offset, 4, TRUE); *offset += 4;

    /* completion cause */
    proto_tree_add_item(tree, hf_xmode_completion_cause, tvb, *offset, 2, TRUE); *offset += 2;
  }
}

/* 
 * dissect_xmode_bcp_xml
 */
static void dissect_xmode_bcp_xml(tvbuff_t *tvb, gint* offset, packet_info* pinfo,  proto_tree* tree, guint8 version)
{
  guint32 length;
  guint8 tid;

  if (tree)
  {
    if (tvb_length_remaining(tvb, *offset) > 0)
    {
      if (0x21 < version)
      {
        /* tid */
        tid = tvb_get_guint8(tvb, *offset);

        proto_tree_add_item(tree, hf_xmode_bcp_tid, tvb, *offset, 1, TRUE); *offset += 1;

        if (check_col(pinfo->cinfo, COL_INFO))
        {
          col_append_fstr(pinfo->cinfo, COL_INFO, " Transaction ID: %d", tid);
        }
      }
      if (version < 0x21)
      {
        /* this is the length of the data */
        length = tvb_get_letohs(tvb, *offset);
        proto_tree_add_item(tree, hf_xmode_length, tvb, *offset, 2, TRUE); 
        *offset += 2;
      } else {
        length = tvb_get_letohl(tvb, *offset);
        proto_tree_add_item(tree, hf_xmode_length, tvb, *offset, 4, TRUE); 
        *offset += 4;
      }

      proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, -1, FALSE); 
      *offset += tvb_length_remaining(tvb, *offset);

    }
  }
}
/* 
 * dissect_xmode_bcp_load_grammar
 */
static void dissect_xmode_bcp_load_grammar(tvbuff_t *tvb, gint* offset, packet_info* pinfo,  proto_tree* tree, guint8 version)
{
  guint32 length;
  guint8 tid;

  if (tree)
  {
    if (tvb_length_remaining(tvb, *offset) > 0)
    {
      if (0x21 < version)
      {
        /* tid */
        tid = tvb_get_guint8(tvb, *offset);

        proto_tree_add_item(tree, hf_xmode_bcp_tid, tvb, *offset, 1, TRUE); *offset += 1;

        if (check_col(pinfo->cinfo, COL_INFO))
        {
          col_append_fstr(pinfo->cinfo, COL_INFO, " Transaction ID: %d", tid);
        }
      }
      if (version < 0x21)
      {
        /* this is the length of the data */
        length = tvb_get_letohs(tvb, *offset);

        proto_tree_add_item(tree, hf_xmode_length, tvb, *offset, 2, TRUE); 
        *offset += 2;

        /* payload */
        proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, -1, FALSE); 
        *offset += tvb_length_remaining(tvb, *offset);
      } else {
        // len of xml
        // xml
        // [len of bin
        // bin]*
        // 
        length = tvb_get_letohl(tvb, *offset);
        proto_tree_add_item(tree, hf_xmode_length, tvb, *offset, 4, TRUE); 
        *offset += 4;

        /* xml */        
        proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, length, FALSE); 
        *offset += length;

        // check remaining to read in long
        while  (tvb_length_remaining(tvb, *offset) > 3)
        {
          /* length again */
          length = tvb_get_letohl(tvb, *offset);
          proto_tree_add_item(tree, hf_xmode_length, tvb, *offset, 4, TRUE); 
          *offset += 4;

          // payload */
          if (tvb_length_remaining(tvb, *offset) >= (gint)length)
          {
            proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, length, FALSE); 
            *offset += length;
          }

        }

        if (tvb_length_remaining(tvb, *offset) > 0)
        {
          proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, -1, FALSE); 
        }

      }


    }
  }
}

/* 
 * dissect_xmode_bcp_data
 */
static void dissect_xmode_bcp_data(tvbuff_t *tvb, gint* offset,  packet_info* pinfo, proto_tree* tree, guint8 version)
{

  guint32 requestId;
  guint32 length;
  guint8 tid;

  if (tree)
  {
    if (0x21 < version)
    {
      /* tid */
      tid = tvb_get_guint8(tvb, *offset);

      proto_tree_add_item(tree, hf_xmode_bcp_tid, tvb, *offset, 1, TRUE); *offset += 1;

      if (check_col(pinfo->cinfo, COL_INFO))
      {
        col_append_fstr(pinfo->cinfo, COL_INFO, " Transaction ID: %d", tid);
      }
    }

    /* request Id */
    requestId = tvb_get_letohl(tvb, *offset);

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " Request ID:%d", requestId);
    }
    proto_tree_add_item(tree, hf_xmode_request_id, tvb, *offset, 4, TRUE); 
    *offset += 4;
    
    if (tvb_length_remaining(tvb, *offset) > 0)
    {
      if (version < 0x21)
      {
        /* this is the length of the data */
        length = tvb_get_letohs(tvb, *offset);
        proto_tree_add_item(tree, hf_xmode_length, tvb, *offset, 2, TRUE); 
        *offset += 2;
      } else {
        length = tvb_get_letohl(tvb, *offset);
        proto_tree_add_item(tree, hf_xmode_length, tvb, *offset, 4, TRUE); 
        *offset += 4;
      }

      proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, -1, FALSE); 
      *offset += tvb_length_remaining(tvb, *offset);

    }
  }
}

/* 
 * dissect_xmode_bcp_params_complete
 */
static void dissect_xmode_bcp_params_complete(tvbuff_t *tvb, gint* offset,  packet_info* pinfo, proto_tree* tree, guint8 version)
{
  guint32 requestId;
  guint16 statusCode;
  guint32 length;
  guint8 tid;

  if (tree)
  {
    if (0x21 < version)
    {
      /* tid */
      tid = tvb_get_guint8(tvb, *offset);

      proto_tree_add_item(tree, hf_xmode_bcp_tid, tvb, *offset, 1, TRUE); *offset += 1;

      if (check_col(pinfo->cinfo, COL_INFO))
      {
        col_append_fstr(pinfo->cinfo, COL_INFO, " Transaction ID: %d", tid);
      }
    }

    /* request Id */
    requestId = tvb_get_letohl(tvb, *offset);

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " Request ID:%d", requestId);
    }
    proto_tree_add_item(tree, hf_xmode_request_id, tvb, *offset, 4, TRUE); 
    *offset += 4;
    
    /* status code */
    statusCode = tvb_get_letohs(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_status_code, tvb, *offset, 2, TRUE); 
    *offset += 2;

    if (tvb_length_remaining(tvb, *offset) > 0)
    {
      if (version < 0x21)
      {
        /* this is the length of the data */
        length = tvb_get_letohs(tvb, *offset);
        proto_tree_add_item(tree, hf_xmode_length, tvb, *offset, 2, TRUE); 
        *offset += 2;
      } else {
        length = tvb_get_letohl(tvb, *offset);
        proto_tree_add_item(tree, hf_xmode_length, tvb, *offset, 4, TRUE); 
        *offset += 4;
      }

      proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, -1, FALSE); 
      *offset += tvb_length_remaining(tvb, *offset);

    }
  }
}
/* 
 * dissect_xmode_bcp_cancel
 */
static void dissect_xmode_bcp_cancel(tvbuff_t *tvb, gint* offset,  packet_info* pinfo, proto_tree* tree, guint8 version)
{

  guint32 requestId, c_requestId;
  guint8 tid;

  if (tree)
  {
    if (0x21 < version)
    {
      /* tid */
      tid = tvb_get_guint8(tvb, *offset);

      proto_tree_add_item(tree, hf_xmode_bcp_tid, tvb, *offset, 1, TRUE); *offset += 1;

      if (check_col(pinfo->cinfo, COL_INFO))
      {
        col_append_fstr(pinfo->cinfo, COL_INFO, " Transaction ID: %d", tid);
      }
    }

    /* request Id */
    requestId = tvb_get_letohl(tvb, *offset);

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " Request ID:%d", requestId);
    }
    proto_tree_add_item(tree, hf_xmode_request_id, tvb, *offset, 4, TRUE); 
    *offset += 4;

    /* request Id to cancel */
    c_requestId = tvb_get_letohl(tvb, *offset);

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " Cancel Request:%d", c_requestId);
    }
    proto_tree_add_item(tree, hf_xmode_request_id, tvb, *offset, 4, TRUE); 
    *offset += 4;
  }
}

/* 
 * dissect_xmode_bcp_response
 */
static void dissect_xmode_bcp_response(tvbuff_t *tvb, gint* offset,  packet_info* pinfo, proto_tree* tree, guint8 version)
{
  guint32 requestId;
  guint16 completion_cause;
  guint8 tid;

  if (tree)
  {
    if (0x21 < version)
    {
      /* tid */
      tid = tvb_get_guint8(tvb, *offset);

      proto_tree_add_item(tree, hf_xmode_bcp_tid, tvb, *offset, 1, TRUE); *offset += 1;

      if (check_col(pinfo->cinfo, COL_INFO))
      {
        col_append_fstr(pinfo->cinfo, COL_INFO, " Transaction ID: %d", tid);
      }
    }

    /* request Id */
    requestId = tvb_get_letohl(tvb, *offset);

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " Request ID:%d", requestId);
    }
    proto_tree_add_item(tree, hf_xmode_request_id, tvb, *offset, 4, TRUE); *offset += 4;

    /* status code */
    proto_tree_add_item(tree, hf_xmode_status_code, tvb, *offset, 2, TRUE); *offset += 2;

    /* request state */
    proto_tree_add_item(tree, hf_xmode_request_state, tvb, *offset, 2, TRUE); *offset += 2;

    /* completion cause */
    completion_cause = tvb_get_letohs(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_completion_cause, tvb, *offset, 2, TRUE); *offset += 2;

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " %s",
          val_to_str(completion_cause, xmode_completion_cause_names, "Unknown (0x%04x)"));
    }

  }
}
/* 
 * dissect_xmode_bcp_cancel_response
 */
static void dissect_xmode_bcp_cancel_response(tvbuff_t *tvb, gint* offset,  packet_info* pinfo, proto_tree* tree, guint8 version)
{
  guint32 requestId;
  guint16 completion_cause;
  guint8 tid;

  if (tree)
  {
    if (0x21 < version)
    {
      /* tid */
      tid = tvb_get_guint8(tvb, *offset);

      proto_tree_add_item(tree, hf_xmode_bcp_tid, tvb, *offset, 1, TRUE); *offset += 1;

      if (check_col(pinfo->cinfo, COL_INFO))
      {
        col_append_fstr(pinfo->cinfo, COL_INFO, " Transaction ID: %d", tid);
      }
    }

    /* request Id */
    requestId = tvb_get_letohl(tvb, *offset);

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " Request ID:%d", requestId);
    }
    proto_tree_add_item(tree, hf_xmode_request_id, tvb, *offset, 4, TRUE); *offset += 4;

    /* status code */
    proto_tree_add_item(tree, hf_xmode_status_code, tvb, *offset, 2, TRUE); *offset += 2;

    /* completion cause */
    completion_cause = tvb_get_letohs(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_completion_cause, tvb, *offset, 2, TRUE); *offset += 2;

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " %s",
          val_to_str(completion_cause, xmode_completion_cause_names, "Unknown (0x%04x)"));
    }

  }
}
/* 
 * dissect_xmode_bcp_connect
 */
static void dissect_xmode_bcp_connect(tvbuff_t *tvb, gint* offset,  packet_info* pinfo, proto_tree* tree)
{
  if (tree)
  {
    proto_tree_add_item(tree, hf_xmode_length_long, tvb, *offset, 4, TRUE); *offset += 4;

    if (tvb_length_remaining > 0)
    {
      proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, -1, FALSE); 
      *offset += tvb_length_remaining(tvb, *offset);
    }
  }
}
/* 
 * dissect_xmode_bcp_connected
 */
static void dissect_xmode_bcp_connected(tvbuff_t *tvb, gint* offset,  packet_info* pinfo, proto_tree* tree)
{
  guint32 length;

  if (tree)
  {
#if 0
    proto_tree_add_item(tree, hf_xmode_length_long, tvb, *offset, 4, TRUE); *offset += 4;

    if (tvb_length_remaining > 0)
    {
      proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, -1, FALSE); 
      *offset += tvb_length_remaining(tvb, *offset);
    }
#endif
    
    length = tvb_get_letohl(tvb, *offset);

    proto_tree_add_item(tree, hf_xmode_length_long, tvb, *offset, 4, TRUE); *offset += 4;

    if (tvb_length_remaining > 0 && length > 0)
    {
      proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, -1, FALSE); 
      *offset += tvb_length_remaining(tvb, *offset);
    }
  }
}
/* 
 * dissect_xmode_bcp_connet_failed
 */
static void dissect_xmode_bcp_connect_failed(tvbuff_t *tvb, gint* offset,  packet_info* pinfo, proto_tree* tree)
{
  guint32 length;
  if (tree)
  {
    length = tvb_get_letohl(tvb, *offset);

    proto_tree_add_item(tree, hf_xmode_length_long, tvb, *offset, 4, TRUE); *offset += 4;

    if (tvb_length_remaining > 0 && length > 0)
    {
      proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, -1, FALSE); 
      *offset += tvb_length_remaining(tvb, *offset);
    }
  }
}

/* 
 * dissect_xmode_bcp_pdx
 */
static void dissect_xmode_bcp_pdx(tvbuff_t *tvb, gint* offset,  packet_info* pinfo, proto_tree* tree)
{
  if (tree)
  {
    proto_tree_add_item(tree, hf_xmode_length_long, tvb, *offset, 4, TRUE); *offset += 4;

    if (tvb_length_remaining > 0)
    {
      proto_tree_add_item(tree, hf_xmode_string, tvb, *offset, -1, FALSE); 
      *offset += tvb_length_remaining(tvb, *offset);
    }
  }
}
/* 
 * dissect_xmode_bcp_device_disconnect
 */
static void dissect_xmode_bcp_device_disconnect(tvbuff_t *tvb, gint* offset,  packet_info* pinfo, proto_tree* tree)
{
  guint16 reason;

  if (tree)
  {
    reason = tvb_get_letohs(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_cop_disconnect_reason, tvb, *offset, 2, TRUE); *offset += 2;

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " %s",
          val_to_str(reason, xmode_cop_disconnect_reason_names, "Unknown (0x%02x)"));
    }

  }
}

/* 
 * dissect_xmode_bcp_begin
 */
static void dissect_xmode_bcp_begin(tvbuff_t *tvb, gint* offset,  packet_info* pinfo, proto_tree* tree)
{
  guint8 tid;

  // tid
  // payload len
  // payload

  if (tree)
  {
    tid = tvb_get_guint8(tvb, *offset);

    proto_tree_add_item(tree, hf_xmode_bcp_tid, tvb, *offset, 1, TRUE); *offset += 1;

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " Transaction ID: %d", tid);
      /* val_to_str(reason, xmode_cop_disconnect_reason_names, "Unknown (0x%02x)")); */
    }

    dissect_xmode_bcp_pdx(tvb, offset,  pinfo, tree);
  }
}

/* 
 * dissect_xmode_bcp_end
 */
static void dissect_xmode_bcp_end(tvbuff_t *tvb, gint* offset,  packet_info* pinfo, proto_tree* tree)
{
  guint8 tid;
  guint32 timeout;

  // tid
  // timeout (long)

  if (tree)
  {
    tid = tvb_get_guint8(tvb, *offset);

    proto_tree_add_item(tree, hf_xmode_bcp_tid, tvb, *offset, 1, TRUE); *offset += 1;

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " Transaction ID: %d", tid);
    }

    timeout = tvb_get_letohl(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_bcp_timeout, tvb, *offset, 4, TRUE); *offset += 4;

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " Timeout: %d", timeout);
    }
  }
}

/* 
 * dissect_xmode_bcp_event
 */
static void dissect_xmode_bcp_event(tvbuff_t *tvb, gint* offset,  packet_info* pinfo, proto_tree* tree)
{
  guint8 tid;
  guint16 event;

  // tid
  // timeout (long)

  if (tree)
  {
    tid = tvb_get_guint8(tvb, *offset);

    proto_tree_add_item(tree, hf_xmode_bcp_tid, tvb, *offset, 1, TRUE); *offset += 1;

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " Transaction ID: %d", tid);
    }

    event = tvb_get_letohs(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_bcp_event, tvb, *offset, 2, TRUE); *offset += 2;

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, 
          val_to_str(event, xmode_bcp_event_names, "Event Unknown (0x%04x)"));
      // " Timeout: %d", timeout);
    }
    dissect_xmode_bcp_pdx(tvb, offset,  pinfo, tree);
  }
}

/* 
 * dissect_xmode_bcp_load_resource
 */
static void dissect_xmode_bcp_load_resource(tvbuff_t *tvb, gint* offset,  packet_info* pinfo, proto_tree* tree)
{
  guint8 tid;

  if (tree)
  {
    tid = tvb_get_guint8(tvb, *offset);

    proto_tree_add_item(tree, hf_xmode_bcp_tid, tvb, *offset, 1, TRUE); *offset += 1;

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO, " Transaction ID: %d", tid);
    }

    dissect_xmode_bcp_pdx(tvb, offset,  pinfo, tree);
  }
}


/* 
 * dissect_xmode_vap
 */
static void dissect_xmode_vap(tvbuff_t *tvb, gint *offset, packet_info* pinfo, proto_tree* tree, guint8 version)
{
  guint32 payload_length = 0;
  guint16 command = 0;
  proto_tree* vap_payload_ti, * vap_payload_tree;

  if (tree)
  {
    command = tvb_get_letohs(tvb, *offset);

    proto_tree_add_item(tree, hf_xmode_vap_command, tvb, *offset, 2, TRUE); *offset += 2;

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_fstr(pinfo->cinfo, COL_INFO,
          val_to_str(command, xmode_vap_command_names, "VAP Unknown (0x%04x)"));
    }

    /* payload length */
    payload_length = tvb_get_letohl(tvb, *offset);
    if (version < 0x12)
    {
      proto_tree_add_item(tree, hf_xmode_payload_length, tvb, *offset, 2, TRUE); *offset +=2;
    }
    else
    {
        proto_tree_add_item(tree, hf_xmode_payload_length, tvb, *offset, 4, TRUE); *offset +=4;
    }

    {
      /* the current_len may be less than the payload_length */
      gint tvb_current_len;
      tvb_current_len = tvb_length_remaining(tvb, *offset);

      vap_payload_ti = proto_tree_add_item(tree, hf_xmode_payload, tvb, *offset, tvb_current_len, FALSE);

      /* set up the subtree for the contents */
      proto_item_set_text(vap_payload_ti, "Payload");
      vap_payload_tree = proto_item_add_subtree(vap_payload_ti, ett_xmode_vap);

      switch(command) {
      case VAP_RECORD_BEGIN:
      case VAP_PLAY_BEGIN:
        dissect_xmode_vap_begin(tvb, offset, pinfo, vap_payload_tree, version);
        break;
      case VAP_RECORD_END:
      case VAP_PLAY_END:
        dissect_xmode_vap_end(tvb, offset, pinfo, vap_payload_tree, version);
        break;
      case VAP_PLAY:
      case VAP_RECORD:
        dissect_xmode_vap_audio(tvb, offset, pinfo, vap_payload_tree, version);
        break;
      case VAP_BACKUP_REQUEST:
        dissect_xmode_vap_backup_request(tvb, offset, pinfo, vap_payload_tree);
        break;
      case VAP_RECOVER_REQUEST:
        dissect_xmode_vap_recover_request(tvb, offset, pinfo, vap_payload_tree);
        break;
      case VAP_BACKUP_RESPONSE:
        dissect_xmode_vap_backup_response(tvb, offset, pinfo, vap_payload_tree);
        break;
      case VAP_RECOVER_RESPONSE:
        dissect_xmode_vap_recover_response(tvb, offset, pinfo, vap_payload_tree);
        break;
      case VAP_RELEASE:
        dissect_xmode_vap_release(tvb, offset, pinfo, vap_payload_tree);
        break;
      default:
        break;
      }
    }
  }
}

/*
 * dissect_xmode_vap_begin
 */
void dissect_xmode_vap_begin(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version)
{
  guint32 audio_id = 0;

  if (tree)
  {
    if (version > 0x10) 
    {
      audio_id = tvb_get_letohl(tvb, *offset);
      if (check_col(pinfo->cinfo, COL_INFO))
      {
        col_append_fstr(pinfo->cinfo, COL_INFO, " Audio ID:%d", audio_id);
      }
      proto_tree_add_item(tree, hf_xmode_audio_id, tvb, *offset, 4, TRUE); *offset += 4;
      proto_tree_add_item(tree, hf_xmode_audio_type_0x11, tvb, *offset, 2, TRUE); *offset += 2;

    }
    else
    {
      proto_tree_add_item(tree, hf_xmode_audio_id, tvb, *offset, 4, TRUE); *offset += 4;
    }
  }
}
/*
 * dissect_xmode_vap_end
 */
void dissect_xmode_vap_end(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version)
{
  guint32 audio_id = 0;

  if (tree)
  {
    if (version > 0x10) 
    {
      audio_id = tvb_get_letohl(tvb, *offset);
      if (check_col(pinfo->cinfo, COL_INFO))
      {
        col_append_fstr(pinfo->cinfo, COL_INFO, " Audio ID:%d", audio_id);
      }
    }
    proto_tree_add_item(tree, hf_xmode_audio_id, tvb, *offset, 4, TRUE); *offset += 4;
  }
}
/*
 * dissect_xmode_vap_audio
 */
void dissect_xmode_vap_audio(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree, guint8 version)
{
  guint32 payload_length;
  guint32 current_len;
  guint32 audio_id = 0;
  
  if (tree)
  {
    if (version > 0x10)
    {
      audio_id = tvb_get_letohl(tvb, *offset);
      if (check_col(pinfo->cinfo, COL_INFO))
      {
        col_append_fstr(pinfo->cinfo, COL_INFO, " Audio ID:%d", audio_id);
      }
      proto_tree_add_item(tree, hf_xmode_audio_id, tvb, *offset, 4, TRUE); *offset +=4;

      if (version > 0x11)
      {

        payload_length = tvb_get_letohl(tvb, *offset);
        if (check_col(pinfo->cinfo, COL_INFO))
        {
          col_append_fstr(pinfo->cinfo, COL_INFO, " Length:%d", payload_length);
        }
        proto_tree_add_item(tree, hf_xmode_payload_length_long, tvb, *offset, 4, TRUE); *offset +=4;
      }
      else
      {
        payload_length = tvb_get_letohs(tvb, *offset);
        if (check_col(pinfo->cinfo, COL_INFO))
        {
          col_append_fstr(pinfo->cinfo, COL_INFO, " Length:%d", payload_length);
        }
        proto_tree_add_item(tree, hf_xmode_payload_length, tvb, *offset, 2, TRUE); *offset +=2;
      }
    }
    else
    {
      proto_tree_add_item(tree, hf_xmode_audio_type_0x10, tvb, *offset, 2, TRUE); *offset += 2;
      proto_tree_add_item(tree, hf_xmode_audio_subtype, tvb, *offset, 2, TRUE); *offset += 2;
      proto_tree_add_item(tree, hf_xmode_audio_id, tvb, *offset, 4, TRUE); *offset +=4;
      payload_length = tvb_get_letohs(tvb, *offset);
      proto_tree_add_item(tree, hf_xmode_payload_length, tvb, *offset, 2, TRUE); *offset +=2;
    }


    current_len = tvb_length_remaining(tvb, *offset);
    if ( current_len < (guint32) payload_length )
    {
      /* the vap audio didn't make it into this packet */
      {
        FILE* fp = fopen("debug.txt", "a");
        fprintf(fp, "Missing VAP: %-4d remaining length of tvb:%d\n", ++count, tvb_length_remaining(tvb, *offset));
        fprintf(fp, "\toffset:%d\n", *offset);
        fclose(fp);
      }
      proto_tree_add_item(tree, hf_xmode_payload, tvb, *offset, current_len, FALSE);
      *offset += current_len;
    }
    else if (current_len > (guint32)payload_length)
    {
      /* additional data after the vap audio */
      {
        FILE* fp = fopen("debug.txt", "a");
        fprintf(fp, "More Data: %-4d remaining length of tvb:%d\n", ++count, tvb_length_remaining(tvb, *offset));
        fprintf(fp, "\toffset:%d\n", *offset);
        fclose(fp);
      }
      proto_tree_add_item(tree, hf_xmode_payload, tvb, *offset, payload_length, FALSE);
      *offset += payload_length;
    }
    else
    {
      /* perfect fit */
      {
        FILE* fp = fopen("debug.txt", "a");
        fprintf(fp, "Perfect Fit: %-4d remaining length of tvb:%d\n", ++count, tvb_length_remaining(tvb, *offset));
        fprintf(fp, "\toffset:%d\n", *offset);
        fclose(fp);
      }
      proto_tree_add_item(tree, hf_xmode_payload, tvb, *offset, current_len, FALSE);
      *offset += current_len;
    }
  }
}
/*
 * dissect_xmode_vap_backup_request
 */
void dissect_xmode_vap_backup_request(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree)
{
  if (tree)
  {
     proto_tree_add_item(tree, hf_xmode_audio_id, tvb, *offset, 4, TRUE); *offset += 4;
  }
}

/*
 * dissect_xmode_vap_recover_request
 */
void dissect_xmode_vap_recover_request(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree)
{
  if (tree)
  {
    proto_tree_add_item(tree, hf_xmode_session_id, tvb, *offset, 4, TRUE); *offset += 4;
    proto_tree_add_item(tree, hf_xmode_audio_id, tvb, *offset, 4, TRUE); *offset += 4;
  }
}

/*
 * dissect_xmode_vap_backup_response
 */
void dissect_xmode_vap_backup_response(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree)
{
  guint16 payload_length;

  if (tree)
  {
    proto_tree_add_item(tree, hf_xmode_audio_id, tvb, *offset, 4, TRUE); *offset += 4;
    proto_tree_add_item(tree, hf_xmode_length, tvb, *offset, 2, TRUE); *offset += 2; /* bytes backedup */
    proto_tree_add_item(tree, hf_xmode_int16, tvb, *offset, 2, TRUE); *offset += 2; /* response code */

    payload_length = tvb_get_letohs(tvb, *offset); /* uri length */
    proto_tree_add_item(tree, hf_xmode_payload_length, tvb, *offset, 2, TRUE); *offset +=2;

    /* TODO: FIXME: check the remaining length has all of the uri */
    proto_tree_add_item(tree, hf_xmode_payload, tvb, *offset, payload_length, FALSE);
    *offset += payload_length;
  }
}

/*
 * dissect_xmode_vap_recover_response
 */
void dissect_xmode_vap_recover_response(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree)
{
  guint16 payload_length;

  if (tree)
  {
    proto_tree_add_item(tree, hf_xmode_session_id, tvb, *offset, 4, TRUE); *offset += 4;
    proto_tree_add_item(tree, hf_xmode_audio_id, tvb, *offset, 4, TRUE); *offset += 4;

    proto_tree_add_item(tree, hf_xmode_length, tvb, *offset, 2, TRUE); *offset += 2; /* bytes backedup */
    proto_tree_add_item(tree, hf_xmode_int16, tvb, *offset, 2, TRUE); *offset += 2; /* response code */

    payload_length = tvb_get_letohs(tvb, *offset); /* uri length */
    proto_tree_add_item(tree, hf_xmode_payload_length, tvb, *offset, 2, TRUE); *offset +=2;

    /* TODO: FIXME: check the remaining length has all of the uri */
    proto_tree_add_item(tree, hf_xmode_payload, tvb, *offset, payload_length, FALSE);
    *offset += payload_length;
  }
}

/*
 * dissect_xmode_vap_release
 */
void dissect_xmode_vap_release(tvbuff_t* tvb, gint* offset, packet_info* pinfo, proto_tree* tree)
{
  if (tree)
  {
    proto_tree_add_item(tree, hf_xmode_session_id, tvb, *offset, 4, TRUE); *offset += 4;
    proto_tree_add_item(tree, hf_xmode_audio_id, tvb, *offset, 4, TRUE); *offset += 4;
  }
}

/* 
 * dissect_nmsp
 */
static void dissect_nmsp(tvbuff_t *tvb, gint *offset, packet_info* pinfo, proto_tree* tree, guint8 version)
{
  guint32 payload_length = 0;
  guint16 command = 0;

  /* so far only the p is f, and nothing else */ 
  if (tree)
  {
    /* command should be zero */
    command = tvb_get_letohs(tvb, *offset);

    proto_tree_add_item(tree, hf_nmsp_command, tvb, *offset, 2, TRUE);
    *offset += 2;

    if (check_col(pinfo->cinfo, COL_INFO))
    {
      col_append_str(pinfo->cinfo, COL_INFO, "Handshake Packet");
      // col_add_str(pinfo->cinfo, COL_INFO, val_to_str(command, xmode_bcp_command_names, "Unknown (0x%04x)"));
    }


    /* payload length should be zero*/
    payload_length = tvb_get_letohl(tvb, *offset);
    proto_tree_add_item(tree, hf_xmode_payload_length_long, tvb, *offset, 4, TRUE); 
    *offset +=4;

  }
}

Windows DLL