From Yaniv Kaul: SPICE dissector
authorBill Meier <wmeier@newsguy.com>
Thu, 29 Sep 2011 18:10:14 +0000 (18:10 -0000)
committerBill Meier <wmeier@newsguy.com>
Thu, 29 Sep 2011 18:10:14 +0000 (18:10 -0000)
"It's not perfect, but has been in use for quite a while and dissects nicely most
of the messages."

https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=6373

svn path=/trunk/; revision=39184

AUTHORS
epan/CMakeLists.txt
epan/dissectors/Makefile.common
epan/dissectors/packet-spice.c [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
index 4ae661e67722f130f3b761c1651fe590ef593dee..ea13222ab87ae1767dfc1b1ca36e4ed67fde3097 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -410,7 +410,7 @@ Ralf Schneider          <ralf.schneider[AT]alcatel.se> {
        OSI ESIS support
 }
 
-Yaniv Kaul              <ykaul[AT]netvision.net.il> {
+Yaniv Kaul              <mykaul [AT] gmail.com> {
        Enhancements to ISAKMP
        CPHA support
        DCERPC OXID operation #5 dissection
@@ -421,6 +421,7 @@ Yaniv Kaul              <ykaul[AT]netvision.net.il> {
        VNC heuristic dissection
        TightVNC dissection
        More tags in the DAAP dissector
+       SPICE dissector
 }
 
 Paul Ionescu            <paul[AT]acorp.ro> {
@@ -3317,6 +3318,7 @@ Glenn Matthews            <glenn.matthews@cisco.com> {
        XMCP dissector
 }
 
+
 and by:
 
 Pavel Roskin            <proski [AT] gnu.org>
index 773df3dcefe55875ed52b931d33eea51ce82db40..ad3061752ff8b1544c1128990335868bbaf27da2 100644 (file)
@@ -986,6 +986,7 @@ set(DISSECTOR_SRC
        dissectors/packet-socketcan.c
        dissectors/packet-socks.c
        dissectors/packet-nasdaq-soup.c
+       dissectors/packet-spice.c
        dissectors/packet-spp.c
        dissectors/packet-spray.c
        dissectors/packet-srp.c
index 0a8c1b1b9aa4cd8c93d4a8fcc52104f2f0a74169..f813b978ae40254ba7e3578180a7cd8f53af3b4b 100644 (file)
@@ -906,6 +906,7 @@ DISSECTOR_SRC = \
        packet-sndcp-xid.c      \
        packet-socketcan.c      \
        packet-socks.c          \
+       packet-spice.c          \
        packet-spp.c            \
        packet-spray.c          \
        packet-srp.c            \
diff --git a/epan/dissectors/packet-spice.c b/epan/dissectors/packet-spice.c
new file mode 100644 (file)
index 0000000..f4db159
--- /dev/null
@@ -0,0 +1,3522 @@
+/* packet-spice.c
+ * Routines for Spice protocol dissection
+ * Copyright 2011, Yaniv Kaul <ykaul@redhat.com>
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can spiceistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * This code is based on the protocol specification:
+ *   http://www.spice-space.org/docs/spice_protocol.pdf
+ *   and the source - git://cgit.freedesktop.org/spice/spice-protocol
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glib.h>
+
+#include <epan/conversation.h>
+#include <epan/emem.h>
+#include <epan/packet.h>
+#include <epan/prefs.h>
+
+
+#define SPICE_MAGIC 0x52454451 /* = "REDQ" */
+
+#define SPICE_VERSION_MAJOR_1 1
+#define SPICE_VERSION_MINOR_0 0
+#define SPICE_VERSION_MAJOR_UNSTABLE 0xfffe
+#define SPICE_VERSION_MINOR_UNSTABLE 0xffff
+
+#define SPICE_TICKET_PUBKEY_BYTES 162
+
+#define SPICE_ALIGN(a, size) (((a) + ((size) - 1)) & ~((size) - 1))
+
+typedef enum {
+    SPICE_LINK_CLIENT,
+    SPICE_LINK_SERVER,
+
+    SPICE_TICKET_CLIENT,
+    SPICE_TICKET_SERVER,
+
+    SPICE_DATA
+} spice_session_state_e;
+
+static dissector_handle_t spice_handle;
+
+#define SPICE_CHANNEL_NONE      0
+#define SPICE_CHANNEL_MAIN      1
+#define SPICE_CHANNEL_DISPLAY   2
+#define SPICE_CHANNEL_INPUTS    3
+#define SPICE_CHANNEL_CURSOR    4
+#define SPICE_CHANNEL_PLAYBACK  5
+#define SPICE_CHANNEL_RECORD    6
+#define SPICE_CHANNEL_TUNNEL    7
+#define SPICE_CHANNEL_SMARTCARD 8
+
+static const value_string channel_types_vs[] = {
+    { SPICE_CHANNEL_NONE,      "Invalid" },
+    { SPICE_CHANNEL_MAIN,      "Main" },
+    { SPICE_CHANNEL_DISPLAY,   "Display" },
+    { SPICE_CHANNEL_INPUTS,    "Inputs" },
+    { SPICE_CHANNEL_CURSOR,    "Cursor" },
+    { SPICE_CHANNEL_PLAYBACK,  "Playback" },
+    { SPICE_CHANNEL_RECORD,    "Record" },
+    { SPICE_CHANNEL_TUNNEL,    "Tunnel" },
+    { SPICE_CHANNEL_SMARTCARD, "Smart Card" },
+    { 0,  NULL }
+};
+
+/* common server messages */
+#define SPICE_MIGRATE             1
+#define SPICE_MIGRATE_DATA        2
+#define SPICE_SET_ACK             3
+#define SPICE_PING                4
+#define SPICE_WAIT_FOR_CHANNELS   5
+#define SPICE_DISCONNECTING       6
+#define SPICE_NOTIFY              7
+
+/* common client messages */
+
+#define SPICEC_ACK_SYNC           1
+#define SPICEC_ACK                2
+#define SPICEC_PONG               3
+#define SPICEC_MIGRATE_FLUSH_MARK 4
+#define SPICEC_MIGRATE_DATA       5
+#define SPICEC_DISCONNECTING      6
+
+#define SPICE_FIRST_AVAIL_MESSAGE 101
+
+static const value_string common_server_message_types[] = {
+    { SPICE_MIGRATE,             "Server MIGRATE" },
+    { SPICE_MIGRATE_DATA,        "Server MIGRATE_DATA" },
+    { SPICE_SET_ACK,             "Server SET_ACK" },
+    { SPICE_PING,                "Server PING" },
+    { SPICE_WAIT_FOR_CHANNELS,   "Server WAIT_FOR_CHANNELS" },
+    { SPICE_DISCONNECTING,       "Server DISCONNECTING" },
+    { SPICE_NOTIFY,              "Server NOTIFY" },
+    { 0, NULL }
+};
+
+static const value_string common_client_message_types[] = {
+    { SPICEC_ACK_SYNC,           "Client ACK_SYNC" },
+    { SPICEC_ACK,                "Client ACK" },
+    { SPICEC_PONG,               "Client PONG" },
+    { SPICEC_MIGRATE_FLUSH_MARK, "Client MIGRATE_FLUSH_MARK" },
+    { SPICEC_MIGRATE_DATA,       "Client MIGRATE_DATA" },
+    { SPICEC_DISCONNECTING,      "Client DISCONNECTING" },
+    { 0, NULL }
+};
+
+#define sizeof_SpiceLinkHeader  16
+#define sizeof_SpiceDataHeader  18
+
+/* playback server channel messages */
+#define SPICE_PLAYBACK_DATA    101
+#define SPICE_PLAYBACK_MODE    102
+#define SPICE_PLAYBACK_START   103
+#define SPICE_PLAYBACK_STOP    104
+
+static const value_string playback_server_message_types[] = {
+    { SPICE_PLAYBACK_DATA,       "Server PLAYBACK_DATA" },
+    { SPICE_PLAYBACK_MODE,       "Server PLAYBACK_MODE" },
+    { SPICE_PLAYBACK_START,      "Server PLAYBACK_START" },
+    { SPICE_PLAYBACK_STOP,       "Server PLAYBACK_STOP" },
+    { 0, NULL }
+};
+
+static const value_string playback_mode_vals[] = {
+    { 0, "INVALID" },
+    { 1, "RAW" },
+    { 2, "CELT_0_5_1" },
+    { 0, NULL }
+};
+
+#define SPICE_PLAYBACK_CAP_CELT_0_5_1 1
+static const value_string playback_caps[] = {
+    { SPICE_PLAYBACK_CAP_CELT_0_5_1, "PLAYBACK_CAP_CELT_0_5_1" },
+    { 0, NULL }
+};
+/* main channel */
+
+/* main channel server messages */
+#define SPICE_MAIN_MIGRATE_BEGIN        101
+#define SPICE_MAIN_MIGRATE_CANCEL       102
+#define SPICE_MAIN_INIT                 103
+#define SPICE_MAIN_CHANNELS_LIST        104
+#define SPICE_MAIN_MOUSE_MODE           105
+#define SPICE_MAIN_MULTI_MEDIA_TIME     106
+#define SPICE_MAIN_AGENT_CONNECTED      107
+#define SPICE_MAIN_AGENT_DISCONNECTED   108
+#define SPICE_MAIN_AGENT_DATA           109
+#define SPICE_MAIN_AGENT_TOKEN          110
+
+static const value_string main_server_message_types[] = {
+    { SPICE_MAIN_MIGRATE_BEGIN,       "Server MIGRATE_BEGIN" },
+    { SPICE_MAIN_MIGRATE_CANCEL,      "Server MIGRATE_CANCEL" },
+    { SPICE_MAIN_INIT,                "Server INIT" },
+    { SPICE_MAIN_CHANNELS_LIST,       "Server CHANNELS_LIST" },
+    { SPICE_MAIN_MOUSE_MODE,          "Server MOUSE_MODE" },
+    { SPICE_MAIN_MULTI_MEDIA_TIME,    "Server MULTI_MEDIA_TIME" },
+    { SPICE_MAIN_AGENT_CONNECTED,     "Server AGENT_CONNECTED" },
+    { SPICE_MAIN_AGENT_DISCONNECTED,  "Server AGENT_DISCONNECTED" },
+    { SPICE_MAIN_AGENT_DATA,          "Server AGENT_DATA" },
+    { SPICE_MAIN_AGENT_TOKEN,         "Server AGENT_TOKEN" },
+    { 0, NULL }
+};
+
+/* main channel client messages */
+#define SPICEC_MAIN_RESERVED            101
+#define SPICEC_MAIN_MIGRATE_READY       102
+#define SPICEC_MAIN_MIGRATE_ERROR       103
+#define SPICEC_MAIN_ATTACH_CHANNELS     104
+#define SPICEC_MAIN_MOUSE_MODE_REQUEST  105
+#define SPICEC_MAIN_AGENT_START         106
+#define SPICEC_MAIN_AGENT_DATA          107
+#define SPICEC_MAIN_AGENT_TOKEN         108
+
+static const value_string main_client_message_types[] = {
+    { SPICEC_MAIN_RESERVED,           "Client RESERVED" },
+    { SPICEC_MAIN_MIGRATE_READY,      "Client MIGRATE_READY" },
+    { SPICEC_MAIN_MIGRATE_ERROR,      "Client MIGRATE_ERROR" },
+    { SPICEC_MAIN_ATTACH_CHANNELS,    "Client ATTACH_CHANNELS" },
+    { SPICEC_MAIN_MOUSE_MODE_REQUEST, "Client MOUSE_MODE_REQUEST" },
+    { SPICEC_MAIN_AGENT_START,        "Client AGENT_START" },
+    { SPICEC_MAIN_AGENT_DATA,         "Client AGENT_DATA" },
+    { SPICEC_MAIN_AGENT_TOKEN,        "Client AGENT_TOKEN" },
+    { 0, NULL }
+};
+
+#define VD_AGENT_MOUSE_STATE            1
+#define VD_AGENT_MONITORS_CONFIG        2
+#define VD_AGENT_REPLY                  3
+#define VD_AGENT_CLIPBOARD              4
+#define VD_AGENT_DISPLAY_CONFIG         5
+#define VD_AGENT_ANNOUNCE_CAPABILITIES  6
+#define VD_AGENT_CLIPBOARD_GRAB         7
+#define VD_AGENT_CLIPBOARD_REQUEST      8
+#define VD_AGENT_CLIPBOARD_RELEASE      9
+
+static const value_string agent_message_type[] = {
+    { VD_AGENT_MOUSE_STATE,           "VD_AGENT_MOUSE_STATE" },
+    { VD_AGENT_MONITORS_CONFIG,       "VD_AGENT_MONITORS_CONFIG" },
+    { VD_AGENT_REPLY,                 "VD_AGENT_REPLY" },
+    { VD_AGENT_CLIPBOARD,             "VD_AGENT_CLIPBOARD" },
+    { VD_AGENT_DISPLAY_CONFIG,        "VD_AGENT_DISPLAY_CONFIG" },
+    { VD_AGENT_ANNOUNCE_CAPABILITIES, "VD_AGENT_ANNOUNCE_CAPABILITIES" },
+    { VD_AGENT_CLIPBOARD_GRAB,        "VD_AGENT_CLIPBOARD_GRAB" },
+    { VD_AGENT_CLIPBOARD_REQUEST,     "VD_AGENT_CLIPBOARD_REQUEST" },
+    { VD_AGENT_CLIPBOARD_RELEASE,     "VD_AGENT_CLIPBOARD_RELEASE" },
+    { 0, NULL }
+};
+
+#define VD_AGENT_CLIPBOARD_NONE         0
+#define VD_AGENT_CLIPBOARD_UTF8_TEXT    1
+#define VD_AGENT_CLIPBOARD_IMAGE_PNG    2
+#define VD_AGENT_CLIPBOARD_IMAGE_BMP    3
+#define VD_AGENT_CLIPBOARD_IMAGE_TIFF   4
+#define VD_AGENT_CLIPBOARD_IMAGE_JPG    5
+
+static const value_string agent_clipboard_type[] = {
+    { VD_AGENT_CLIPBOARD_NONE,      "NONE" },
+    { VD_AGENT_CLIPBOARD_UTF8_TEXT, "UTF8_TEXT" },
+    { VD_AGENT_CLIPBOARD_IMAGE_PNG, "IMAGE_PNG" },
+    { VD_AGENT_CLIPBOARD_IMAGE_PNG, "IMAGE_BMP" },
+    { VD_AGENT_CLIPBOARD_IMAGE_PNG, "IMAGE_TIFF" },
+    { VD_AGENT_CLIPBOARD_IMAGE_PNG, "IMAGE_JPG" },
+    { 0, NULL }
+};
+/* record channel */
+/* record channel server messages */
+#define SPICE_RECORD_START            101
+#define SPICE_RECORD_STOP             102
+
+static const value_string record_server_message_types[] = {
+    { SPICE_RECORD_START, "Server RECORD_START" },
+    { SPICE_RECORD_STOP,  "Server RECORD_STOP" },
+    { 0, NULL }
+};
+
+/* record channel client messages */
+#define SPICEC_RECORD_DATA        101
+#define SPICEC_RECORD_MODE        102
+#define SPICEC_RECORD_START_MARK  103
+
+static const value_string record_client_message_types[] = {
+    { SPICEC_RECORD_DATA,       "Client RECORD_DATA" },
+    { SPICEC_RECORD_MODE,       "Client RECORD_MODE" },
+    { SPICEC_RECORD_START_MARK, "Client RECORD_START_MARK" },
+    { 0, NULL }
+};
+
+/* record channel capabilities - same as playback */
+static const value_string record_caps[] = {
+    { SPICE_PLAYBACK_CAP_CELT_0_5_1, "PLAYBACK_CAP_CELT_0_5_1" },
+    { 0, NULL }
+};
+
+/* display channel */
+/* display channel server messages */
+#define SPICE_DISPLAY_MODE                  101
+#define SPICE_DISPLAY_MARK                  102
+#define SPICE_DISPLAY_RESET                 103
+#define SPICE_DISPLAY_COPY_BITS             104
+#define SPICE_DISPLAY_INVAL_LIST            105
+#define SPICE_DISPLAY_INVAL_ALL_PIXMAPS     106
+#define SPICE_DISPLAY_INVAL_PALETTE         107
+#define SPICE_DISPLAY_INVAL_ALL_PALETTES    108
+#define SPICE_DISPLAY_STREAM_CREATE         122
+#define SPICE_DISPLAY_STREAM_DATA           123
+#define SPICE_DISPLAY_STREAM_CLIP           124
+#define SPICE_DISPLAY_STREAM_DESTROY        125
+#define SPICE_DISPLAY_STREAM_DESTROY_ALL    126
+#define SPICE_DISPLAY_DRAW_FILL             302
+#define SPICE_DISPLAY_DRAW_OPAQUE           303
+#define SPICE_DISPLAY_DRAW_COPY             304
+#define SPICE_DISPLAY_DRAW_BLEND            305
+#define SPICE_DISPLAY_DRAW_BLACKNESS        306
+#define SPICE_DISPLAY_DRAW_WHITENESS        307
+#define SPICE_DISPLAY_DRAW_INVERS           308
+#define SPICE_DISPLAY_DRAW_ROP3             309
+#define SPICE_DISPLAY_DRAW_STROKE           310
+#define SPICE_DISPLAY_DRAW_TEXT             311
+#define SPICE_DISPLAY_DRAW_TRANSPARENT      312
+#define SPICE_DISPLAY_DRAW_ALPHA_BLEND      313
+#define SPICE_DISPLAY_DRAW_SURFACE_CREATE   314
+#define SPICE_DISPLAY_DRAW_SURFACE_DESTROY  315
+
+static const value_string display_server_message_types[] = {
+    { SPICE_DISPLAY_MODE,                 "MODE" },
+    { SPICE_DISPLAY_MARK,                 "MARK" },
+    { SPICE_DISPLAY_RESET,                "RESET" },
+    { SPICE_DISPLAY_COPY_BITS,            "COPY_BITS" },
+    { SPICE_DISPLAY_INVAL_LIST,           "INVAL_LIST" },
+    { SPICE_DISPLAY_INVAL_ALL_PIXMAPS,    "INVAL_ALL_PIXMAPS" },
+    { SPICE_DISPLAY_INVAL_PALETTE,        "INVAL_PALETTE" },
+    { SPICE_DISPLAY_INVAL_ALL_PALETTES,   "INVAL_ALL_PALETTES" },
+    { SPICE_DISPLAY_STREAM_CREATE,        "STREAM_CREATE" },
+    { SPICE_DISPLAY_STREAM_DATA,          "STREAM_DATA"    },
+    { SPICE_DISPLAY_STREAM_CLIP,          "STREAM_CLIP"    },
+    { SPICE_DISPLAY_STREAM_DESTROY,       "STREAM_DESTROY"    },
+    { SPICE_DISPLAY_STREAM_DESTROY_ALL,   "STREAM_DESTROY_ALL"    },
+    { SPICE_DISPLAY_DRAW_FILL,            "DRAW_FILL"    },
+    { SPICE_DISPLAY_DRAW_OPAQUE,          "DRAW_OPAQUE" },
+    { SPICE_DISPLAY_DRAW_COPY,            "DRAW_COPY" },
+    { SPICE_DISPLAY_DRAW_BLEND,           "DRAW_BLEND" },
+    { SPICE_DISPLAY_DRAW_BLACKNESS,       "DRAW_BLACKNESS" },
+    { SPICE_DISPLAY_DRAW_WHITENESS,       "DRAW_WHITENESS" },
+    { SPICE_DISPLAY_DRAW_INVERS,          "DRAW_INVERS" },
+    { SPICE_DISPLAY_DRAW_ROP3,            "DRAW_ROP3" },
+    { SPICE_DISPLAY_DRAW_STROKE,          "DRAW_STROKE" },
+    { SPICE_DISPLAY_DRAW_TEXT,            "DRAW_TEXT" },
+    { SPICE_DISPLAY_DRAW_TRANSPARENT,     "DRAW_TRANSPARENT" },
+    { SPICE_DISPLAY_DRAW_ALPHA_BLEND,     "DRAW_ALPHA_BLEND" },
+    { SPICE_DISPLAY_DRAW_SURFACE_CREATE,  "DRAW_SURFACE_CREATE" },
+    { SPICE_DISPLAY_DRAW_SURFACE_DESTROY, "DRAW_SURFACE_DESTROY" },
+    { 0, NULL }
+};
+
+
+#define TOP_DOWN    1
+static const value_string stream_flags[] = {
+    { 0,        "None" },
+    { TOP_DOWN, "TOP DOWN" },
+    { 0, NULL }
+};
+
+#define MJPEG    1
+static const value_string stream_codec_types[] = {
+    { MJPEG, "MJPEG" },
+    { 0, NULL }
+};
+
+
+/* display channel client messages */
+#define SPICEC_DISPLAY_INIT    101
+static const value_string display_client_message_types[] = {
+    { SPICEC_DISPLAY_INIT, "Client DISPLAY INIT" },
+    { 0, NULL }
+};
+
+#define sizeof_RedcDisplayInit 14
+
+/* cursor channel */
+/* cursor channel server messages */
+#define SPICE_CURSOR_INIT         101
+#define SPICE_CURSOR_RESET        102
+#define SPICE_CURSOR_SET          103
+#define SPICE_CURSOR_MOVE         104
+#define SPICE_CURSOR_HIDE         105
+#define SPICE_CURSOR_TRAIL        106
+#define SPICE_CURSOR_INVAL_ONE    107
+#define SPICE_CURSOR_INVAL_ALL    108
+
+static const value_string cursor_visible_vs[] = {
+    { 1, "Visible" },
+    { 0, "Invisible" },
+    { 0, NULL }
+};
+
+static const value_string cursor_server_message_types[] = {
+    { SPICE_CURSOR_INIT,      "Server CURSOR_INIT" },
+    { SPICE_CURSOR_RESET,     "Server CURSOR_RESET" },
+    { SPICE_CURSOR_SET,       "Server CURSOR_SET" },
+    { SPICE_CURSOR_MOVE,      "Server CURSOR_MOVE" },
+    { SPICE_CURSOR_HIDE,      "Server CURSOR_HIDE" },
+    { SPICE_CURSOR_TRAIL,     "Server CURSOR_TRAIL" },
+    { SPICE_CURSOR_INVAL_ONE, "Server CURSOR_INVAL_ONE" },
+    { SPICE_CURSOR_INVAL_ALL, "Server CURSOR_INVAL_ALL" },
+    { 0, NULL }
+};
+
+/* cursor channel client messages */
+static const value_string cursor_client_message_types[] = {
+    { 0, NULL }
+};
+
+#define    SPICE_CURSOR_FLAGS_NONE       1
+#define    SPICE_CURSOR_FLAGS_CACHE_ME   2
+#define    SPICE_CURSOR_FLAGS_FROM_CACHE 4
+
+static const value_string cursor_flags_vs[] = {
+    { SPICE_CURSOR_FLAGS_NONE,       "NONE" },
+    { SPICE_CURSOR_FLAGS_CACHE_ME,   "CACHE_ME" },
+    { SPICE_CURSOR_FLAGS_FROM_CACHE, "FROM_CACHE" },
+    { 0, NULL }
+};
+
+#define SPICE_CURSOR_TYPE_ALPHA   0
+#define SPICE_CURSOR_TYPE_MONO    1
+#define SPICE_CURSOR_TYPE_COLOR4  2
+#define SPICE_CURSOR_TYPE_COLOR8  3
+#define SPICE_CURSOR_TYPE_COLOR16 4
+#define SPICE_CURSOR_TYPE_COLOR24 5
+#define SPICE_CURSOR_TYPE_COLOR32 6
+
+static const value_string cursor_type_vs[] = {
+    { SPICE_CURSOR_TYPE_ALPHA,   "CURSOR_TYPE_ALPHA" },
+    { SPICE_CURSOR_TYPE_MONO,    "CURSOR_TYPE_MONO" },
+    { SPICE_CURSOR_TYPE_COLOR4,  "CURSOR_TYPE_COLOR4" },
+    { SPICE_CURSOR_TYPE_COLOR8,  "CURSOR_TYPE_COLOR8" },
+    { SPICE_CURSOR_TYPE_COLOR16, "CURSOR_TYPE_COLOR16" },
+    { SPICE_CURSOR_TYPE_COLOR24, "CURSOR_TYPE_COLOR24" },
+    { SPICE_CURSOR_TYPE_COLOR32, "CURSOR_TYPE_COLOR32" },
+    { 0, NULL }
+};
+
+typedef struct {
+    guint64 unique;
+    guint8 type;
+    guint16 width;
+    guint16 height;
+    guint16 hot_spot_x;
+    guint16 hot_spot_y;
+} CursorHeader;
+
+#define sizeof_CursorHeader 17
+
+#define SPICE_MOUSE_MODE_SERVER 1
+#define SPICE_MOUSE_MODE_CLIENT 2
+
+static const value_string spice_mouse_modes_vs[] = {
+    { SPICE_MOUSE_MODE_SERVER, "Server mouse" },
+    { SPICE_MOUSE_MODE_CLIENT, "Client mouse" },
+    { 0, NULL }
+};
+
+static const value_string spice_agent_vs[] = {
+    { 0, "Disconnected" },
+    { 1, "Connected" },
+    { 0, NULL }
+};
+
+#define SPICE_NOTIFY_SEVERITY_INFO  0
+#define SPICE_NOTIFY_SEVERITY_WARN  1
+#define SPICE_NOTIFY_SEVERITY_ERROR 2
+
+static const value_string spice_severity_vs[] = {
+    { SPICE_NOTIFY_SEVERITY_INFO,  "Info" },
+    { SPICE_NOTIFY_SEVERITY_WARN,  "Warning" },
+    { SPICE_NOTIFY_SEVERITY_ERROR, "Error" },
+    { 0, NULL }
+};
+
+#define SPICE_NOTIFY_VISIBILITY_LOW    0
+#define SPICE_NOTIFY_VISIBILITY_MEDIUM 1
+#define SPICE_NOTIFY_VISIBILITY_HIGH   2
+
+static const value_string spice_visibility_vs[] = {
+    { SPICE_NOTIFY_VISIBILITY_LOW,    "Low visibility" },
+    { SPICE_NOTIFY_VISIBILITY_MEDIUM, "Medium visibility" },
+    { SPICE_NOTIFY_VISIBILITY_HIGH,   "High visibility" },
+    { 0, NULL }
+};
+
+static const value_string spice_error_codes_vs[] = {
+    { 0, "OK"                     },
+    { 1, "ERROR"                  },
+    { 2, "INVALID_MAGIC"          },
+    { 3, "INVALID_DATA"           },
+    { 4, "VERSION_MISMATCH"       },
+    { 5, "NEED_SECURED"           },
+    { 6, "NEED_UNSECURED"         },
+    { 7, "PERMISSION_DENIED"      },
+    { 8, "BAD_CONNECTION_ID"      },
+    { 9, "CHANNEL_NOT_AVAILABLE"  },
+    { 0,  NULL }
+};
+
+static const value_string spice_common_caps_vs[] = {
+    { 0, "SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION" },
+    { 1, "SPICE_COMMON_CAP_AUTH_SPICE" },
+    { 2, "SPICE_COMMON_CAP_AUTH_SASL" },
+    { 0, NULL }
+};
+
+/* Inputs channel */
+#define SPICEC_INPUTS_KEY_DOWN        101
+#define SPICEC_INPUTS_KEY_UP          102
+#define SPICEC_INPUTS_KEY_MODIFIERS   103
+#define SPICEC_INPUTS_MOUSE_MOTION    111
+#define SPICEC_INPUTS_MOUSE_POSITION  112
+#define SPICEC_INPUTS_MOUSE_PRESS     113
+#define SPICEC_INPUTS_MOUSE_RELEASE   114
+#define SPICE_INPUTS_INIT             101
+#define SPICE_INPUTS_KEY_MODIFIERS    102
+#define SPICE_INPUTS_MOUSE_MOTION_ACK 111
+
+static const value_string inputs_client_message_types[] = {
+    { SPICEC_INPUTS_KEY_DOWN,        "Client KEY_DOWN" },
+    { SPICEC_INPUTS_KEY_UP,          "Client INPUTS_KEY_UP" },
+    { SPICEC_INPUTS_KEY_MODIFIERS,   "Client KEY_MODIFIERS" },
+    { SPICEC_INPUTS_MOUSE_MOTION,    "Client MOUSE_MOTION" },
+    { SPICEC_INPUTS_MOUSE_POSITION,  "Client MOUSE_POSITION" },
+    { SPICEC_INPUTS_MOUSE_PRESS,     "Client MOUSE_PRESS" },
+    { SPICEC_INPUTS_MOUSE_RELEASE,   "Client MOUSE_RELEASE" },
+    { 0, NULL }
+};
+
+static const value_string inputs_server_message_types[] = {
+    { SPICE_INPUTS_INIT,             "Server INPUTS_INIT" },
+    { SPICE_INPUTS_KEY_MODIFIERS,    "Server KEY_MODIFIERS" },
+    { SPICE_INPUTS_MOUSE_MOTION_ACK, "Server MOUSE_MOTION_ACK" },
+    { 0, NULL }
+};
+
+#define SPICE_SCROLL_LOCK_MODIFIER 1
+#define SPICE_NUM_LOCK_MODIFIER    2
+#define SPICE_CAPS_LOCK_MODIFIER   4
+
+static const value_string input_modifiers_types[] = {
+    { 0, "None" },
+    { SPICE_SCROLL_LOCK_MODIFIER, "Scroll lock" },
+    { SPICE_NUM_LOCK_MODIFIER,    "Num lock" },
+    { SPICE_CAPS_LOCK_MODIFIER,   "CAPS lock" },
+    { 0, NULL }
+};
+
+/* This structure will be tied to each conversation. */
+typedef struct {
+    guint32 connection_id;
+    guint32 num_channel_caps;
+    guint32 destport;
+    spice_session_state_e next_state;
+    guint8 channel_type;
+    guint8 channel_id;
+} spice_conversation_t;
+
+typedef struct {
+    spice_session_state_e state;
+} spice_packet_t;
+
+typedef struct {
+    gint32 left;
+    gint32 top;
+    gint32 right;
+    gint32 bottom;
+} SpiceRect;
+
+#define sizeof_SpiceRect 16
+#define CLIP_TYPE_NONE    0
+#define CLIP_TYPE_RECTS   1
+
+static const value_string clip_types_vs[] = {
+    { CLIP_TYPE_NONE,  "NONE" },
+    { CLIP_TYPE_RECTS, "RECTS" },
+    { 0, NULL }
+};
+
+typedef struct {
+    guint8 type;
+} Clip;
+#define sizeof_Clip 1 /* This is correct only if the type is none. If it is RECTS, this is followed by: */
+
+typedef struct {
+    guint32 num_rects; /* this is followed by RECT rects[num_rects] */
+} ClipRects;
+
+
+typedef struct {
+    guint32 surface_id;
+    SpiceRect bounding_box;
+    Clip clip;
+} DisplayBase;
+
+#define sizeof_DisplayBase 21 /* size without a rect list in the Clip */
+
+typedef struct {
+    gint32 x;
+    gint32 y;
+} POINT;
+
+#define sizeof_POINT 8
+
+typedef struct {
+    gint16 x;
+    gint16 y;
+} POINT16;
+
+#define sizeof_POINT16 4
+
+#define    SPICE_BRUSH_TYPE_NONE 0
+#define    SPICE_BRUSH_TYPE_SOLID 1
+#define    SPICE_BRUSH_TYPE_PATTERN 2
+
+static const value_string brush_types_vs[] = {
+    { SPICE_BRUSH_TYPE_NONE,    "NONE" },
+    { SPICE_BRUSH_TYPE_SOLID,   "SOLID" },
+    { SPICE_BRUSH_TYPE_PATTERN, "PATTERN" },
+    { 0, NULL }
+};
+
+typedef struct {
+    guint64 image;
+    POINT position;
+} Pattern;
+
+#define sizeof_Pattern 16
+
+typedef struct {
+    guint8 type;
+    union {
+        guint32 color;
+        Pattern patten;
+    };
+} Brush;
+
+#define sizeof_Brush 17 /* for pattern */
+
+static const value_string Mask_flags_vs[] = {
+    { 0, "MASK_FLAG_INVERS" },
+    { 0, NULL }
+};
+
+typedef struct {
+    guint8 flags;
+    POINT position;
+    guint32 bitmap;
+} Mask;
+
+#define sizeof_Mask 13
+
+static const value_string scale_mode_vs[] = {
+    { 0, "IMAGE_SCALE_INTERPOLATE" },
+    { 1, "IMAGE_SCALE_NEAREST" },
+    { 0, NULL }
+};
+
+typedef struct {
+    guint64 id;
+    guint8    type;
+    guint8    flag;
+    guint32    width;
+    guint32 height;
+} ImageDescriptor;
+
+#define sizeof_ImageDescriptor 18
+
+#define IMAGE_TYPE_BITMAP                 0
+#define IMAGE_TYPE_QUIC                   1
+#define IMAGE_TYPE_RESERVED               2
+#define IMAGE_TYPE_LZ_PLT               100
+#define IMAGE_TYPE_LZ_RGB               101
+#define IMAGE_TYPE_GLZ_RGB              102
+#define IMAGE_TYPE_FROM_CACHE           103
+#define IMAGE_TYPE_SURFACE              104
+#define IMAGE_TYPE_JPEG                 105
+#define IMAGE_TYPE_FROM_CACHE_LOSSLESS  106
+#define IMAGE_TYPE_ZLIB_GLZ_RGB         107
+#define IMAGE_TYPE_JPEG_ALPHA           108
+
+static const value_string image_type_vs[] = {
+    { IMAGE_TYPE_BITMAP,              "BITMAP" },
+    { IMAGE_TYPE_QUIC,                "QUIC" },
+    { IMAGE_TYPE_LZ_PLT,              "LZ_PLT" },
+    { IMAGE_TYPE_LZ_RGB,              "LZ_RGB" },
+    { IMAGE_TYPE_GLZ_RGB,             "GLZ_RGB" },
+    { IMAGE_TYPE_FROM_CACHE,          "FROM_CACHE" },
+    { IMAGE_TYPE_SURFACE,             "SURFACE" },
+    { IMAGE_TYPE_JPEG,                "JPEG" },
+    { IMAGE_TYPE_FROM_CACHE_LOSSLESS, "FROM_CACHE_LOSSLESS" },
+    { IMAGE_TYPE_ZLIB_GLZ_RGB,        "ZLIB_GLZ_RGB" },
+    { IMAGE_TYPE_JPEG_ALPHA,          "JPEG_ALPHA" },
+    { 0, NULL }
+};
+
+/* FIXME - those flags should be bit-wise, I guess! */
+#define IMAGE_FLAGS_CACHE_ME    (1 << 0)
+#define IMAGE_FLAGS_HIGH_BITS_SET    (1 << 1)
+#define IMAGE_FLAGS_REPLACE_ME    (1 << 2)
+static const value_string image_flags_vs[] = {
+    { 0,                         "None" },
+    { IMAGE_FLAGS_CACHE_ME,      "CACHE_ME" },
+    { IMAGE_FLAGS_HIGH_BITS_SET, "HIGH_BITS_SET" },
+    { IMAGE_FLAGS_REPLACE_ME,    "REPLACE_ME" },
+    { 0, NULL }
+};
+
+static const value_string rop_descriptor_vs[] = {
+    { (1 << 0), "SPICE_ROPD_INVERS_SRC" },
+    { (1 << 1), "SPICE_ROPD_INVERS_BRUSH" },
+    { (1 << 2), "SPICE_ROPD_INVERS_DEST" },
+    { (1 << 3), "SPICE_ROPD_OP_PUT" },
+    { (1 << 4), "SPICE_ROPD_OP_OR" },
+    { (1 << 5), "SPICE_ROPD_OP_AND" },
+    { (1 << 6), "SPICE_ROPD_OP_XOR" },
+    { (1 << 7), "SPICE_ROPD_OP_BLACKNESS" },
+    { (1 << 8), "SPICE_ROPD_OP_WHITENESS" },
+    { (1 << 9), "SPICE_ROPD_OP_INVERS" },
+    { (1 << 10), "SPICE_ROPD_INVERS_RES" },
+    { 0, NULL }
+};
+
+#define QUIC_IMAGE_TYPE_INVALID 0
+#define QUIC_IMAGE_TYPE_GRAY 1
+#define QUIC_IMAGE_TYPE_RGB16 2
+#define QUIC_IMAGE_TYPE_RGB24 3
+#define QUIC_IMAGE_TYPE_RGB32 4
+#define QUIC_IMAGE_TYPE_RGBA 5
+
+static const value_string quic_type_vs[] = {
+    { QUIC_IMAGE_TYPE_INVALID, "INVALID" },
+    { QUIC_IMAGE_TYPE_GRAY,    "GRAY" },
+    { QUIC_IMAGE_TYPE_RGB16,   "RGB16" },
+    { QUIC_IMAGE_TYPE_RGB24,   "RGB24" },
+    { QUIC_IMAGE_TYPE_RGB32,   "RGB32" },
+    { QUIC_IMAGE_TYPE_RGBA,    "RGBA" },
+    { 0, NULL }
+};
+
+#define LZ_IMAGE_TYPE_INVALID  0
+#define LZ_IMAGE_TYPE_PLT1_LE  1
+#define LZ_IMAGE_TYPE_PLT1_BE  2      /* PLT stands for palette */
+#define LZ_IMAGE_TYPE_PLT4_LE  3
+#define LZ_IMAGE_TYPE_PLT4_BE  4
+#define LZ_IMAGE_TYPE_PLT8     5
+#define LZ_IMAGE_TYPE_RGB16    6
+#define LZ_IMAGE_TYPE_RGB24    7
+#define LZ_IMAGE_TYPE_RGB32    8
+#define LZ_IMAGE_TYPE_RGBA     9
+#define LZ_IMAGE_TYPE_XXXA    10
+static const value_string LzImage_type_vs[] = {
+    { LZ_IMAGE_TYPE_INVALID, "INVALID" },
+    { LZ_IMAGE_TYPE_PLT1_LE, "PLT1_LE" },
+    { LZ_IMAGE_TYPE_PLT1_BE, "PLT1_BE" },
+    { LZ_IMAGE_TYPE_PLT4_LE, "PLT4_LE" },
+    { LZ_IMAGE_TYPE_PLT4_BE, "PLT4_BE" },
+    { LZ_IMAGE_TYPE_PLT8,    "PLT8" },
+    { LZ_IMAGE_TYPE_RGB16,   "RGB16" },
+    { LZ_IMAGE_TYPE_RGB24,   "RGB24" },
+    { LZ_IMAGE_TYPE_RGB32,   "RGB32" },
+    { LZ_IMAGE_TYPE_RGBA,    "RGBA" },
+    { LZ_IMAGE_TYPE_XXXA,    "RGB JPEG, Alpha LZ" },
+    { 0, NULL }
+};
+
+#define PIXMAP_FORMAT_1BIT_LE 1
+#define PIXMAP_FORMAT_1BIT_BE 2
+#define PIXMAP_FORMAT_4BIT_LE 3
+#define PIXMAP_FORMAT_4BIT_BE 4
+#define PIXMAP_FORMAT_8BIT    5
+#define PIXMAP_FORMAT_16BIT   6
+#define PIXMAP_FORMAT_24BIT   7
+#define PIXMAP_FORMAT_32BIT   8
+#define PIXMAP_FORMAT_RGBA    9
+
+static const value_string Pixmap_types_vs[] = {
+    { PIXMAP_FORMAT_1BIT_LE, "1BIT_LE" },
+    { PIXMAP_FORMAT_1BIT_BE, "1BIT_BE" },
+    { PIXMAP_FORMAT_4BIT_LE, "4BIT_LE" },
+    { PIXMAP_FORMAT_4BIT_BE, "4BIT_BE" },
+    { PIXMAP_FORMAT_8BIT,    "8BIT" },
+    { PIXMAP_FORMAT_16BIT,   "16BIT" },
+    { PIXMAP_FORMAT_24BIT,   "24BIT" },
+    { PIXMAP_FORMAT_32BIT,   "32BIT" },
+    { PIXMAP_FORMAT_RGBA,    "RGBA" },
+    { 0, NULL }
+};
+
+/* desegmentation of spice protocol */
+static gboolean spice_desegment = TRUE;
+
+static gint ett_spice = -1;
+static gint ett_link_client = -1;
+static gint ett_link_server = -1;
+static gint ett_link_caps = -1;
+static gint ett_data = -1;
+static gint ett_message = -1;
+static gint ett_ticket_client = -1;
+static gint ett_ticket_server = -1;
+static gint ett_playback = -1;
+static gint ett_display_client = -1;
+static gint ett_display_server = -1;
+static gint ett_common_server_message = -1;
+static gint ett_common_client_message = -1;
+static gint ett_point = -1;
+static gint ett_point16 = -1;
+static gint ett_cursor = -1;
+static gint ett_spice_main = -1;
+static gint ett_rect = -1;
+static gint ett_DisplayBase = -1;
+static gint ett_Clip = -1;
+static gint ett_Mask = -1;
+static gint ett_imagedesc = -1;
+static gint ett_imageQuic = -1;
+static gint ett_GLZ_RGB = -1;
+static gint ett_LZ_RGB = -1;
+static gint ett_LZ_PLT = -1;
+static gint ett_ZLIB_GLZ = -1;
+static gint ett_Uncomp_tree = -1;
+static gint ett_LZ_JPEG = -1;
+static gint ett_JPEG = -1;
+static gint ett_cursor_header = -1;
+static gint ett_RedCursor = -1;
+static gint ett_pattern = -1;
+static gint ett_brush = -1;
+static gint ett_Pixmap = -1;
+static gint ett_inputs_client = -1;
+static gint ett_rectlist = -1;
+static gint ett_inputs_server = -1;
+static gint ett_record_client = -1;
+static gint ett_main_client = -1;
+static gint ett_spice_agent = -1;
+static int proto_spice = -1;
+static int hf_spice_magic  = -1;
+static int hf_major_version  = -1;
+static int hf_minor_version  = -1;
+static int hf_message_size  = -1;
+static int hf_conn_id  = -1;
+static int hf_channel_type  = -1;
+static int hf_channel_id  = -1;
+static int hf_num_common_caps  = -1;
+static int hf_num_channel_caps  = -1;
+static int hf_caps_offset  = -1;
+static int hf_error_code  = -1;
+static int hf_data = -1;
+static int hf_serial = -1;
+static int hf_data_size = -1;
+static int hf_data_sublist = -1;
+static int hf_link_client = -1;
+static int hf_link_server = -1;
+static int hf_ticket_client = -1;
+static int hf_ticket_server = -1;
+static int hf_main_cap = -1;
+static int hf_display_cap = -1;
+static int hf_inputs_cap = -1;
+static int hf_cursor_cap = -1;
+static int hf_record_cap = -1;
+static int hf_common_cap = -1;
+static int hf_playback_cap = -1;
+static int hf_playback_record_mode_timstamp = -1;
+static int hf_playback_record_mode = -1;
+static int hf_red_set_ack_generation = -1;
+static int hf_red_set_ack_window = -1;
+static int hf_Clip_type = -1;
+static int hf_Mask_flag = -1;
+static int hf_Mask_bitmap = -1;
+static int hf_display_rop_descriptor = -1;
+static int hf_display_scale_mode = -1;
+static int hf_display_stream_id = -1;
+static int hf_display_stream_width = -1;
+static int hf_display_stream_height = -1;
+static int hf_display_stream_src_width = -1;
+static int hf_display_stream_src_height = -1;
+static int hf_display_stream_data_size = -1;
+static int hf_display_stream_codec_type = -1;
+static int hf_display_stream_flags = -1;
+static int hf_red_ping_id = -1;
+static int hf_red_timestamp = -1;
+static int hf_spice_display_mode_width = -1;
+static int hf_spice_display_mode_height = -1;
+static int hf_spice_display_mode_depth = -1;
+static int hf_image_desc_id = -1;
+static int hf_image_desc_type = -1;
+static int hf_image_desc_flags = -1;
+static int hf_image_desc_width = -1;
+static int hf_image_desc_height = -1;
+static int hf_quic_width = -1;
+static int hf_quic_height = -1;
+static int hf_quic_major_version  = -1;
+static int hf_quic_minor_version  = -1;
+static int hf_quic_type = -1;
+static int hf_GLZ_RGB_width = -1;
+static int hf_GLZ_RGB_height = -1;
+static int hf_LZ_major_version  = -1;
+static int hf_LZ_minor_version  = -1;
+static int hf_GLZ_RGB_type = -1;
+static int hf_GLZ_RGB_stride = -1;
+static int hf_GLZ_RGB_dict_id = -1;
+static int hf_cursor_trail_len = -1;
+static int hf_cursor_trail_freq = -1;
+static int hf_cursor_trail_visible = -1;
+static int hf_cursor_unique = -1;
+static int hf_cursor_type = -1;
+static int hf_cursor_width = -1;
+static int hf_cursor_height = -1;
+static int hf_cursor_hotspot_x = -1;
+static int hf_cursor_hotspot_y = -1;
+static int hf_cursor_flags = -1;
+static int hf_cursor_id = -1;
+static int hf_spice_display_init_cache_id = -1;
+static int hf_spice_display_init_cache_size = -1;
+static int hf_spice_display_init_glz_dict_id = -1;
+static int hf_spice_display_init_dict_window_size = -1;
+static int hf_brush_type = -1;
+static int hf_brush_rgb = -1;
+static int hf_pixmap_width = -1;
+static int hf_pixmap_height = -1;
+static int hf_pixmap_stride = -1;
+static int hf_pixmap_address = -1;
+static int hf_pixmap_format = -1;
+static int hf_pixmap_flags = -1;
+static int hf_keyboard_bits = -1;
+static int hf_rectlist_size = -1;
+static int hf_session_id = -1;
+static int hf_display_channels_hint = -1;
+static int hf_supported_mouse_modes = -1;
+static int hf_current_mouse_mode = -1;
+static int hf_agent_connected = -1;
+static int hf_agent_tokens = -1;
+static int hf_agent_protocol = -1;
+static int hf_agent_type = -1;
+static int hf_agent_opaque = -1;
+static int hf_agent_size = -1;
+static int hf_agent_token = -1;
+static int hf_agent_clipboard_selection = -1;
+static int hf_agent_clipboard_type = -1;
+static int hf_multi_media_time = -1;
+static int hf_ram_hint = -1;
+static int hf_button_state = -1;
+static int hf_mouse_display_id = -1;
+static int hf_display_text_fore_mode = -1;
+static int hf_display_text_back_mode = -1;
+static int hf_display_surface_id = -1;
+static int hf_display_surface_width = -1;
+static int hf_display_surface_height = -1;
+static int hf_display_surface_format = -1;
+static int hf_display_surface_flags = -1;
+static int hf_main_client_agent_tokens = -1;
+static int hf_tranparent_src_color = -1;
+static int hf_tranparent_true_color = -1;
+static dissector_handle_t jpeg_handle;
+
+static guint32
+dissect_ID(tvbuff_t *tvb, proto_tree *tree, const guint32 offset)
+{
+    const guint32 id = tvb_get_letohl(tvb, offset);
+    proto_tree_add_text(tree, tvb, offset, 4, "ID: %u (0x%x)", id, id);
+    return id;
+}
+
+/* returns the pixmap size in bytes */
+static guint32
+dissect_Pixmap(tvbuff_t *tvb, proto_tree *tree, guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *Pixmap_tree;
+    guint32 PixmapSize;
+    guint32 strides, height, pallete_ptr;
+
+    ti = proto_tree_add_text(tree, tvb, offset, 0, "Pixmap"); /* size is fixed later */
+    Pixmap_tree = proto_item_add_subtree(ti, ett_Pixmap);
+    proto_tree_add_item(Pixmap_tree, hf_pixmap_format, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+    offset += 1;
+    proto_tree_add_item(Pixmap_tree, hf_pixmap_flags, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+    offset += 1;
+    proto_tree_add_item(Pixmap_tree, hf_pixmap_width, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+    offset += 4;
+    height = tvb_get_letohl(tvb, offset);
+    proto_tree_add_item(Pixmap_tree, hf_pixmap_height, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+    offset += 4;
+    strides = tvb_get_letohl(tvb, offset);
+    proto_tree_add_item(Pixmap_tree, hf_pixmap_stride, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+    offset += 4;
+    pallete_ptr = tvb_get_letohl(tvb, offset);
+    proto_tree_add_item(Pixmap_tree, hf_pixmap_address, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+    offset += 4;
+    PixmapSize = height * strides;
+    proto_item_set_len(ti, 18 + PixmapSize);
+    proto_tree_add_text(Pixmap_tree, tvb, offset, PixmapSize, "Pixmap pixels (%d bytes)", PixmapSize);
+    /*TODO: complete pixmap dissection */
+
+    return PixmapSize + 18;
+}
+
+/* returns the type of cursor */
+static guint8
+dissect_CursorHeader(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint16 *width, guint16 *height)
+{
+    proto_item *ti=NULL;
+    proto_tree *CursorHeader_tree;
+    const guint8 type = tvb_get_guint8(tvb, offset + 8);
+    *width  = tvb_get_letohs(tvb, offset + 8 + 1);
+    *height = tvb_get_letohs(tvb, offset + 8 + 1 + 2);
+
+    if (tree) {
+        ti = proto_tree_add_text(tree, tvb, offset, sizeof_CursorHeader, "Cursor Header");
+        CursorHeader_tree = proto_item_add_subtree(ti, ett_cursor_header);
+        proto_tree_add_item(CursorHeader_tree, hf_cursor_unique,    tvb, offset, 8, ENC_LITTLE_ENDIAN);
+        offset += 8;
+        proto_tree_add_item(CursorHeader_tree, hf_cursor_type,      tvb, offset, 1, ENC_LITTLE_ENDIAN);
+        offset += 1;
+        proto_tree_add_item(CursorHeader_tree, hf_cursor_width,     tvb, offset, 2, ENC_LITTLE_ENDIAN);
+        offset += 2;
+        proto_tree_add_item(CursorHeader_tree, hf_cursor_height,    tvb, offset, 2, ENC_LITTLE_ENDIAN);
+        offset += 2;
+        proto_tree_add_item(CursorHeader_tree, hf_cursor_hotspot_x, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+        offset += 2;
+        proto_tree_add_item(CursorHeader_tree, hf_cursor_hotspot_y, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+    }
+
+    return type;
+}
+
+/* returns the size of RedCursor */
+static guint32
+dissect_RedCursor(tvbuff_t *tvb, proto_tree *tree, guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *RedCursor_tree;
+    guint8 type;
+    guint16 height, width;
+    guint32 init_offset = offset, data_size = 0;
+    const guint16 flags = tvb_get_letohs(tvb, offset);
+
+    ti = proto_tree_add_text(tree, tvb, offset, 2, "RedCursor"); /* FIXME - fix size if flag is not NONE */
+    RedCursor_tree = proto_item_add_subtree(ti, ett_RedCursor);
+
+    proto_tree_add_item(RedCursor_tree, hf_cursor_flags, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+    if (flags == SPICE_CURSOR_FLAGS_NONE) {
+        return 2;
+    }
+
+    offset += 2;
+
+    type = dissect_CursorHeader(tvb, RedCursor_tree, offset, &width, &height);
+    offset += sizeof_CursorHeader;
+
+
+    if ((width == 0 || height == 0) || flags == SPICE_CURSOR_FLAGS_FROM_CACHE) {
+        proto_item_set_len(ti, offset - init_offset);
+        return (offset - init_offset);
+    }
+
+    switch (type) {
+        case SPICE_CURSOR_TYPE_ALPHA:
+            data_size = (width << 2) * height;
+            break;
+        case SPICE_CURSOR_TYPE_MONO:
+            data_size = (SPICE_ALIGN(width, 8) >> 2) * height;
+            break;
+        /* TODO: fix all size calculations for below cursor types, using SPICE_ALIGN */
+        case SPICE_CURSOR_TYPE_COLOR4:
+        case SPICE_CURSOR_TYPE_COLOR8:
+        case SPICE_CURSOR_TYPE_COLOR16:
+        case SPICE_CURSOR_TYPE_COLOR24:
+        case SPICE_CURSOR_TYPE_COLOR32:
+            break;
+        default:
+            data_size = 0;
+            break;
+    }
+    if (data_size != 0) {
+        proto_tree_add_text(RedCursor_tree, tvb, offset, data_size, "Cursor data (%u bytes)", data_size);
+    } else {
+        proto_tree_add_text(RedCursor_tree, tvb, offset, -1, "Cursor data");
+    }
+        offset += data_size;
+
+
+    return (offset - init_offset);
+}
+
+/* returns the image type, needed for later */
+static guint8
+dissect_ImageDescriptor(tvbuff_t *tvb, proto_tree *tree, guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *ImageDescriptor_tree;
+    const guint8 type = tvb_get_guint8(tvb, offset + 8);
+
+    if (tree) {
+        ti = proto_tree_add_text(tree, tvb, offset, sizeof_ImageDescriptor, "Image Descriptor");
+        ImageDescriptor_tree = proto_item_add_subtree(ti, ett_imagedesc);
+
+        proto_tree_add_item(ImageDescriptor_tree, hf_image_desc_id,     tvb, offset, 8, ENC_LITTLE_ENDIAN);
+        offset += 8;
+        proto_tree_add_item(ImageDescriptor_tree, hf_image_desc_type,   tvb, offset, 1, ENC_LITTLE_ENDIAN);
+        offset += 1;
+        proto_tree_add_item(ImageDescriptor_tree, hf_image_desc_flags,  tvb, offset, 1, ENC_LITTLE_ENDIAN);
+        offset += 1;
+        proto_tree_add_item(ImageDescriptor_tree, hf_image_desc_width,  tvb, offset, 4, ENC_LITTLE_ENDIAN);
+        offset += 4;
+        proto_tree_add_item(ImageDescriptor_tree, hf_image_desc_height, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+    }
+
+    return type;
+}
+
+static guint32
+dissect_ImageQuic(tvbuff_t *tvb, proto_tree *tree, guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *ImageQuic_tree;
+    const guint32 QuicSize = tvb_get_letohl(tvb, offset);
+
+    if (tree) {
+        ti = proto_tree_add_text(tree, tvb, offset, QuicSize + 4, "QUIC Image");
+        ImageQuic_tree = proto_item_add_subtree(ti, ett_imageQuic);
+
+        proto_tree_add_text(ImageQuic_tree, tvb, offset, 4, "QUIC image size: %u bytes", QuicSize);
+        offset += 4;
+        proto_tree_add_text(ImageQuic_tree, tvb, offset, 4, "QUIC magic (QUIC)");
+        offset += 4;
+        proto_tree_add_item(ImageQuic_tree, hf_quic_major_version, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+        offset += 2;
+        proto_tree_add_item(ImageQuic_tree, hf_quic_minor_version, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+        offset += 2;
+        proto_tree_add_item(ImageQuic_tree, hf_quic_type,          tvb, offset, 4, ENC_LITTLE_ENDIAN);
+        offset += 4;
+        proto_tree_add_item(ImageQuic_tree, hf_quic_width,         tvb, offset, 4, ENC_LITTLE_ENDIAN);
+        offset += 4;
+        proto_tree_add_item(ImageQuic_tree, hf_quic_height,        tvb, offset, 4, ENC_LITTLE_ENDIAN);
+        offset += 4;
+        proto_tree_add_text(ImageQuic_tree, tvb, offset , QuicSize - 20, "QUIC compressed image data (%u bytes)", QuicSize);
+    }
+
+    return QuicSize + 4;
+}
+static void
+dissect_ImageLZ_common(tvbuff_t *tvb, proto_tree *tree, const guint32 offset)
+{
+
+    proto_tree_add_text(tree, tvb, offset, 4, "LZ magic (\"  ZL\")");
+    proto_tree_add_item(tree, hf_LZ_major_version, tvb, offset + 4, 2, ENC_BIG_ENDIAN);
+    proto_tree_add_item(tree, hf_LZ_minor_version, tvb, offset + 6, 2, ENC_BIG_ENDIAN);
+}
+
+static guint32
+dissect_ImageLZ_JPEG(tvbuff_t *tvb, proto_tree *tree, const guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *LZ_JPEG_tree;
+    const guint32 LZ_JPEGSize = tvb_get_letohl(tvb, offset);
+
+    if (tree) {
+        ti = proto_tree_add_text(tree, tvb, offset, LZ_JPEGSize + 4, "LZ_JPEG Image");
+        LZ_JPEG_tree = proto_item_add_subtree(ti, ett_LZ_JPEG);
+        proto_tree_add_text(LZ_JPEG_tree, tvb, offset, 4, "LZ JPEG image size: %u bytes", LZ_JPEGSize);
+        dissect_ImageLZ_common(tvb, LZ_JPEG_tree, offset + 4);
+    }
+
+    return offset + 12 ;
+}
+
+static guint32
+dissect_ImageGLZ_RGB(tvbuff_t *tvb, proto_tree *tree, guint32 offset, const guint32 size, gboolean zlib_compressed)
+{
+    proto_item *ti=NULL;
+    proto_tree *GLZ_RGB_tree;
+    guint32 GLZ_RGBSize;
+
+    if (size == 0) { /* if no size was passed to us, need to fetch it. Otherwise, we already have it from the callee */
+        GLZ_RGBSize = tvb_get_letohl(tvb, offset);
+        ti = proto_tree_add_text(tree, tvb, offset, GLZ_RGBSize + 4, "GLZ_RGB Image");
+        GLZ_RGB_tree = proto_item_add_subtree(ti, ett_GLZ_RGB);
+        proto_tree_add_text(GLZ_RGB_tree, tvb, offset, 4, "GLZ RGB image size: %u bytes", GLZ_RGBSize);
+        offset += 4;
+    } else {
+        GLZ_RGBSize = size;
+        ti = proto_tree_add_text(tree, tvb, offset, GLZ_RGBSize, "GLZ_RGB Image");
+        GLZ_RGB_tree = proto_item_add_subtree(ti, ett_GLZ_RGB);
+    }
+    if (tree) {
+        dissect_ImageLZ_common(tvb, GLZ_RGB_tree, offset);
+        offset += 8;
+
+/* FIXME: this disregards the possible encoding within the type, so currently the type is sometimes wrong. From server/glz_encoder.c:
+    if (top_down) {
+        encode(encoder, (type & LZ_IMAGE_TYPE_MASK) | (1 << LZ_IMAGE_TYPE_LOG));
+    } else {
+        encode(encoder, (type & LZ_IMAGE_TYPE_MASK));
+    }
+*/
+        proto_tree_add_item(GLZ_RGB_tree, hf_GLZ_RGB_type, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+        offset += 2;
+        if (!zlib_compressed)
+            offset += 2; /* TODO: in ZLIB_GLZ_RGB, there are two extra bytes here! Understand why, but for the time being, hop over them. */
+        proto_tree_add_item(GLZ_RGB_tree, hf_GLZ_RGB_width, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+        offset += 4;
+        proto_tree_add_item(GLZ_RGB_tree, hf_GLZ_RGB_height, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+        offset += 4;
+        proto_tree_add_item(GLZ_RGB_tree, hf_GLZ_RGB_stride, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+        offset += 4;
+        proto_tree_add_item(GLZ_RGB_tree, hf_GLZ_RGB_dict_id, tvb, offset, 8, ENC_LITTLE_ENDIAN);
+        offset += 8;
+
+        proto_tree_add_text(GLZ_RGB_tree, tvb, offset , GLZ_RGBSize - 30, "GLZ_RGB compressed image data (%u bytes)", GLZ_RGBSize - 30);
+    }
+
+    return GLZ_RGBSize + 4;
+}
+
+static guint32
+dissect_ImageLZ_RGB(tvbuff_t *tvb, proto_tree *tree, guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *LZ_RGB_tree;
+    const guint32 LZ_RGBSize = tvb_get_letohl(tvb, offset);
+
+    if (tree) {
+        ti = proto_tree_add_text(tree, tvb, offset, LZ_RGBSize + 4, "LZ_RGB Image");
+        LZ_RGB_tree = proto_item_add_subtree(ti, ett_LZ_RGB);
+        proto_tree_add_text(LZ_RGB_tree, tvb, offset, 4, "LZ RGB image size: %u bytes", LZ_RGBSize);
+        offset += 4;
+        dissect_ImageLZ_common(tvb, LZ_RGB_tree, offset);
+        offset += 8;
+
+/* FIXME: this disregards the possible encoding within the type, so currently the type is sometimes wrong. From server/glz_encoder.c:
+    if (top_down) {
+        encode(encoder, (type & LZ_IMAGE_TYPE_MASK) | (1 << LZ_IMAGE_TYPE_LOG));
+    } else {
+        encode(encoder, (type & LZ_IMAGE_TYPE_MASK));
+    }
+*/
+        offset += 2; /* TODO: not sure we need this! Understand what this is */
+        proto_tree_add_item(LZ_RGB_tree, hf_GLZ_RGB_type, tvb, offset, 2, FALSE);
+        offset += 2;
+        /*FIXME: sometimes width is incorrect (bitmask?) */
+        proto_tree_add_item(LZ_RGB_tree, hf_GLZ_RGB_width, tvb, offset, 4, FALSE);
+        offset += 4;
+        proto_tree_add_item(LZ_RGB_tree, hf_GLZ_RGB_height, tvb, offset, 4, FALSE);
+        offset += 4;
+        proto_tree_add_item(LZ_RGB_tree, hf_GLZ_RGB_stride, tvb, offset, 4, FALSE);
+        offset += 4;
+        proto_tree_add_item(LZ_RGB_tree, hf_GLZ_RGB_dict_id, tvb, offset, 8, TRUE);
+        offset += 8;
+
+        /*TODO: show win_head_image_dist - not sure what it stands for though
+ *         proto_tree_add_item(LZ_RGB_tree, hf_GLZ_RGB_win_head_image_dist, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ *         offset +=4;
+ *         */
+        proto_tree_add_text(LZ_RGB_tree, tvb, offset , LZ_RGBSize - 30, "LZ_RGB compressed image data (%u bytes)", LZ_RGBSize);
+    }
+
+    return LZ_RGBSize + 4;
+}
+
+static guint32
+dissect_ImageLZ_PLT(tvbuff_t *tvb, proto_tree *tree, guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *LZ_PLT_tree;
+    guint32 LZ_PLTSize, somesize;
+
+    /* TODO: const guint8 flags = tvb_get_guint8(tvb, offset); dissect and present */
+
+    LZ_PLTSize = tvb_get_letohl(tvb, offset + 1);
+    ti = proto_tree_add_text(tree, tvb, offset, LZ_PLTSize + 5, "LZ_PLT Image");
+    LZ_PLT_tree = proto_item_add_subtree(ti, ett_LZ_PLT);
+
+    proto_tree_add_text(LZ_PLT_tree, tvb, offset, 1, "Flag");
+    offset += 1;
+    proto_tree_add_text(LZ_PLT_tree, tvb, offset, 4, "LZ PLT image size: %u bytes", LZ_PLTSize);
+    offset += 4;
+    somesize = tvb_get_letohl(tvb, offset);
+    proto_tree_add_text(LZ_PLT_tree, tvb, offset, 4, "Some other size: %u", somesize);
+    offset += 4; /* FIXME - pal pallete value? offset? */
+    dissect_ImageLZ_common(tvb, LZ_PLT_tree, offset);
+    offset += 8;
+
+    /* TODO - complete dissection */
+    return 1 + 4 + 4 + 8 + 8 + LZ_PLTSize;
+}
+
+
+
+static guint32
+dissect_ImageJPEG_Alpha(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset)
+{
+        proto_item *ti=NULL;
+        proto_tree *JPEG_tree;
+        tvbuff_t *jpeg_tvb;
+    guint32 JPEG_Size, Data_Size;
+
+    /*TODO: const guint8 flags = tvb_get_guint8(tvb, offset); dissect and present */
+    offset += 1;
+
+    JPEG_Size = tvb_get_letohl(tvb, offset);
+    offset += 4;
+
+    Data_Size = tvb_get_letohl(tvb, offset);
+    offset += 4;
+
+        ti = proto_tree_add_text(tree, tvb, offset - 9, Data_Size + 5, "RGB JPEG Image, Alpha channel (%u bytes)", Data_Size);
+        JPEG_tree = proto_item_add_subtree(ti, ett_JPEG);
+
+        jpeg_tvb = tvb_new_subset(tvb, offset, JPEG_Size, JPEG_Size);
+        call_dissector(jpeg_handle, jpeg_tvb, pinfo, JPEG_tree);
+    offset += JPEG_Size;
+
+    offset += dissect_ImageLZ_JPEG(tvb, JPEG_tree, offset - 4);
+
+        return Data_Size + 1;
+}
+
+static guint32
+dissect_ImageJPEG(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, const guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *JPEG_tree;
+    tvbuff_t *jpeg_tvb;
+
+    const guint32 JPEG_Size = tvb_get_letohl(tvb, offset);
+    ti = proto_tree_add_text(tree, tvb, offset, JPEG_Size + 4, "JPEG Image (%u bytes)", JPEG_Size);
+    JPEG_tree = proto_item_add_subtree(ti, ett_JPEG);
+
+    jpeg_tvb = tvb_new_subset(tvb, offset + 4, JPEG_Size, JPEG_Size);
+    call_dissector(jpeg_handle, jpeg_tvb, pinfo, JPEG_tree);
+
+    return JPEG_Size + 4;
+}
+
+static guint32
+dissect_ImageZLIB_GLZ(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *ZLIB_GLZ_tree, *Uncomp_tree = NULL;
+    tvbuff_t* uncompressed_tvb = NULL;
+    guint32 ZLIB_GLZSize, ZLIB_uncompSize;
+
+    ZLIB_uncompSize = tvb_get_letohl(tvb, offset);
+    ZLIB_GLZSize = tvb_get_letohl(tvb, offset + 4); /* compressed size */
+    if (tree) {
+        ti = proto_tree_add_text(tree, tvb, offset, ZLIB_GLZSize + 8, "ZLIB over GLZ Image");
+        ZLIB_GLZ_tree = proto_item_add_subtree(ti, ett_ZLIB_GLZ);
+
+        proto_tree_add_text(ZLIB_GLZ_tree, tvb, offset, 4, "ZLIB stream uncompressed size: %u bytes", ZLIB_uncompSize);
+        offset += 4;
+        proto_tree_add_text(ZLIB_GLZ_tree, tvb, offset, 4, "ZLIB stream compressed size: %u bytes", ZLIB_GLZSize);
+        offset += 4;
+        ti = proto_tree_add_text(ZLIB_GLZ_tree, tvb, offset, ZLIB_GLZSize, "ZLIB stream (%u bytes)", ZLIB_GLZSize);
+#ifdef HAVE_LIBZ
+        uncompressed_tvb = tvb_uncompress(tvb, offset, ZLIB_GLZSize);
+        if (uncompressed_tvb != NULL) {
+            add_new_data_source(pinfo, uncompressed_tvb, "Uncompressed GLZ stream");
+            Uncomp_tree = proto_item_add_subtree(ti, ett_Uncomp_tree);
+            dissect_ImageGLZ_RGB(uncompressed_tvb, Uncomp_tree, 0, ZLIB_uncompSize, TRUE);
+        } else {
+                proto_tree_add_text (ZLIB_GLZ_tree, tvb, offset, -1, "Error: Unable to decompress content");
+            }
+#endif
+    }
+
+    return ZLIB_GLZSize + 8;
+}
+
+/* returns the size of an image, not offset */
+static guint32
+dissect_Image(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 offset)
+{
+    guint32 ImageSize = 0;
+    const guint8 type = dissect_ImageDescriptor(tvb, tree, offset);;
+
+    offset += sizeof_ImageDescriptor;
+
+    switch(type) {
+        case IMAGE_TYPE_QUIC:
+            ImageSize = dissect_ImageQuic(tvb, tree, offset);
+            break;
+        case IMAGE_TYPE_GLZ_RGB:
+            ImageSize = dissect_ImageGLZ_RGB(tvb, tree, offset, 0, FALSE);
+            break;
+        case IMAGE_TYPE_LZ_RGB:
+            ImageSize = dissect_ImageLZ_RGB(tvb, tree, offset);
+            break;
+        case IMAGE_TYPE_BITMAP:
+            ImageSize = dissect_Pixmap(tvb, tree, offset);
+            break;
+        case IMAGE_TYPE_FROM_CACHE:
+            proto_tree_add_text(tree, tvb, offset, 0, "Image from Cache");
+            break;
+        case IMAGE_TYPE_FROM_CACHE_LOSSLESS:
+            proto_tree_add_text(tree, tvb, offset, 0, "Image from Cache - lossless");
+            break;
+        case IMAGE_TYPE_ZLIB_GLZ_RGB:
+            ImageSize = dissect_ImageZLIB_GLZ(tvb, tree, pinfo, offset);
+            break;
+        case IMAGE_TYPE_JPEG:
+            ImageSize = dissect_ImageJPEG(tvb, tree, pinfo, offset);
+            break;
+        case IMAGE_TYPE_JPEG_ALPHA:
+            ImageSize = dissect_ImageJPEG_Alpha(tvb, tree, pinfo, offset);
+            break;
+        case IMAGE_TYPE_LZ_PLT:
+            ImageSize = dissect_ImageLZ_PLT(tvb, tree, offset);
+            break;
+        case IMAGE_TYPE_SURFACE:
+            ImageSize = 4; /* surface ID */
+            proto_tree_add_text(tree, tvb, offset, ImageSize, "Surface ID: %u", tvb_get_letohl(tvb, offset));
+            break;
+        default:
+            proto_tree_add_text(tree, tvb, offset, 0, "Unknown image type - cannot dissect");
+    }
+
+    return sizeof_ImageDescriptor + ImageSize;
+}
+
+static SpiceRect
+dissect_SpiceRect(tvbuff_t *tvb, proto_tree *tree, const guint32 offset, const gint32 id)
+{
+    proto_item *ti=NULL;
+    proto_tree *rect_tree;
+    SpiceRect rect;
+
+    rect.left   = tvb_get_letohl(tvb, offset);
+    rect.top    = tvb_get_letohl(tvb, offset + 4);
+    rect.right  = tvb_get_letohl(tvb, offset + 8);
+    rect.bottom = tvb_get_letohl(tvb, offset + 12);
+
+    if (tree) {
+        if (id != -1) {
+            ti = proto_tree_add_text(tree, tvb, offset, sizeof_SpiceRect, "RECT %u: (%u-%u, %u-%u)", id, rect.left, rect.top, rect.right, rect.bottom);
+        } else { /* single rectangle */
+            ti = proto_tree_add_text(tree, tvb, offset, sizeof_SpiceRect, "RECT: (%u-%u, %u-%u)", rect.left, rect.top, rect.right, rect.bottom);
+        }
+        rect_tree = proto_item_add_subtree(ti, ett_rect);
+
+        proto_tree_add_text(rect_tree, tvb, offset,      4, "left: %u", rect.left);
+        proto_tree_add_text(rect_tree, tvb, offset + 4,  4, "top: %u", rect.top);
+        proto_tree_add_text(rect_tree, tvb, offset + 8,  4, "right: %u", rect.right);
+        proto_tree_add_text(rect_tree, tvb, offset + 12, 4, "bottom: %u", rect.bottom);
+    }
+
+    return rect;
+}
+
+static guint32
+rect_is_empty(const SpiceRect r)
+{
+    return r.top == r.bottom || r.left == r.right;
+}
+
+static guint32
+dissect_RectList(tvbuff_t *tvb, proto_tree *tree, guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *rectlist_tree;
+    guint32 i;
+    const guint32 rectlist_size = tvb_get_letohl(tvb, offset);
+
+    if (tree) {
+        ti = proto_tree_add_text(tree, tvb, offset, 4 + (rectlist_size * sizeof_SpiceRect), "RectList (%d rects)", rectlist_size);
+        rectlist_tree = proto_item_add_subtree(ti, ett_rectlist);
+
+        proto_tree_add_item(rectlist_tree, hf_rectlist_size, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+        offset += 4;
+        for (i = 0; i != rectlist_size; i++ ) {
+            dissect_SpiceRect(tvb, rectlist_tree, offset, i);
+            offset += sizeof_SpiceRect;
+        }
+    }
+
+    return (4 + (rectlist_size * sizeof_SpiceRect));
+}
+
+/* returns clip type */
+static guint8
+dissect_Clip(tvbuff_t *tvb, proto_tree *tree, const guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *Clip_tree;
+    const guint8 type = tvb_get_guint8(tvb, offset);
+
+    if (tree) {
+        ti = proto_tree_add_text(tree, tvb, offset, 1, "SpiceClip");
+        Clip_tree = proto_item_add_subtree(ti, ett_Clip);
+        proto_tree_add_item(Clip_tree, hf_Clip_type, tvb, offset, sizeof_Clip, ENC_LITTLE_ENDIAN);
+    }
+
+    return type;
+}
+
+static POINT
+dissect_POINT(tvbuff_t *tvb, proto_tree *tree, const guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *point_tree;
+    POINT point;
+
+    point.x = tvb_get_letohl(tvb, offset);
+    point.y = tvb_get_letohl(tvb, offset + 4);
+
+    if (tree) {
+        ti = proto_tree_add_text(tree, tvb, offset, sizeof_POINT, "POINT (%u, %u)", point.x, point.y);
+        point_tree = proto_item_add_subtree(ti, ett_point);
+
+        proto_tree_add_text(point_tree, tvb, offset,     4, "x: %u", point.x);
+        proto_tree_add_text(point_tree, tvb, offset + 4, 4, "y: %u", point.y);
+    }
+
+    return point;
+}
+
+static POINT16
+dissect_POINT16(tvbuff_t *tvb, proto_tree *tree, const guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *point16_tree;
+    POINT16 point16;
+
+    point16.x = tvb_get_letohs(tvb, offset);
+    point16.y = tvb_get_letohs(tvb, offset + 2);
+
+    if (tree) {
+        ti = proto_tree_add_text(tree, tvb, offset, sizeof_POINT16, "POINT16 (%u, %u)", point16.x, point16.y);
+        point16_tree = proto_item_add_subtree(ti, ett_point16);
+
+        proto_tree_add_text(point16_tree, tvb, offset,     2, "x: %u", point16.x);
+        proto_tree_add_text(point16_tree, tvb, offset + 2, 2, "y: %u", point16.y);
+    }
+
+    return point16;
+}
+
+static guint32
+dissect_Mask(tvbuff_t *tvb, proto_tree *tree, guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *Mask_tree;
+    guint32 bitmap;
+
+    ti = proto_tree_add_text(tree, tvb, offset, sizeof_Mask, "Mask");
+    Mask_tree = proto_item_add_subtree(ti, ett_Mask);
+
+    proto_tree_add_item(Mask_tree, hf_Mask_flag, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+    offset += 1;
+    dissect_POINT(tvb, Mask_tree, offset);
+    offset += sizeof_POINT;
+    bitmap = tvb_get_letohl(tvb, offset);
+    proto_tree_add_item(Mask_tree, hf_Mask_bitmap, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+    offset += 4;
+    if (bitmap != 0) {
+        proto_item_set_len(ti, sizeof_Mask + sizeof_ImageDescriptor);
+        dissect_ImageDescriptor(tvb, Mask_tree, offset);
+        return sizeof_Mask + sizeof_ImageDescriptor;
+    }
+
+    return sizeof_Mask;
+}
+
+/* returns brush size */
+static guint32
+dissect_Brush(tvbuff_t *tvb, proto_tree *tree, guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *brush_tree;
+    const guint8 type = tvb_get_guint8(tvb, offset);
+
+    switch(type) {
+        case SPICE_BRUSH_TYPE_SOLID:
+            ti = proto_tree_add_text(tree, tvb, offset, 5, "Brush - SOLID");
+            brush_tree = proto_item_add_subtree(ti, ett_brush);
+            proto_tree_add_item(brush_tree, hf_brush_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+            offset += 1;
+            proto_tree_add_item(brush_tree, hf_brush_rgb, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            return 5;
+            break;
+        case SPICE_BRUSH_TYPE_PATTERN:
+            ti = proto_tree_add_text(tree, tvb, offset, 17, "Brush - PATTERN");
+            brush_tree = proto_item_add_subtree(ti, ett_brush);
+            proto_tree_add_item(brush_tree, hf_brush_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+            offset += 1;
+            /* FIXME: this is supposed to be the offset to the image to be used as the pattern. For now the hack is that callers check if the returned size was not 5 (therefore SOLID, it's a pattern and later on dissect the image. That's bad. Really. */
+            dissect_ID(tvb, brush_tree, offset);
+            offset += 4;
+            dissect_POINT(tvb, brush_tree, offset);
+            offset += 8;
+            return (1 + 4 + 8);
+            break;
+        case SPICE_BRUSH_TYPE_NONE:
+            proto_tree_add_text(tree, tvb, offset, 1, "Brush - NONE");
+            return 1;
+            break;
+        default:
+            proto_tree_add_text(tree, tvb, offset, 0, "Invalid Brush type");
+            return 0;
+            break;
+    }
+
+    return 0;
+}
+
+static guint32
+dissect_DisplayBase(tvbuff_t *tvb, proto_tree *tree, guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *DisplayBase_tree;
+    SpiceRect rect;
+    guint8 clip_type;
+    guint32 clip_size = 0;
+    ti = proto_tree_add_text(tree, tvb, offset, sizeof_DisplayBase, "SpiceMsgDisplayBase");
+    DisplayBase_tree = proto_item_add_subtree(ti, ett_DisplayBase);
+    proto_tree_add_item(DisplayBase_tree, hf_display_surface_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+    offset += 4;
+    rect = dissect_SpiceRect(tvb, DisplayBase_tree, offset, -1);
+    proto_item_append_text(ti, " - SpiceRect box (%u-%u, %u-%u)",rect.left, rect.top, rect.right, rect.bottom);
+    offset += sizeof_SpiceRect;
+    clip_type = dissect_Clip(tvb, DisplayBase_tree, offset);
+    offset += sizeof_Clip;
+    if (clip_type == CLIP_TYPE_RECTS) {
+        clip_size = dissect_RectList(tvb, DisplayBase_tree, offset);
+        offset += clip_size;
+        proto_item_set_len(ti, sizeof_DisplayBase + clip_size);
+        return sizeof_DisplayBase + clip_size;
+    }
+    return sizeof_DisplayBase;
+}
+
+static const gchar* get_message_type_string(const guint16 message_type, const spice_conversation_t *spice_info, const gboolean client_message)
+{
+
+    if (message_type < SPICE_FIRST_AVAIL_MESSAGE) { /* this is a common message */
+        if (client_message) {
+            return val_to_str_const(message_type, common_client_message_types, "Unknown client message");
+        } else {
+            return val_to_str_const(message_type, common_server_message_types, "Unknown server message");
+        }
+    }
+
+    switch (spice_info->channel_type) {
+        case SPICE_CHANNEL_PLAYBACK:
+            return val_to_str_const(message_type, playback_server_message_types, "Unknown playback channel server message");
+            break;
+        case SPICE_CHANNEL_RECORD:
+            if (client_message) {
+                return val_to_str_const(message_type, record_client_message_types, "Unknown record channel client message");
+            } else {
+                return val_to_str_const(message_type, record_server_message_types, "Unknown record channel server message");
+            }
+            break;
+        case SPICE_CHANNEL_MAIN:
+            if (client_message) {
+                return val_to_str_const(message_type, main_client_message_types, "Unknown main channel client message");
+            } else {
+                return val_to_str_const(message_type, main_server_message_types, "Unknown main channel server message");
+            }
+            break;
+        case SPICE_CHANNEL_CURSOR:
+            if (client_message) {
+                return val_to_str_const(message_type, cursor_client_message_types, "Unknown cursor channel client message");
+            } else {
+                return val_to_str_const(message_type, cursor_server_message_types, "Unknown cursor channel server message");
+            }
+            break;
+        case SPICE_CHANNEL_DISPLAY:
+            if (client_message) {
+                return val_to_str_const(message_type, display_client_message_types, "Unknown display channel client message");
+            } else {
+                return val_to_str_const(message_type, display_server_message_types, "Unknown display channel server message");
+            }
+            break;
+        case SPICE_CHANNEL_INPUTS:
+            if (client_message) {
+                return val_to_str_const(message_type, inputs_client_message_types, "Unknown inputs channel client message");
+            } else {
+                return val_to_str_const(message_type, inputs_server_message_types, "Unknown inputs channel server message");
+            }
+            break;
+        default:
+            break;
+    }
+    return "Unknown message";
+}
+
+static void
+dissect_spice_data_header(tvbuff_t *tvb, proto_tree *tree, const spice_conversation_t *spice_info, const gboolean client_message, const guint16 message_type, guint32 *sublist_size, guint32 offset)
+{
+        *sublist_size = tvb_get_letohl(tvb, offset + 14);
+
+    if (tree) {
+                proto_tree_add_item(tree, hf_serial,       tvb, offset, 8, ENC_LITTLE_ENDIAN);
+                offset += 8;
+                proto_tree_add_text(tree, tvb, offset, 2, "Message type: %s (%d)", get_message_type_string(message_type, spice_info, client_message), message_type);
+                offset += 2;
+                proto_tree_add_item(tree, hf_data_size,    tvb, offset, 4, ENC_LITTLE_ENDIAN);
+                offset += 4;
+                proto_tree_add_item(tree, hf_data_sublist, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+        }
+}
+
+
+static guint32
+dissect_spice_common_client_messages(tvbuff_t *tvb, proto_tree *tree, const guint16 message_type, guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *client_message_tree;
+
+    switch(message_type) {
+        case SPICEC_ACK_SYNC:
+            ti = proto_tree_add_text(tree, tvb, offset, 4, "Client ACK_SYNC message");
+            client_message_tree = proto_item_add_subtree(ti, ett_common_client_message);
+            proto_tree_add_item(client_message_tree, hf_red_set_ack_generation, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            break;
+        case SPICEC_ACK:
+            ti = proto_tree_add_text(tree, tvb, offset, 0, "Client ACK message");
+            client_message_tree = proto_item_add_subtree(ti, ett_common_client_message);
+            break;
+        case SPICEC_PONG:
+            ti = proto_tree_add_text(tree, tvb, offset, 12, "Client PONG message");
+            client_message_tree = proto_item_add_subtree(ti, ett_common_client_message);
+            proto_tree_add_item(client_message_tree, hf_red_ping_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(client_message_tree, hf_red_timestamp, tvb, offset, 8, ENC_LITTLE_ENDIAN);
+            offset += 8;
+            break;
+        /*
+        case SPICEC_MIGRATE_FLUSH_MARK:
+        case SPICEC_MIGRATE_DATA:
+        case SPICEC_DISCONNECTING:
+        */
+        default:
+            proto_tree_add_text(tree, tvb, offset, 0, "Unknown common client message - cannot dissect");
+            break;
+    }
+
+    return offset;
+}
+
+static guint32
+dissect_spice_common_server_messages(tvbuff_t *tvb, proto_tree *tree, const guint16 message_type, guint32 offset, const guint32 total_message_size)
+{
+    proto_item *ti=NULL;
+    proto_tree *server_message_tree;
+    guint32 message_len, severity, visibility;
+
+    switch(message_type) {
+        /*
+        case SPICE_MIGRATE:
+        case SPICE_MIGRATE_DATA:
+        case SPICE_WAIT_FOR_CHANNELS:
+        case SPICE_DISCONNECTING:
+        */
+        case SPICE_SET_ACK:
+            ti = proto_tree_add_text(tree, tvb, offset, 8, "Server SET_ACK message");
+            server_message_tree = proto_item_add_subtree(ti, ett_common_server_message);
+            proto_tree_add_item(server_message_tree, hf_red_set_ack_generation, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(server_message_tree, hf_red_set_ack_window, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            break;
+        case SPICE_PING:
+            ti = proto_tree_add_text(tree, tvb, offset, 12, "Server PING message");
+            server_message_tree = proto_item_add_subtree(ti, ett_common_server_message);
+            proto_tree_add_item(server_message_tree, hf_red_ping_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(server_message_tree, hf_red_timestamp, tvb, offset, 8, ENC_LITTLE_ENDIAN);
+            offset += 8;
+            if (total_message_size > 12) {
+                proto_tree_add_text(server_message_tree, tvb, offset, total_message_size - 12, "PING DATA (%d bytes)", total_message_size - 12);
+                offset += (total_message_size - 12);
+            }
+            break;
+        case SPICE_NOTIFY:
+            ti = proto_tree_add_text(tree, tvb, offset, 12, "Server NOTIFY message");
+            server_message_tree = proto_item_add_subtree(ti, ett_common_server_message);
+            proto_tree_add_item(server_message_tree, hf_red_timestamp, tvb, offset, 8, ENC_LITTLE_ENDIAN);
+            offset += 8;
+            /* TODO: properly dissect severity and visibility flags, using hf_ and proto_tree_add_item */
+            severity = tvb_get_letohl(tvb, offset);
+            proto_tree_add_text(server_message_tree, tvb, offset, 4, "Severity: %s (%d)", val_to_str_const(severity, spice_severity_vs, "unknown severity"), severity);
+            offset += 4;
+            visibility = tvb_get_letohl(tvb, offset);
+            proto_tree_add_text(server_message_tree, tvb, offset, 4, "Visibility: %s (%d)", val_to_str_const(visibility, spice_visibility_vs, "unknown visibility"), visibility);
+            offset += 4;
+            /*TODO: based on severity, dissect the error code */
+            proto_tree_add_text(server_message_tree, tvb, offset, 4, "error/warning/info code: %d", tvb_get_letohl(tvb, offset));
+            offset += 4;
+            message_len = tvb_get_letohl(tvb, offset);
+            proto_tree_add_text(server_message_tree, tvb, offset, 4, "message length: %d", message_len);
+            offset += 4;
+            /*TODO use proto_tree_add_string and get the stringz using tvb_get_ephemeral_stringz() */
+            proto_tree_add_text(server_message_tree, tvb, offset, message_len + 1, "Message content");
+            offset += (message_len + 1);
+            break;
+        default:
+            proto_tree_add_text(tree, tvb, offset, 0, "Unknown common server message - cannot dissect");
+            break;
+    }
+
+    return offset;
+}
+static guint32
+dissect_spice_record_client(tvbuff_t *tvb, proto_tree *tree, const guint16 message_type, guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *record_tree;
+
+    switch(message_type) {
+        case SPICEC_RECORD_MODE:
+            ti = proto_tree_add_text(tree, tvb, offset, 8, "Client RECORD_MODE message"); /* size is incorrect, fixed later */
+            record_tree = proto_item_add_subtree(ti, ett_record_client);
+            proto_tree_add_item(record_tree, hf_playback_record_mode_timstamp, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(record_tree, hf_playback_record_mode, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+            offset += 2;
+            /* TODO - mode dependant, there may be more data here */
+            break;
+        default:
+            proto_tree_add_text(tree, tvb, offset, 0, "Unknown record client message - cannot dissect");
+            break;
+    }
+
+    return offset;
+}
+
+static guint32
+dissect_spice_display_client(tvbuff_t *tvb, proto_tree *tree, const guint16 message_type, guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *display_tree;
+
+    switch(message_type) {
+        case SPICEC_DISPLAY_INIT:
+            ti = proto_tree_add_text(tree, tvb, offset, sizeof_RedcDisplayInit, "Client INIT message");
+            display_tree = proto_item_add_subtree(ti, ett_display_client);
+            proto_tree_add_item(display_tree, hf_spice_display_init_cache_id, tvb, offset,  1, ENC_LITTLE_ENDIAN);
+            offset += 1;
+            proto_tree_add_item(display_tree, hf_spice_display_init_cache_size, tvb, offset,  8, ENC_LITTLE_ENDIAN);
+            offset += 8;
+            proto_tree_add_item(display_tree, hf_spice_display_init_glz_dict_id, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+            offset += 1;
+            proto_tree_add_item(display_tree, hf_spice_display_init_dict_window_size, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            break;
+        default:
+            proto_tree_add_text(tree, tvb, offset, 0, "Unknown display client message - cannot dissect");
+            break;
+    }
+
+    return offset;
+}
+
+static guint32
+dissect_spice_display_server(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, const guint16 message_type, guint32 offset)
+{
+    guint32 data_size, displayBaseLen;
+    guint8 clip_type;
+    guint16 glyphs;
+    SpiceRect r;
+    tvbuff_t *jpeg_tvb;
+
+    switch(message_type) {
+        case SPICE_DISPLAY_MODE:
+            proto_tree_add_item(tree, hf_spice_display_mode_width, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_spice_display_mode_height, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_spice_display_mode_depth, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            break;
+        case SPICE_DISPLAY_MARK:
+            proto_tree_add_text(tree, tvb, offset, 0, "DISPLAY_MARK message");
+            break;
+        case SPICE_DISPLAY_RESET:
+            proto_tree_add_text(tree, tvb, offset, 0, "DISPLAY_RESET message");
+            break;
+        case SPICE_DISPLAY_DRAW_ALPHA_BLEND:
+            displayBaseLen = dissect_DisplayBase(tvb, tree, offset);
+            offset += displayBaseLen;
+            /* TODO: Flag 1 byte, Alpha 1 byte dissection*/
+            offset += 2;
+            dissect_ID(tvb, tree, offset);
+            offset += 4;
+            dissect_SpiceRect(tvb, tree, offset, -1);
+            offset += sizeof_SpiceRect;
+            data_size = dissect_Image(tvb, tree, pinfo, offset);
+            offset += data_size;
+            break;
+        case SPICE_DISPLAY_DRAW_BLACKNESS:
+            displayBaseLen = dissect_DisplayBase(tvb, tree, offset);
+            offset += displayBaseLen;
+            offset += dissect_Mask(tvb, tree, offset);
+            break;
+        case SPICE_DISPLAY_COPY_BITS:
+            displayBaseLen = dissect_DisplayBase(tvb, tree, offset);
+            offset += displayBaseLen;
+            dissect_POINT(tvb, tree, offset);
+            offset += sizeof_POINT;
+            break;
+        case SPICE_DISPLAY_DRAW_WHITENESS:
+            displayBaseLen = dissect_DisplayBase(tvb, tree, offset);
+            offset += displayBaseLen;
+            offset += dissect_Mask(tvb, tree, offset);
+            break;
+        case SPICE_DISPLAY_DRAW_INVERS:
+            displayBaseLen = dissect_DisplayBase(tvb, tree, offset);
+            offset += displayBaseLen;
+            offset += dissect_Mask(tvb, tree, offset);
+            break;
+        case SPICE_DISPLAY_DRAW_FILL:
+            displayBaseLen = dissect_DisplayBase(tvb, tree, offset);
+            offset += displayBaseLen;
+            data_size = dissect_Brush(tvb, tree, offset);
+            offset += data_size;
+
+            proto_tree_add_item(tree, hf_display_rop_descriptor, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+            offset += 2;
+
+            offset += dissect_Mask(tvb, tree, offset);
+
+            if (data_size != 5) { /* if it's not a SOLID brush, it's a PATTERN, dissect its image descriptior */
+                offset += dissect_Image(tvb, tree, pinfo, offset);
+            }
+            break;
+        case SPICE_DISPLAY_DRAW_TRANSPARENT:
+            displayBaseLen = dissect_DisplayBase(tvb, tree, offset);
+            offset += displayBaseLen;
+            dissect_ID(tvb, tree, offset);
+            offset += 4;
+            /* source area */
+            dissect_SpiceRect(tvb, tree, offset, -1);
+            offset += sizeof_SpiceRect;
+            proto_tree_add_item(tree, hf_tranparent_src_color, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_tranparent_true_color, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            data_size = dissect_Image(tvb, tree, pinfo, offset);
+            offset += data_size;
+            break;
+        case SPICE_DISPLAY_DRAW_BLEND:
+        case SPICE_DISPLAY_DRAW_COPY:
+            displayBaseLen = dissect_DisplayBase(tvb, tree, offset);
+            offset += displayBaseLen;
+            /* SpiceImage *src_bitmap */
+            dissect_ID(tvb, tree, offset);
+            offset += 4;
+
+            /* source area */
+            dissect_SpiceRect(tvb, tree, offset, -1);
+            offset += sizeof_SpiceRect;
+
+            proto_tree_add_item(tree, hf_display_rop_descriptor, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+            offset += 2;
+
+            proto_tree_add_item(tree, hf_display_scale_mode, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+            offset += 1;
+
+            offset += dissect_Mask(tvb, tree, offset);
+
+            data_size = dissect_Image(tvb, tree, pinfo, offset);
+            offset += data_size;
+            break;
+        case SPICE_DISPLAY_INVAL_ALL_PALETTES:
+            proto_tree_add_text(tree, tvb, offset, 0, "DISPLAY_INVAL_ALL_PALETTES message");
+            break;
+        case SPICE_DISPLAY_DRAW_TEXT:
+            displayBaseLen = dissect_DisplayBase(tvb, tree, offset);
+            offset += displayBaseLen;
+            dissect_ID(tvb, tree, offset);
+            offset += 4;
+
+            r = dissect_SpiceRect(tvb, tree, offset, -1);
+            offset += sizeof_SpiceRect;
+            if (!rect_is_empty(r)) {
+                data_size = dissect_Brush(tvb, tree, offset);
+                offset += data_size;
+            }
+            proto_tree_add_item(tree, hf_display_text_fore_mode, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+            offset += 2;
+            proto_tree_add_item(tree, hf_display_text_back_mode, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+            offset += 2;
+
+            glyphs = tvb_get_letohs(tvb, offset);
+            proto_tree_add_text(tree, tvb, offset, 2, "Number of glyphs: %u", glyphs);
+            offset += 2;
+            proto_tree_add_text(tree, tvb, offset, 2, "Glyph flags");
+            /*TODO finish dissecting glyph list */
+            break;
+        case SPICE_DISPLAY_DRAW_STROKE:
+            displayBaseLen = dissect_DisplayBase(tvb, tree, offset);
+            offset  += displayBaseLen;
+            /*TODO: complete and correct dissection */
+
+            break;
+        case SPICE_DISPLAY_STREAM_CLIP:
+            proto_tree_add_item(tree, hf_display_stream_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            clip_type = dissect_Clip(tvb, tree, offset);
+            offset += sizeof_Clip;
+            if (clip_type == CLIP_TYPE_RECTS) {
+                offset += dissect_RectList(tvb, tree, offset);
+            }
+            break;
+        case SPICE_DISPLAY_STREAM_CREATE:
+            proto_tree_add_item(tree, hf_display_surface_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_display_stream_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_display_stream_flags, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+            offset += 1;
+            proto_tree_add_item(tree, hf_display_stream_codec_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+            offset += 1;
+            proto_tree_add_text(tree, tvb, offset, 8, "stamp");
+            offset += 8;
+            proto_tree_add_item(tree, hf_display_stream_width, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_display_stream_height, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_display_stream_src_width, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_display_stream_src_height, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            dissect_SpiceRect(tvb, tree, offset, -1);
+            offset += sizeof_SpiceRect;
+            clip_type = dissect_Clip(tvb, tree, offset);
+            offset += sizeof_Clip;
+            if (clip_type == CLIP_TYPE_RECTS) {
+                offset += dissect_RectList(tvb, tree, offset);
+            }
+
+            break;
+        case SPICE_DISPLAY_STREAM_DATA:
+            data_size = tvb_get_letohl(tvb, offset + 8);
+            proto_tree_add_item(tree, hf_display_stream_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_multi_media_time, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_display_stream_data_size, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_text(tree, tvb, offset, data_size, "Stream data");
+            jpeg_tvb = tvb_new_subset(tvb, offset, data_size, data_size);
+                call_dissector(jpeg_handle, jpeg_tvb, pinfo, tree);
+            offset += data_size;
+            break;
+        case SPICE_DISPLAY_STREAM_DESTROY:
+            proto_tree_add_item(tree, hf_display_stream_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            break;
+        case SPICE_DISPLAY_STREAM_DESTROY_ALL:
+            proto_tree_add_text(tree, tvb, offset, 0, "DISPLAY_STREAM_DESTROY_ALL message");
+            break;
+        case SPICE_DISPLAY_DRAW_SURFACE_CREATE:
+            proto_tree_add_item(tree, hf_display_surface_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_display_surface_width, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_display_surface_height, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_display_surface_format, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_display_surface_flags, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            break;
+        case SPICE_DISPLAY_DRAW_SURFACE_DESTROY:
+            proto_tree_add_item(tree, hf_display_surface_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            break;
+        default:
+            proto_tree_add_text(tree, tvb, offset, 0, "Unknown display server message - cannot dissect");
+            break;
+    }
+
+    return offset;
+}
+
+static guint32
+dissect_spice_playback_server(tvbuff_t *tvb, proto_tree *tree, const guint16 message_type, guint32 offset)
+{
+    switch(message_type) {
+        case SPICE_PLAYBACK_DATA:
+            proto_tree_add_item(tree, hf_playback_record_mode_timstamp, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            /* TODO - mode dependent, there may be more data here */
+            break;
+            break;
+        case SPICE_PLAYBACK_MODE:
+            proto_tree_add_item(tree, hf_playback_record_mode_timstamp, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_playback_record_mode, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+            offset += 2;
+            /* TODO - mode dependent, there may be more data here */
+            break;
+        case SPICE_PLAYBACK_START:
+            /*TODO: no. channels (UINT32), format (UINT16), frequency (UINT32), time (UINT32)*/
+            offset += 14;
+            break;
+        case SPICE_PLAYBACK_STOP:
+            proto_tree_add_text(tree, tvb, offset, 0, "PLAYBACK_STOP message");
+            break;
+        default:
+            proto_tree_add_text(tree, tvb, offset, 0, "Unknown playback server message - cannot dissect");
+            break;
+    }
+    return offset;
+}
+
+static guint32
+dissect_spice_cursor_server(tvbuff_t *tvb, proto_tree *tree, const guint16 message_type, guint32 offset)
+{
+    guint32 RedCursorSize;
+
+    switch(message_type) {
+        case SPICE_CURSOR_INIT:
+            dissect_POINT16(tvb, tree, offset);
+            offset += sizeof_POINT16;
+            proto_tree_add_item(tree, hf_cursor_trail_len, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+            offset += 2;
+            proto_tree_add_item(tree, hf_cursor_trail_freq, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+            offset += 2;
+            proto_tree_add_item(tree, hf_cursor_trail_visible, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+            offset += 1;
+            RedCursorSize = dissect_RedCursor(tvb, tree, offset);
+            offset += RedCursorSize;
+            break;
+        case SPICE_CURSOR_RESET:
+            proto_tree_add_text(tree, tvb, offset, 0, "CURSOR_RESET message");
+            break;
+        case SPICE_CURSOR_SET:
+            dissect_POINT16(tvb, tree, offset);
+            offset += sizeof_POINT16;
+            offset +=1; /*TODO flags */
+            RedCursorSize = dissect_RedCursor(tvb, tree, offset);
+            offset += RedCursorSize;
+            break;
+        case SPICE_CURSOR_MOVE:
+            dissect_POINT16(tvb, tree, offset);
+            offset += sizeof_POINT16;
+            break;
+        case SPICE_CURSOR_HIDE:
+            proto_tree_add_text(tree, tvb, offset, 0, "CURSOR_HIDE message");
+            break;
+        case SPICE_CURSOR_TRAIL:
+            proto_tree_add_item(tree, hf_cursor_trail_len, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+            offset += 2;
+            proto_tree_add_item(tree, hf_cursor_trail_freq, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+            offset += 2;
+            break;
+        case SPICE_CURSOR_INVAL_ONE:
+            proto_tree_add_item(tree, hf_cursor_id, tvb, offset, 8, ENC_LITTLE_ENDIAN);
+            offset += 8;
+            break;
+        case SPICE_CURSOR_INVAL_ALL:
+            proto_tree_add_text(tree, tvb, offset, 0, "CURSOR_INVAL_ALL message");
+            break;
+        default:
+            proto_tree_add_text(tree, tvb, offset, 0, "Unknown cursor server message - cannot dissect");
+            break;
+    }
+    return offset;
+}
+
+static guint32
+dissect_spice_record_server(tvbuff_t *tvb, proto_tree *tree, const guint16 message_type, const guint32 offset)
+{
+    switch(message_type) {
+        case SPICE_RECORD_STOP:
+            proto_tree_add_text(tree, tvb, offset, 0, "RECORD_STOP message");
+            break;
+        default:
+            proto_tree_add_text(tree, tvb, offset, 0, "Unknown record server message - cannot dissect");
+            break;
+    }
+    return offset;
+}
+
+static guint32
+dissect_spice_agent_message(tvbuff_t *tvb, proto_tree *tree, const guint32 message_type, guint32 message_len, guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *agent_tree;
+
+    switch (message_type) {
+        case VD_AGENT_MOUSE_STATE:
+            ti = proto_tree_add_text(tree, tvb, offset, 4, "VD_AGENT_MOUSE_STATE message");
+            agent_tree = proto_item_add_subtree(ti, ett_spice_agent);
+            offset += 4;
+            break;
+        case VD_AGENT_MONITORS_CONFIG:
+            ti = proto_tree_add_text(tree, tvb, offset, 4, "VD_AGENT_MONITORS_CONFIG message");
+            agent_tree = proto_item_add_subtree(ti, ett_spice_agent);
+            offset += 4;
+            break;
+        case VD_AGENT_REPLY:
+            ti = proto_tree_add_text(tree, tvb, offset, 8, "VD_AGENT_REPLY message");
+            agent_tree = proto_item_add_subtree(ti, ett_spice_agent);
+            /* TODO: complete dissection */
+            offset += 8;
+            break;
+        case VD_AGENT_CLIPBOARD:
+            ti = proto_tree_add_text(tree, tvb, offset, message_len, "VD_AGENT_CLIPBOARD message");
+            agent_tree = proto_item_add_subtree(ti, ett_spice_agent);
+            /*TODO: display string */
+            offset += message_len;
+            break;
+        case VD_AGENT_DISPLAY_CONFIG:
+            ti = proto_tree_add_text(tree, tvb, offset, 4, "VD_AGENT_DISPLAY_CONFIG message");
+            agent_tree = proto_item_add_subtree(ti, ett_spice_agent);
+            offset += 4;
+            break;
+        case VD_AGENT_ANNOUNCE_CAPABILITIES:
+            ti = proto_tree_add_text(tree, tvb, offset, 8, "VD_AGENT_ANNOUNCE_CAPABILITIES message");
+            agent_tree = proto_item_add_subtree(ti, ett_spice_agent);
+            /* TODO: complete dissection */
+            offset += 8;
+            break;
+        case VD_AGENT_CLIPBOARD_GRAB:
+            ti = proto_tree_add_text(tree, tvb, offset, 4, "VD_AGENT_CLIPBOARD_GRAB message");
+            agent_tree = proto_item_add_subtree(ti, ett_spice_agent);
+            proto_tree_add_item(agent_tree, hf_agent_clipboard_selection, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+            offset += 1;
+            proto_tree_add_text(agent_tree, tvb, offset, 3, "reserverd");
+            offset += 3;
+            break;
+        case VD_AGENT_CLIPBOARD_REQUEST:
+            ti = proto_tree_add_text(tree, tvb, offset, 8, "VD_AGENT_CLIPBOARD_REQUEST message");
+            agent_tree = proto_item_add_subtree(ti, ett_spice_agent);
+            proto_tree_add_item(agent_tree, hf_agent_clipboard_selection, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+            offset += 1;
+            proto_tree_add_text(agent_tree, tvb, offset, 3, "reserverd");
+            offset += 3;
+            proto_tree_add_item(agent_tree, hf_agent_clipboard_type, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            break;
+        case VD_AGENT_CLIPBOARD_RELEASE:
+            ti = proto_tree_add_text(tree, tvb, offset, 0, "VD_AGENT_CLIPBOARD_RELEASE message");
+            agent_tree = proto_item_add_subtree(ti, ett_spice_agent);
+            break;
+        default:
+            proto_tree_add_text(tree, tvb, offset, 0, "Unknown agent message - cannot dissect");
+            break;
+    }
+    return offset;
+}
+
+static guint32
+dissect_spice_main_server(tvbuff_t *tvb, proto_tree *tree, const guint16 message_type, guint32 offset)
+{
+    guint32 num_channels, i, agent_msg_type, agent_msg_len;
+    guint8 channel_type;
+
+    switch(message_type) {
+        case SPICE_MAIN_INIT:
+            proto_tree_add_item(tree, hf_session_id, tvb, offset, 4, ENC_BIG_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_display_channels_hint, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_supported_mouse_modes, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_current_mouse_mode, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_agent_connected, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_agent_tokens, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_multi_media_time, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_ram_hint, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            break;
+        case SPICE_MAIN_CHANNELS_LIST:
+            num_channels = tvb_get_letohl(tvb, offset);
+            proto_tree_add_text(tree, tvb, offset, 4, "Number of channels: %u", num_channels);
+            offset += 4;
+            for (i = 1; i <= num_channels; i++ ) {
+                channel_type = tvb_get_guint8(tvb, offset);
+                proto_tree_add_text(tree, tvb, offset, 1, "Type: %s [%d]", val_to_str_const(channel_type, channel_types_vs, "Unknown"), channel_type);
+                offset += 1;
+                proto_tree_add_text(tree, tvb, offset, 1, "\tID: %d", tvb_get_guint8(tvb, offset));
+                offset += 1;
+            }
+            break;
+        case SPICE_MAIN_MULTI_MEDIA_TIME:
+            proto_tree_add_item(tree, hf_multi_media_time, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            break;
+        case SPICE_MAIN_MOUSE_MODE:
+            proto_tree_add_text(tree, tvb, offset, 4, "MOUSE_MODE message");
+            /* TODO:
+                mouse_mode supported_modes;
+                mouse_mode current_mode;
+            */
+            offset += 4;
+            break;
+        case SPICE_MAIN_AGENT_DATA:
+            proto_tree_add_item(tree, hf_agent_protocol, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            proto_tree_add_item(tree, hf_agent_type, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            agent_msg_type = tvb_get_letohl(tvb, offset);
+            offset += 4;
+            proto_tree_add_item(tree, hf_agent_opaque, tvb, offset, 8, ENC_LITTLE_ENDIAN);
+            offset += 8;
+            proto_tree_add_item(tree, hf_agent_size, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            agent_msg_len = tvb_get_letohl(tvb, offset);
+            offset += 4;
+            offset = dissect_spice_agent_message(tvb, tree, agent_msg_type, agent_msg_len, offset);
+            break;
+        case SPICE_MAIN_AGENT_TOKEN:
+            proto_tree_add_item(tree, hf_agent_token, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            break;
+        default:
+            proto_tree_add_text(tree, tvb, offset, 0, "Unknown main server message - cannot dissect");
+            break;
+    }
+    return offset;
+}
+
+static guint32
+dissect_spice_main_client(tvbuff_t *tvb, proto_tree *tree, const guint16 message_type, guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *main_tree;
+
+    switch(message_type) {
+        case SPICEC_MAIN_MOUSE_MODE_REQUEST:
+            ti = proto_tree_add_text(tree, tvb, offset, 4, "Client MOUSE_MODE_REQUEST message");
+            main_tree = proto_item_add_subtree(ti, ett_main_client);
+            /* TODO: mouse_mode , 2 bytes. */
+            offset += 2;
+            break;
+        case SPICEC_MAIN_ATTACH_CHANNELS:
+            ti = proto_tree_add_text(tree, tvb, offset, 4, "Client MAIN_ATTACH_CHANNEL message");
+            main_tree = proto_item_add_subtree(ti, ett_main_client);
+            break;
+        case SPICEC_MAIN_AGENT_START:
+            ti = proto_tree_add_text(tree, tvb, offset, 4, "Client AGENT_START message");
+            main_tree = proto_item_add_subtree(ti, ett_main_client);
+            proto_tree_add_item(main_tree, hf_main_client_agent_tokens, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            break;
+        default:
+            proto_tree_add_text(tree, tvb, offset, 0, "Unknown main client message - cannot dissect");
+            break;
+    }
+    return offset;
+}
+
+static guint32
+dissect_spice_inputs_client(tvbuff_t *tvb, proto_tree *tree, const guint16 message_type, guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *inputs_tree;
+
+    switch(message_type) {
+        case SPICEC_INPUTS_KEY_DOWN: /*TODO: complete dissection*/
+            ti = proto_tree_add_text(tree, tvb, offset, 4, "Client KEY_DOWN message");
+            inputs_tree = proto_item_add_subtree(ti, ett_inputs_client);
+            offset += 4;
+            break;
+        case SPICEC_INPUTS_KEY_UP:
+            ti = proto_tree_add_text(tree, tvb, offset, 4, "Client KEY_UP message");
+            inputs_tree = proto_item_add_subtree(ti, ett_inputs_client);
+            offset += 4;
+            break;
+        case SPICEC_INPUTS_KEY_MODIFIERS:
+            ti = proto_tree_add_text(tree, tvb, offset, 2, "Client KEY_MODIFIERS message");
+            inputs_tree = proto_item_add_subtree(ti, ett_inputs_client);
+            proto_tree_add_item(inputs_tree, hf_keyboard_bits, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+            offset += 2;
+            break;
+        case SPICEC_INPUTS_MOUSE_POSITION:
+            ti = proto_tree_add_text(tree, tvb, offset, sizeof_POINT + 3, "Client MOUSE_POSITION message");
+            inputs_tree = proto_item_add_subtree(ti, ett_inputs_client);
+            dissect_POINT(tvb, inputs_tree, offset);
+            offset += sizeof_POINT;
+            proto_tree_add_item(inputs_tree, hf_button_state, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+            offset += 2;
+            proto_tree_add_item(inputs_tree, hf_mouse_display_id, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+            offset += 1;
+            break;
+        case SPICEC_INPUTS_MOUSE_MOTION:
+            ti = proto_tree_add_text(tree, tvb, offset, sizeof_POINT + 4, "Client MOUSE_MOTION message");
+            inputs_tree = proto_item_add_subtree(ti, ett_inputs_client);
+            dissect_POINT(tvb, inputs_tree, offset);
+            offset += sizeof_POINT;
+            proto_tree_add_item(inputs_tree, hf_button_state, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+            offset += 4;
+            break;
+        case SPICEC_INPUTS_MOUSE_PRESS:
+            ti = proto_tree_add_text(tree, tvb, offset, 3, "Client MOUSE_PRESS message");
+            inputs_tree = proto_item_add_subtree(ti, ett_inputs_client);
+            proto_tree_add_item(inputs_tree, hf_button_state, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+            offset += 2;
+            proto_tree_add_item(inputs_tree, hf_mouse_display_id, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+            offset += 1;
+            break;
+        case SPICEC_INPUTS_MOUSE_RELEASE:
+            ti = proto_tree_add_text(tree, tvb, offset, 3, "Client MOUSE_RELEASE message");
+            inputs_tree = proto_item_add_subtree(ti, ett_inputs_client);
+            proto_tree_add_item(inputs_tree, hf_button_state, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+            offset += 2;
+            proto_tree_add_item(inputs_tree, hf_mouse_display_id, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+            offset += 1;
+            break;
+        default:
+            proto_tree_add_text(tree, tvb, offset, 0, "Unknown inputs client message - cannot dissect");
+            break;
+    }
+    return offset;
+}
+
+static guint32
+dissect_spice_inputs_server(tvbuff_t *tvb, proto_tree *tree, const guint16 message_type, guint32 offset)
+{
+    switch(message_type) {
+        case SPICE_INPUTS_INIT:
+            proto_tree_add_item(tree, hf_keyboard_bits, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+            offset += 2;
+            break;
+        case SPICE_INPUTS_KEY_MODIFIERS:
+            proto_tree_add_item(tree, hf_keyboard_bits, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+            offset += 2;
+            break;
+        case SPICE_INPUTS_MOUSE_MOTION_ACK:
+            proto_tree_add_text(tree, tvb, offset, 0, "Server INPUTS_MOUSE_MOTION_ACK message");
+            break;
+        default:
+            proto_tree_add_text(tree, tvb, offset, 0, "Unknown inputs server message - cannot dissect");
+            break;
+    }
+    return offset;
+}
+
+static guint32
+dissect_spice_data_server_pdu(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, spice_conversation_t *spice_info, guint32 offset, const guint32 total_message_size)
+{
+    proto_item *ti=NULL, *msg_ti=NULL;
+    proto_tree *data_header_tree, *message_tree;
+    guint16 message_type;
+    guint32 message_size, sublist_size, old_offset;
+
+    message_type = tvb_get_letohs(tvb, offset + 8);
+    message_size = tvb_get_letohl(tvb, offset + 10);
+
+    msg_ti = proto_tree_add_text(tree, tvb, offset, 0, "%s (%d bytes)", get_message_type_string(message_type, spice_info, FALSE), message_size + sizeof_SpiceDataHeader);
+    message_tree = proto_item_add_subtree(msg_ti, ett_message);
+
+    ti = proto_tree_add_item(message_tree, hf_data, tvb, 0, sizeof_SpiceDataHeader, ENC_LITTLE_ENDIAN);
+    data_header_tree = proto_item_add_subtree(ti, ett_data);
+
+    dissect_spice_data_header(tvb, data_header_tree, spice_info, FALSE, message_type, &sublist_size, offset);
+    proto_item_set_len(msg_ti, message_size + sizeof_SpiceDataHeader);
+    offset += sizeof_SpiceDataHeader;
+    old_offset = offset;
+
+    col_append_fstr(pinfo->cinfo, COL_INFO, "%s", get_message_type_string(message_type, spice_info, FALSE));
+    if (message_type < SPICE_FIRST_AVAIL_MESSAGE) { /* this is a common message */
+        offset = dissect_spice_common_server_messages(tvb, message_tree, message_type, offset, total_message_size - sizeof_SpiceDataHeader);
+        return offset;
+    }
+
+    switch (spice_info->channel_type) {
+        case SPICE_CHANNEL_PLAYBACK:
+            offset = dissect_spice_playback_server(tvb, message_tree, message_type, offset);
+            break;
+        case SPICE_CHANNEL_RECORD:
+            offset = dissect_spice_record_server(tvb, message_tree, message_type, offset);
+            break;
+        case SPICE_CHANNEL_MAIN:
+            offset = dissect_spice_main_server(tvb, message_tree, message_type, offset);
+            break;
+        case SPICE_CHANNEL_CURSOR:
+            offset = dissect_spice_cursor_server(tvb, message_tree, message_type, offset);
+            break;
+        case SPICE_CHANNEL_DISPLAY:
+            offset = dissect_spice_display_server(tvb, message_tree, pinfo, message_type, offset);
+            break;
+        case SPICE_CHANNEL_INPUTS:
+            offset = dissect_spice_inputs_server(tvb, message_tree, message_type, offset);
+            break;
+        case SPICE_CHANNEL_TUNNEL:
+            /* TODO: Not implemented yet */
+        case SPICE_CHANNEL_SMARTCARD:
+            /* TODO: Not implemented yet */
+        default:
+            proto_tree_add_text(message_tree, tvb, offset, 0, "Unknown server PDU - cannot dissect");
+    }
+
+    if((offset - old_offset) != message_size) {
+        /* This means we have not dissected the complete message. Would be nice to fix */
+        /* DBG3("dissect_spice_data_server_pdu() - FIXME:message %u in packet %d was not fully dissected (offset 0x%x).\r\n", message_type, pinfo->fd->num, offset); */
+        offset = old_offset + message_size;
+    }
+
+    return offset;
+}
+
+static guint32
+dissect_spice_data_client_pdu(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, spice_conversation_t *spice_info, guint32 offset)
+{
+    proto_item *ti=NULL;
+    proto_tree *data_header_tree;
+    guint16 message_type;
+    guint32 message_size, sublist_size;
+
+    ti = proto_tree_add_item(tree, hf_data, tvb, 0, sizeof_SpiceDataHeader, ENC_LITTLE_ENDIAN);
+    data_header_tree = proto_item_add_subtree(ti, ett_data);
+
+    message_type = tvb_get_letohs(tvb, offset + 8);
+    message_size = tvb_get_letohl(tvb, offset + 10);
+
+    dissect_spice_data_header(tvb, data_header_tree, spice_info, TRUE, message_type, &sublist_size, offset);
+    col_append_fstr(pinfo->cinfo, COL_INFO, "%s", get_message_type_string(message_type, spice_info, TRUE));
+    offset += sizeof_SpiceDataHeader;
+
+    /* TODO: deal with sub-messages list first. As implementation does not uses sub-messsages list yet, it cannot be implemented in the dissector yet.*/
+
+    if (message_type < SPICE_FIRST_AVAIL_MESSAGE) { /* this is a common message */
+        return dissect_spice_common_client_messages(tvb, tree, message_type, offset);
+    }
+
+    switch (spice_info->channel_type) {
+        case SPICE_CHANNEL_PLAYBACK:
+            break;
+        case SPICE_CHANNEL_RECORD:
+            offset = dissect_spice_record_client(tvb, tree, message_type, offset);
+            break;
+        case SPICE_CHANNEL_MAIN:
+            offset = dissect_spice_main_client(tvb, tree, message_type, offset);
+            break;
+        case SPICE_CHANNEL_DISPLAY:
+            offset = dissect_spice_display_client(tvb, tree, message_type, offset);
+            break;
+        case SPICE_CHANNEL_INPUTS:
+            offset = dissect_spice_inputs_client(tvb, tree, message_type, offset);
+            break;
+        default:
+            proto_tree_add_text(tree, tvb, offset, 0, "Unknown client PDU - cannot dissect");
+            break;
+    }
+
+    return offset;
+}
+
+static void
+dissect_spice_link_common_header(tvbuff_t *tvb, proto_tree *tree)
+{
+     if (tree) {
+        /* dissect common header */
+        proto_tree_add_item(tree, hf_spice_magic,   tvb,  0, 4, ENC_NA);
+        proto_tree_add_item(tree, hf_major_version, tvb,  4, 4, ENC_LITTLE_ENDIAN);
+        proto_tree_add_item(tree, hf_minor_version, tvb,  8, 4, ENC_LITTLE_ENDIAN);
+        proto_tree_add_item(tree, hf_message_size,  tvb, 12, 4, ENC_LITTLE_ENDIAN);
+    }
+}
+
+static void
+dissect_spice_common_capabilities(tvbuff_t *tvb, proto_tree *tree, guint32 offset, const int caps_len)
+{
+/* TODO: save common and per-channel capabilities in spice_info ? */
+    int i;
+    for(i = 0; i != caps_len ; i++) {
+        proto_tree_add_item(tree, hf_common_cap, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+        offset += 4;
+    }
+}
+
+static void
+dissect_spice_link_capabilities(tvbuff_t *tvb, proto_tree *tree, guint32 offset, const int caps_len, const spice_conversation_t *spice_info)
+{
+/* TODO: save common and per-channel capabilities in spice_info ? */
+    int i;
+
+    for(i = 0; i != caps_len ; i++) {
+        switch (spice_info->channel_type) {
+            case SPICE_CHANNEL_PLAYBACK:
+                proto_tree_add_item(tree, hf_playback_cap, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+                break;
+            case SPICE_CHANNEL_MAIN:
+                proto_tree_add_item(tree, hf_main_cap, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+                break;
+            case SPICE_CHANNEL_DISPLAY:
+                proto_tree_add_item(tree, hf_display_cap, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+                break;
+            case SPICE_CHANNEL_INPUTS:
+                proto_tree_add_item(tree, hf_inputs_cap, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+                break;
+            case SPICE_CHANNEL_CURSOR:
+                proto_tree_add_item(tree, hf_cursor_cap, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+                break;
+            case SPICE_CHANNEL_RECORD:
+                proto_tree_add_item(tree, hf_record_cap, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+                break;
+            default:
+                proto_tree_add_text(tree, tvb, offset, 0, "Unknown channel - cannot dissect");
+                break;
+        }
+        offset += 4;
+    }
+}
+
+static void
+dissect_spice_link_client_pdu(tvbuff_t *tvb, proto_tree *tree, spice_conversation_t *spice_info)
+{
+    guint32 offset;
+    guint32 common_caps_len, channel_caps_len;
+    proto_item *ti=NULL;
+    proto_tree *link_header_tree = NULL;
+    proto_tree *caps_tree = NULL;
+
+     if (tree) {
+        ti = proto_tree_add_item(tree, hf_link_client, tvb, 0, sizeof_SpiceLinkHeader, ENC_LITTLE_ENDIAN);
+        link_header_tree = proto_item_add_subtree(ti, ett_link_client);
+
+        dissect_spice_link_common_header(tvb, link_header_tree);
+    }
+    offset = sizeof_SpiceLinkHeader;
+
+    if (tree) {
+        proto_tree_add_item(tree, hf_conn_id,          tvb, offset,      4, ENC_LITTLE_ENDIAN);
+        proto_tree_add_item(tree, hf_channel_type,     tvb, offset + 4,  1, ENC_LITTLE_ENDIAN);
+        proto_tree_add_item(tree, hf_channel_id,       tvb, offset + 5,  1, ENC_LITTLE_ENDIAN);
+
+        proto_tree_add_item(tree, hf_num_common_caps,  tvb, offset + 6,  4, ENC_LITTLE_ENDIAN);
+        proto_tree_add_item(tree, hf_num_channel_caps, tvb, offset + 10, 4, ENC_LITTLE_ENDIAN);
+
+        proto_tree_add_item(tree, hf_caps_offset,      tvb, offset + 14, 4, ENC_LITTLE_ENDIAN);
+    }
+
+    common_caps_len = tvb_get_letohl(tvb, offset + 6);
+    channel_caps_len = tvb_get_letohl(tvb, offset + 10);
+
+    if (spice_info->channel_type == SPICE_CHANNEL_NONE) {
+        spice_info->channel_type = tvb_get_guint8(tvb, offset + 4);
+    }
+    offset += sizeof_SpiceDataHeader;
+
+    if (common_caps_len > 0) {
+        ti = proto_tree_add_text(tree, tvb, offset, common_caps_len * 4, "Common Capabilities (%d bytes)", common_caps_len * 4); /* caps_len multiplied by 4 as length is in UINT32 units */
+        caps_tree = proto_item_add_subtree(ti, ett_link_caps);
+        /* TODO: dissect common capabilities */
+        offset += (common_caps_len * 4);
+    }
+    if (channel_caps_len > 0) {
+        ti = proto_tree_add_text(tree, tvb, offset, channel_caps_len * 4, "Channel Capabilities (%d bytes)", channel_caps_len * 4); /* caps_len multiplied by 4 as length is in UINT32 units */
+        caps_tree = proto_item_add_subtree(ti, ett_link_caps);
+        dissect_spice_link_capabilities(tvb, caps_tree, offset, channel_caps_len, spice_info);
+    }
+}
+
+static void
+dissect_spice_link_server_pdu(tvbuff_t *tvb, proto_tree *tree, spice_conversation_t *spice_info)
+{
+    guint32 offset;
+    guint32 common_caps_len, channel_caps_len;
+    proto_item *ti=NULL;
+    proto_tree *link_tree = NULL;
+    proto_tree *caps_tree = NULL;
+
+     if (tree) {
+        ti = proto_tree_add_item(tree, hf_link_server, tvb, 0, sizeof_SpiceLinkHeader, ENC_LITTLE_ENDIAN);
+        link_tree = proto_item_add_subtree(ti, ett_link_server);
+
+         dissect_spice_link_common_header(tvb, link_tree);
+    }
+
+    offset = sizeof_SpiceLinkHeader;
+
+    if (tree) {
+        proto_tree_add_item(tree, hf_error_code,       tvb, offset,                                  4, ENC_LITTLE_ENDIAN);
+        proto_tree_add_text(tree, tvb, offset + 4, SPICE_TICKET_PUBKEY_BYTES, "X.509 SubjectPublicKeyInfo (ASN.1)");
+        proto_tree_add_item(tree, hf_num_common_caps,  tvb, offset + 4 + SPICE_TICKET_PUBKEY_BYTES,  4, ENC_LITTLE_ENDIAN);
+        proto_tree_add_item(tree, hf_num_channel_caps, tvb, offset + 8 + SPICE_TICKET_PUBKEY_BYTES,  4, ENC_LITTLE_ENDIAN);
+
+        proto_tree_add_item(tree, hf_caps_offset,      tvb, offset + 12 + SPICE_TICKET_PUBKEY_BYTES, 4, ENC_LITTLE_ENDIAN);
+    }
+
+    common_caps_len = tvb_get_letohl(tvb, offset + 4 + SPICE_TICKET_PUBKEY_BYTES);
+    channel_caps_len = tvb_get_letohl(tvb, offset + 8 + SPICE_TICKET_PUBKEY_BYTES);
+    offset += sizeof_SpiceLinkHeader + SPICE_TICKET_PUBKEY_BYTES;
+
+    if (common_caps_len > 0) {
+        ti = proto_tree_add_text(tree, tvb, offset, common_caps_len * 4, "Common Capabilities (%d bytes)", common_caps_len * 4); /* caps_len multiplied by 4 as length is in UINT32 units */
+        caps_tree = proto_item_add_subtree(ti, ett_link_caps);
+        dissect_spice_common_capabilities(tvb, caps_tree, offset, common_caps_len);
+        offset += (common_caps_len *4);
+    }
+    if (channel_caps_len > 0) {
+        ti = proto_tree_add_text(tree, tvb, offset, channel_caps_len * 4, "Channel Capabilities (%d bytes)", channel_caps_len * 4); /* caps_len multiplied by 4 as length is in UINT32 units */
+        caps_tree = proto_item_add_subtree(ti, ett_link_caps);
+        dissect_spice_link_capabilities(tvb, caps_tree, offset, channel_caps_len, spice_info);
+    }
+}
+
+static int
+dissect_spice(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+
+    conversation_t *conversation;
+    spice_conversation_t *spice_info;
+    spice_packet_t *per_packet_info;
+    guint32 avail;
+    guint32 pdu_len = 0;
+    guint32 offset;
+    proto_item *ti=NULL;
+    proto_tree *spice_tree=NULL;
+    proto_tree *spice_data_tree=NULL;
+
+    gboolean first_record_in_frame;
+
+    conversation = find_or_create_conversation(pinfo);
+
+    spice_info = (spice_conversation_t*)conversation_get_proto_data(conversation, proto_spice);
+    if(!spice_info) {
+        spice_info = se_alloc0(sizeof(spice_conversation_t));
+        spice_info->destport = pinfo->destport;
+        spice_info->channel_type = SPICE_CHANNEL_NONE;
+        spice_info->next_state = SPICE_LINK_CLIENT;
+        conversation_add_proto_data(conversation, proto_spice, spice_info);
+    }
+
+    per_packet_info = p_get_proto_data(pinfo->fd, proto_spice);
+    if(!per_packet_info) {
+        per_packet_info = se_alloc(sizeof(spice_packet_t));
+        per_packet_info->state = spice_info->next_state;
+        p_add_proto_data(pinfo->fd, proto_spice, per_packet_info);
+    }
+
+    col_clear(pinfo->cinfo, COL_INFO);
+    first_record_in_frame = TRUE;
+
+    switch (per_packet_info->state) {
+        case SPICE_LINK_CLIENT:
+            avail = tvb_reported_length(tvb);
+            if (avail < sizeof_SpiceLinkHeader) { /* the header is at least sizeof_SpiceLinkHeader (16) bytes long */
+                pinfo->desegment_offset = 0;
+                pinfo->desegment_len = sizeof_SpiceLinkHeader - avail;
+                return avail;
+            }
+            pdu_len = tvb_get_letohl(tvb, 12) + sizeof_SpiceLinkHeader;
+            if (avail < pdu_len && spice_desegment) { /* Did not get all the PDU - request the full length of the PDU */
+                pinfo->desegment_offset = 0;
+                pinfo->desegment_len = pdu_len - avail;
+                return avail;
+             }
+            col_set_str(pinfo->cinfo, COL_INFO, "Client link message");
+            if (tree) {
+                ti = proto_tree_add_item(tree, proto_spice, tvb, 0, -1, ENC_NA);
+                spice_tree = proto_item_add_subtree(ti, ett_spice);
+            }
+            dissect_spice_link_client_pdu(tvb, spice_tree, spice_info);
+            col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "Spice %s", val_to_str_const(spice_info->channel_type,channel_types_vs, "Unknown"));
+            spice_info->next_state = SPICE_LINK_SERVER;
+            return pdu_len;
+            break;
+        case SPICE_LINK_SERVER:
+            avail = tvb_reported_length(tvb);
+            if (avail < sizeof_SpiceLinkHeader) {
+                pinfo->desegment_offset = 0;
+                pinfo->desegment_len = sizeof_SpiceLinkHeader - avail;
+                return avail;
+            }
+            pdu_len = tvb_get_letohl(tvb, 12) + sizeof_SpiceLinkHeader;
+            if (avail < pdu_len) {
+                pinfo->desegment_offset = 0;
+                pinfo->desegment_len = pdu_len - avail;
+                return avail;
+            }
+            col_set_str(pinfo->cinfo, COL_INFO, "Server link message");
+            col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "Spice %s", val_to_str_const(spice_info->channel_type,channel_types_vs, "Unknown"));
+            if (tree) {
+                ti = proto_tree_add_item(tree, proto_spice, tvb, 0, -1, ENC_NA);
+                spice_tree = proto_item_add_subtree(ti, ett_spice);
+            }
+            dissect_spice_link_server_pdu(tvb, spice_tree, spice_info);
+            spice_info->next_state = SPICE_TICKET_CLIENT;
+            return pdu_len;
+            break;
+        case SPICE_DATA:
+            offset = 0;
+            while (offset < tvb_reported_length(tvb)) {
+                avail = tvb_length_remaining(tvb, offset);
+                if (avail < sizeof_SpiceDataHeader) { /* didn't get enough to dissect even the header */
+                    pinfo->desegment_offset = offset;
+                    pinfo->desegment_len = sizeof_SpiceDataHeader - avail;
+                    return offset;
+                }
+                pdu_len = tvb_get_letohl(tvb, offset + 14); /* this is actually the sub-message list size */
+                if (pdu_len == 0) { /* if there are no sub-messages, get the usual message body size. Note that we do not dissect properly yet sub-messages - but they are not used in the protcol either */
+                    pdu_len = tvb_get_letohl(tvb, offset + 10);
+                } else {
+                    pdu_len = tvb_get_letohl(tvb, offset + 10);
+                }
+                pdu_len += sizeof_SpiceDataHeader; /* +sizeof_SpiceDataHeader since you need to exclude the SPICE data header, which is sizeof_SpiceDataHeader (18) bytes long) */
+                if (avail < pdu_len) { /* didn't get the complete PDU, returning */
+                    pinfo->desegment_offset = offset;
+                    pinfo->desegment_len = pdu_len - avail;
+                    return offset;
+                 }
+                col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "Spice %s", val_to_str_const(spice_info->channel_type,channel_types_vs, "Unknown"));
+                if (!first_record_in_frame) { /* if it's not the first dissected PDU, we want in COL_INFO to have: "PDU_type_A, PDU_typeB, PDU_typeC, etc. */
+                    col_append_str(pinfo->cinfo, COL_INFO, ", ");
+                }
+                if (tree && spice_data_tree == NULL) {
+                    ti = proto_tree_add_item(tree, proto_spice, tvb, offset, pdu_len, ENC_NA);
+                    spice_data_tree = proto_item_add_subtree(ti, ett_data);
+                }
+                if (spice_info->destport == pinfo->destport) { /* client to server traffic */
+                     offset = dissect_spice_data_client_pdu(tvb, spice_data_tree, pinfo, spice_info, offset);
+                     avail = tvb_length_remaining(tvb, offset);
+                 } else { /* server to client traffic */
+                     offset = dissect_spice_data_server_pdu(tvb, spice_data_tree, pinfo, spice_info, offset, pdu_len);
+                     avail = tvb_length_remaining(tvb, offset);
+                 }
+                first_record_in_frame = FALSE;
+             }
+             return offset;
+            break;
+        case SPICE_TICKET_CLIENT:
+            avail = tvb_reported_length(tvb);
+            pdu_len = 128;
+            if (avail < pdu_len) {
+                pinfo->desegment_offset = 0;
+                pinfo->desegment_len = pdu_len - avail;
+                return avail;
+            }
+            col_set_str(pinfo->cinfo, COL_INFO, "Client ticket");
+            col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "Spice %s", val_to_str_const(spice_info->channel_type,channel_types_vs, "Unknown"));
+            if (tree) {
+                ti = proto_tree_add_item(tree, proto_spice, tvb, 0, -1, ENC_NA);
+                spice_tree = proto_item_add_subtree(ti, ett_ticket_client);
+                proto_tree_add_item(spice_tree, hf_ticket_client, tvb, 0, -1, ENC_LITTLE_ENDIAN);
+            }
+            spice_info->next_state = SPICE_TICKET_SERVER;
+            return 128;
+            break;
+        case SPICE_TICKET_SERVER:
+            avail = tvb_reported_length(tvb);
+            pdu_len = 4;
+            if (avail < pdu_len) {
+                pinfo->desegment_offset = 0;
+                pinfo->desegment_len = pdu_len - avail;
+                return avail;
+            }
+            col_set_str(pinfo->cinfo, COL_INFO, "Server ticket");
+            col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "Spice %s", val_to_str_const(spice_info->channel_type,channel_types_vs, "Unknown"));
+            if (tree) {
+                ti = proto_tree_add_item(tree, proto_spice, tvb, 0, -1, ENC_NA);
+                spice_tree = proto_item_add_subtree(ti, ett_ticket_server);
+                proto_tree_add_item(spice_tree, hf_ticket_server, tvb, 0, 4, ENC_LITTLE_ENDIAN);
+            }
+            spice_info->next_state = SPICE_DATA;
+            return pdu_len;
+            break;
+        default:
+            break;
+    }
+    return 0;
+}
+
+static gboolean
+test_spice_protocol(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+    conversation_t *conversation;
+
+    if (tvb_reported_length(tvb) >= 4 && tvb_get_ntohl(tvb, 0) == SPICE_MAGIC) {
+        conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst,
+                                        pinfo->ptype, pinfo->srcport,
+                                        pinfo->destport, 0);
+        conversation_set_dissector(conversation, spice_handle);
+        dissect_spice(tvb, pinfo, tree);
+        return TRUE;
+    }
+    return FALSE;
+}
+
+/* Register the protocol with Wireshark */
+void
+proto_register_spice(void)
+{
+    /* Setup list of header fields */
+    static hf_register_info hf[] = {
+        { &hf_link_client,
+          { "Link client header", "spice.link_client",
+            FT_NONE, BASE_NONE, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_link_server,
+          { "LINK server header", "spice.link_server",
+            FT_NONE, BASE_NONE, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_spice_magic,
+          { "SPICE MAGIC", "spice.magic",
+            FT_STRING, BASE_NONE, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_major_version,
+          { "Protocol major version", "spice.major_version",
+            FT_INT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_minor_version,
+          { "Protocol minor version", "spice.minor_version",
+            FT_INT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_message_size,
+          { "Message size", "spice.message_size",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_conn_id,
+          { "Session ID", "spice.conn_id",
+            FT_UINT32, BASE_HEX, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_channel_type,
+          { "Channel type", "spice.channel_type",
+            FT_UINT8, BASE_DEC, VALS(channel_types_vs), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_channel_id,
+          { "Channel ID", "spice.channel_id",
+            FT_UINT8, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_num_common_caps,
+          { "Number of common capabilities", "spice.num_common_caps",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_num_channel_caps,
+          { "Number of channel capabilities", "spice.num_channel_caps",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_caps_offset,
+          { "Capabilities offset (bytes)", "spice.caps_offset",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_error_code,
+          { "spice ERROR", "spice.error_code",
+            FT_UINT32, BASE_DEC, VALS(spice_error_codes_vs), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_serial,
+          { "Message serial number", "spice.serial",
+            FT_UINT64, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_data,
+          { "Message header", "spice.message_header",
+            FT_NONE, BASE_NONE, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_data_size,
+          { "Message body size (bytes)", "spice.message_size",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_data_sublist,
+          { "Sub-list offset (bytes)", "spice.message_sublist",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_ticket_client,
+          { "Ticket - client", "spice.ticket_client",
+            FT_NONE, BASE_NONE, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_ticket_server,
+          { "Link result", "spice.ticket_server",
+            FT_UINT32, BASE_DEC, VALS(spice_error_codes_vs), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_common_cap,
+          { "Common channels capabilitity", "spice.common_cap",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_playback_cap,
+          { "Playback channel capability", "spice.playback_cap",
+            FT_UINT32, BASE_DEC, VALS(playback_caps), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_record_cap,
+          { "Record channel capability", "spice.record_cap",
+            FT_UINT32, BASE_DEC, VALS(record_caps), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_cursor_cap,
+          { "Cursor channel capability", "spice.cursor_cap",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_inputs_cap,
+          { "Inputs channel capability", "spice.inputs_cap",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_main_cap,
+          { "Main channel capability", "spice.main_cap",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_display_cap,
+          { "Display channelcapability", "spice.display_cap",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_playback_record_mode_timstamp,
+          { "Timestamp", "spice.timestamp",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_playback_record_mode,
+          { "Mode", "spice.mode",
+            FT_UINT16, BASE_DEC, VALS(playback_mode_vals), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_red_set_ack_generation,
+          { "Set ACK generation", "spice.red_set_ack_generation",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_red_set_ack_window,
+          { "Set ACK window (messages)", "spice.red_set_ack_window",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_Clip_type,
+          { "Clip type", "spice.clip_type",
+            FT_UINT8, BASE_DEC, VALS(clip_types_vs), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_Mask_flag,
+          { "Mask flag", "spice.mask_flag",
+            FT_UINT8, BASE_DEC, VALS(Mask_flags_vs), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_Mask_bitmap,
+          { "Bitmap address", "spice.mask_bitmap",
+            FT_UINT32, BASE_HEX_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_display_rop_descriptor,
+          { "ROP descriptor", "spice.display_rop_descriptor",
+            FT_UINT16, BASE_HEX, VALS(rop_descriptor_vs), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_display_scale_mode,
+          { "Scale mode", "spice.scale_mode",
+            FT_UINT8, BASE_DEC, VALS(scale_mode_vs), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_red_ping_id,
+          { "Ping ID", "spice.ping_id",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_red_timestamp,
+          { "timestamp", "spice.timestamp",
+            FT_UINT64, BASE_HEX, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_spice_display_mode_width,
+          { "Display Width", "spice.display_width",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_spice_display_mode_height,
+          { "Display Height", "spice.display_height",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_spice_display_mode_depth,
+          { "Color depth", "spice.display_depth",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_image_desc_id,
+          { "Image ID", "spice.image_id",
+            FT_UINT64, BASE_HEX, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_image_desc_type,
+          { "Image type", "spice.image_type",
+            FT_UINT8, BASE_DEC, VALS(image_type_vs), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_image_desc_flags,
+          { "Flags", "spice.image_flags",
+            FT_UINT8, BASE_HEX, VALS(image_flags_vs), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_image_desc_width,
+          { "Width", "spice.image_width",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_image_desc_height,
+          { "Height", "spice.image_height",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_quic_width,
+          { "Width", "spice.quic_width",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_quic_type,
+          { "QUIC image type", "spice.quic_type",
+            FT_UINT32, BASE_DEC, VALS(quic_type_vs), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_quic_height,
+          { "Height", "spice.quic_height",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_quic_major_version,
+          { "QUIC major version", "spice.quic_major_version",
+            FT_UINT16, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_quic_minor_version,
+          { "QUIC minor version", "spice.quic_minor_version",
+            FT_UINT16, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_GLZ_RGB_width,
+          { "Width", "spice.GLZ_RGB_width",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_GLZ_RGB_height,
+          { "Height", "spice.GLZ_RGB_height",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_GLZ_RGB_type,
+          { "(G)LZ_RGB image type", "spice.GLZ_RGB_type",
+            FT_UINT16, BASE_DEC, VALS(LzImage_type_vs), 0xf,
+            NULL, HFILL }
+        },
+        { &hf_LZ_major_version,
+          { "LZ major version", "spice.LZ_major_version",
+            FT_UINT16, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_LZ_minor_version,
+          { "LZ minor version", "spice.LZ_minor_version",
+            FT_UINT16, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_GLZ_RGB_stride,
+          { "Stride", "spice.GLZ_RGB_stride",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_GLZ_RGB_dict_id,
+          { "(G)LZ RGB Dictionary ID", "spice.GLZ_RGB_dict_id",
+            FT_UINT64, BASE_HEX, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_cursor_trail_len,
+          { "Cursor trail length", "spice.cursor_trail_len",
+            FT_UINT16, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_cursor_trail_freq,
+          { "Cursor trail frequency", "spice.cursor_trail_freq",
+            FT_UINT16, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_cursor_trail_visible,
+          { "Cursor trail visiblity", "spice.cursor_trail_visible",
+            FT_UINT8, BASE_DEC, VALS(cursor_visible_vs), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_cursor_unique,
+          { "Cursor unique ID", "spice._cursor_unique",
+            FT_UINT64, BASE_HEX, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_cursor_type,
+          { "Cursor type", "spice.cursor_type",
+            FT_UINT8, BASE_HEX, VALS(cursor_type_vs), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_cursor_width,
+          { "Cursor width", "spice.cursor_width",
+            FT_UINT16, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_cursor_height,
+          { "Cursor height", "spice.cursor_height",
+            FT_UINT16, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_cursor_hotspot_x,
+          { "Cursor hotspot X", "spice.cursor_hotspot_x",
+            FT_UINT16, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_cursor_hotspot_y,
+          { "Cursor hotspot Y", "spice.cursor_hotspot_y",
+            FT_UINT16, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_cursor_flags, /*FIXME - those are flags */
+          { "Cursor flags", "spice.cursor_flags",
+            FT_UINT16, BASE_HEX, VALS(cursor_flags_vs), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_cursor_id,
+          { "Cursor ID", "spice.cursor_id",
+            FT_UINT64, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_spice_display_init_cache_id,
+          { "Cache ID", "spice.display_init_cache_id",
+            FT_UINT8, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_spice_display_init_cache_size,
+          { "Cache size (pixels)", "spice.display_init_cache_size",
+            FT_UINT64, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_spice_display_init_glz_dict_id,
+          { "GLZ Dictionary ID", "spice.display_init_glz_dict_id",
+            FT_UINT8, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_spice_display_init_dict_window_size,
+          { "Dictionary window size", "spice.display_init_dict_window_size",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_brush_type,
+          { "Brush type", "spice.brush_type",
+            FT_UINT8, BASE_DEC, VALS(brush_types_vs), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_brush_rgb,
+          { "Brush color", "spice.brush_rgb",
+            FT_UINT32, BASE_HEX, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_pixmap_width,
+          { "Pixmap width", "spice.pixmap_width",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_pixmap_height,
+          { "Pixmap height", "spice.pixmap_height",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_pixmap_stride,
+          { "Pixmap stride", "spice.pixmap_stride",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_pixmap_address,
+          { "Pixmap palettte pointer", "spice.pixmap_palette_address",
+            FT_UINT32, BASE_HEX_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_pixmap_format,
+          { "Pixmap format", "spice.pixmap_format",
+            FT_UINT8, BASE_DEC, VALS(Pixmap_types_vs), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_pixmap_flags,
+          { "Pixmap flags", "spice.pixmap_flags",
+            FT_UINT8, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_keyboard_bits, /* FIXME - flags */
+          { "Keyboard led bits", "spice.keyboard_bits",
+            FT_UINT16, BASE_HEX, VALS(input_modifiers_types), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_rectlist_size,
+          { "RectList size", "spice.rectlist_size",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_session_id,
+          { "Session ID", "spice.main_session_id",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_display_channels_hint,
+          { "Number of display channels", "spice.display_channels_hint",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_supported_mouse_modes, /* FIXME: bitmask */
+          { "Supported mouse modes", "spice.supported_mouse_modes",
+            FT_UINT32, BASE_HEX, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_current_mouse_mode,
+          { "Current mouse mode", "spice.current_mouse_mode",
+            FT_UINT32, BASE_DEC, VALS(spice_mouse_modes_vs), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_agent_connected,
+          { "Agent", "spice.agent",
+            FT_UINT32, BASE_DEC, VALS(spice_agent_vs), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_agent_tokens,
+          { "Agent tokens", "spice.agent_tokens",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_multi_media_time,
+          { "Current server multimedia time", "spice.multimedia_time",
+            FT_UINT32, BASE_HEX, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_ram_hint,
+          { "RAM hint", "spice.ram_hint",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_button_state, /*FIXME - bitmask */
+          { "Mouse button state", "spice.button_state",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_mouse_display_id,
+          { "Mouse display ID", "spice.mouse_display_id",
+            FT_UINT8, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_display_text_fore_mode,
+          { "Text foreground mode", "spice.draw_text_fore_mode",
+            FT_UINT16, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_display_text_back_mode,
+          { "Text background mode", "spice.draw_text_back_mode",
+            FT_UINT16, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_display_stream_id,
+          { "Stream ID", "spice.display_stream_id",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_display_stream_flags,
+          { "Stream flags", "spice.display_stream_flags",
+            FT_UINT8, BASE_DEC, VALS(stream_flags), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_display_stream_codec_type,
+          { "Stream codec type", "spice.display_stream_codec_type",
+            FT_UINT32, BASE_DEC, VALS(stream_codec_types), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_display_stream_data_size,
+          { "Stream data size", "spice.display_stream_data_size",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_display_stream_width,
+          { "Stream width", "spice.stream_width",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_display_stream_height,
+          { "Stream height", "spice.stream_height",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_display_stream_src_width,
+          { "Stream source width", "spice.stream_src_width",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_display_stream_src_height,
+          { "Stream source height", "spice.stream_src_height",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_display_surface_id,
+          { "Surface ID", "spice.surface_id",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_display_surface_width,
+          { "Surface width", "spice.surface_width",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_display_surface_height,
+          { "Surface height", "spice.surface_height",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_display_surface_format,
+          { "Surface format", "spice.surface_format",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_display_surface_flags,
+          { "Surface flags", "spice.surface_flags",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_tranparent_src_color,
+          { "Transparent source color", "spice.display_transparent_src_color",
+            FT_UINT32, BASE_HEX, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_tranparent_true_color,
+          { "Transparent true color", "spice.display_transparent_true_color",
+            FT_UINT32, BASE_HEX, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_main_client_agent_tokens,
+          { "Agent Tokes", "spice.main_agent_tokens",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_agent_protocol,
+          { "Agent Protocol version", "spice.main_agent_protocol",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_agent_type,
+          { "Agent Type", "spice.main_agent_type",
+            FT_UINT32, BASE_DEC, VALS(agent_message_type), 0x0,
+            NULL, HFILL }
+        },
+        { &hf_agent_opaque,
+          { "Agent Opaque", "spice.main_agent_opaque",
+            FT_UINT64, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_agent_size,
+          { "Agent message size", "spice.main_agent_size",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_agent_token,
+          { "Agent token", "spice.main_agent_token",
+            FT_UINT32, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_agent_clipboard_selection,
+          { "Agent clipboard selection", "spice.main_agent_clipboard_selection",
+            FT_UINT8, BASE_DEC, NULL, 0x0,
+            NULL, HFILL }
+        },
+        { &hf_agent_clipboard_type,
+          { "Agent clipboard type", "spice.main_agent_clipboard_type",
+            FT_UINT32, BASE_DEC, VALS(agent_clipboard_type), 0x0,
+            NULL, HFILL }
+        },
+    };
+
+    /* Setup protocol subtree arrays */
+    static gint *ett[] = {
+        &ett_spice,
+        &ett_link_client,
+        &ett_link_server,
+        &ett_link_caps,
+        &ett_ticket_client,
+        &ett_ticket_server,
+        &ett_data,
+        &ett_message,
+        &ett_playback,
+        &ett_common_server_message,
+        &ett_common_client_message,
+        &ett_display_client,
+        &ett_display_server,
+        &ett_point,
+        &ett_point16,
+        &ett_rect,
+        &ett_DisplayBase,
+        &ett_Clip,
+        &ett_Mask,
+        &ett_imagedesc,
+        &ett_imageQuic,
+        &ett_GLZ_RGB,
+        &ett_LZ_RGB,
+        &ett_ZLIB_GLZ,
+        &ett_Uncomp_tree,
+        &ett_LZ_JPEG,
+        &ett_LZ_PLT,
+        &ett_JPEG,
+        &ett_cursor_header,
+        &ett_RedCursor,
+        &ett_cursor,
+        &ett_spice_main,
+        &ett_brush,
+        &ett_pattern,
+        &ett_Pixmap,
+        &ett_inputs_client,
+        &ett_rectlist,
+        &ett_inputs_server,
+        &ett_record_client,
+        &ett_main_client,
+        &ett_spice_agent
+    };
+
+    /* Register the protocol name and description */
+    proto_spice = proto_register_protocol("Spice protocol",
+                                          "Spice", "spice");
+
+    /* Requispice function calls to register the header fields and subtrees */
+    proto_register_field_array(proto_spice, hf, array_length(hf));
+    proto_register_subtree_array(ett, array_length(ett));
+
+}
+
+void
+proto_reg_handoff_spice(void)
+{
+    spice_handle = new_create_dissector_handle(dissect_spice, proto_spice);
+    dissector_add_handle("tcp.port", spice_handle);   /* for "decode as" */
+    heur_dissector_add("tcp", test_spice_protocol, proto_spice);
+    jpeg_handle = find_dissector("image-jfif");
+}
+