[spice-protocol 1/4] build-sys: Import code generation files from spice-common

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is in preparation for shipping these files as part of
spice-protocol, and allow spice-common to stop using spice-protocol as a
submodule. The reason for moving them there is in order to be able to
automatically regenerate enums.h when spice.proto changes.

This commit is just a placeholder so that a patch series can be sent.
The actual commit is a merge commit of a repository created with git
filter-branch on spice-common to only keep the relevant files:

git filter-branch \
    --prune-empty \
    --index-filter '
        git ls-tree -z -r --name-only --full-tree $GIT_COMMIT \
        | grep -z -v "^python_modules/" \
        | grep -z -v "^spice.proto" \
        | grep -z -v "^spice1.proto" \
        | grep -z -v "^spice_codegen.py" \
        | xargs -0 -r git rm -f --cached -r
    ' \
    -- \
    --all

git filter-branch --force \
    --prune-empty
    --index-filter '
        git rm -f spice-protocol --cached  --ignore-unmatch
    ' \
    -- \
    --all
---
 python_modules/Makefile.am     |   16 +
 python_modules/__init__.py     |    0
 python_modules/codegen.py      |  378 +++++++++++
 python_modules/demarshal.py    | 1270 ++++++++++++++++++++++++++++++++++++
 python_modules/marshal.py      |  427 ++++++++++++
 python_modules/ptypes.py       | 1061 ++++++++++++++++++++++++++++++
 python_modules/spice_parser.py |  162 +++++
 spice.proto                    | 1388 ++++++++++++++++++++++++++++++++++++++++
 spice1.proto                   |  943 +++++++++++++++++++++++++++
 9 files changed, 5645 insertions(+)
 create mode 100644 python_modules/Makefile.am
 create mode 100644 python_modules/__init__.py
 create mode 100644 python_modules/codegen.py
 create mode 100644 python_modules/demarshal.py
 create mode 100644 python_modules/marshal.py
 create mode 100644 python_modules/ptypes.py
 create mode 100644 python_modules/spice_parser.py
 create mode 100644 spice.proto
 create mode 100644 spice1.proto

diff --git a/python_modules/Makefile.am b/python_modules/Makefile.am
new file mode 100644
index 0000000..50e1a71
--- /dev/null
+++ b/python_modules/Makefile.am
@@ -0,0 +1,16 @@
+NULL =
+
+PYTHON_MODULES =				\
+	__init__.py				\
+	codegen.py				\
+	demarshal.py				\
+	marshal.py				\
+	ptypes.py				\
+	spice_parser.py				\
+	$(NULL)
+
+EXTRA_DIST = $(PYTHON_MODULES)
+
+DISTCLEANFILES = *.pyc
+
+-include $(top_srcdir)/git.mk
diff --git a/python_modules/__init__.py b/python_modules/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/python_modules/codegen.py b/python_modules/codegen.py
new file mode 100644
index 0000000..f324498
--- /dev/null
+++ b/python_modules/codegen.py
@@ -0,0 +1,378 @@
+
+import six
+from io import StringIO
+
+def camel_to_underscores(s, upper = False):
+    res = ""
+    for i in range(len(s)):
+        c = s[i]
+        if i > 0 and c.isupper():
+            res = res + "_"
+        if upper:
+            res = res + c.upper()
+        else:
+            res = res + c.lower()
+    return res
+
+def underscores_to_camel(s):
+    res = ""
+    do_upper = True
+    for i in range(len(s)):
+        c = s[i]
+        if c == "_":
+            do_upper = True
+        else:
+            if do_upper:
+                res = res + c.upper()
+            else:
+                res = res + c
+            do_upper = False
+    return res
+
+proto_prefix = "Temp"
+
+def set_prefix(prefix):
+    global proto_prefix
+    global proto_prefix_upper
+    global proto_prefix_lower
+    proto_prefix = prefix
+    proto_prefix_upper = prefix.upper()
+    proto_prefix_lower = prefix.lower()
+
+def prefix_underscore_upper(*args):
+    s = proto_prefix_upper
+    for arg in args:
+        s = s + "_" + arg
+    return s
+
+def prefix_underscore_lower(*args):
+    s = proto_prefix_lower
+    for arg in args:
+        s = s + "_" + arg
+    return s
+
+def prefix_camel(*args):
+    s = proto_prefix
+    for arg in args:
+        s = s + underscores_to_camel(arg)
+    return s
+
+def increment_identifier(idf):
+    v = idf[-1:]
+    if v.isdigit():
+        return idf[:-1] + str(int(v) + 1)
+    return idf + "2"
+
+def sum_array(array):
+    if len(array) == 0:
+        return 0
+    return " + ".join(array)
+
+class CodeWriter:
+    def __init__(self):
+        self.out = StringIO()
+        self.contents = [self.out]
+        self.indentation = 0
+        self.at_line_start = True
+        self.indexes = ["i", "j", "k", "ii", "jj", "kk"]
+        self.current_index = 0
+        self.generated = {}
+        self.vars = []
+        self.has_error_check = False
+        self.options = {}
+        self.function_helper_writer = None
+
+    def set_option(self, opt, value = True):
+        self.options[opt] = value
+
+    def has_option(self, opt):
+        return opt in self.options
+
+    def set_is_generated(self, kind, name):
+        if kind not in self.generated:
+            v = {}
+            self.generated[kind] = v
+        else:
+            v = self.generated[kind]
+        v[name] = 1
+
+    def is_generated(self, kind, name):
+        if kind not in self.generated:
+            return False
+        v = self.generated[kind]
+        return name in v
+
+    def getvalue(self):
+        strs = [writer.getvalue() for writer in self.contents]
+        return "".join(strs)
+
+    def get_subwriter(self):
+        writer = CodeWriter()
+        self.contents.append(writer)
+        self.out = StringIO()
+        self.contents.append(self.out)
+        writer.indentation = self.indentation
+        writer.at_line_start = self.at_line_start
+        writer.generated = self.generated
+        writer.options = self.options
+        writer.public_prefix = self.public_prefix
+
+        return writer
+
+    def write(self, s):
+        # Ensure its a unicode string
+        if six.PY3:
+            s = str(s)
+        else:
+            s = unicode(s)
+
+        if len(s) == 0:
+            return
+
+        if self.at_line_start:
+            for i in range(self.indentation):
+                self.out.write(u" ")
+            self.at_line_start = False
+        self.out.write(s)
+        return self
+
+    def newline(self):
+        self.out.write(u"\n")
+        self.at_line_start = True
+        return self
+
+    def writeln(self, s):
+        self.write(s)
+        self.newline()
+        return self
+
+    def label(self, s):
+        self.indentation = self.indentation - 1
+        self.write(s + ":")
+        self.indentation = self.indentation + 1
+        self.newline()
+
+    def statement(self, s):
+        self.write(s)
+        self.write(";")
+        self.newline()
+        return self
+
+    def assign(self, var, val):
+        self.write("%s = %s" % (var, val))
+        self.write(";")
+        self.newline()
+        return self
+
+    def increment(self, var, val):
+        self.write("%s += %s" % (var, val))
+        self.write(";")
+        self.newline()
+        return self
+
+    def comment(self, str):
+        self.write("/* " + str + " */")
+        return self
+
+    def todo(self, str):
+        self.comment("TODO: *** %s ***" % str).newline()
+        return self
+
+    def error_check(self, check, label = "error"):
+        self.has_error_check = True
+        with self.block("if (SPICE_UNLIKELY(%s))" % check):
+            if self.has_option("print_error"):
+                self.statement('printf("%%s: Caught error - %s", __PRETTY_FUNCTION__)' % check)
+            if self.has_option("assert_on_error"):
+                self.statement("assert(0)")
+            self.statement("goto %s" % label)
+
+    def indent(self):
+        self.indentation += 4
+
+    def unindent(self):
+        self.indentation -= 4
+        if self.indentation < 0:
+            self.indenttation = 0
+
+    def begin_block(self, prefix= "", comment = ""):
+        if len(prefix) > 0:
+            self.write(prefix)
+        if self.at_line_start:
+            self.write("{")
+        else:
+            self.write(" {")
+        if len(comment) > 0:
+            self.write(" ")
+            self.comment(comment)
+        self.newline()
+        self.indent()
+
+    def end_block(self, semicolon=False, newline=True):
+        self.unindent()
+        if self.at_line_start:
+            self.write("}")
+        else:
+            self.write(" }")
+        if semicolon:
+            self.write(";")
+        if newline:
+            self.newline()
+
+    class Block:
+        def __init__(self, writer, semicolon, newline):
+            self.writer = writer
+            self.semicolon = semicolon
+            self.newline = newline
+
+        def __enter__(self):
+            return self.writer.get_subwriter()
+
+        def __exit__(self, exc_type, exc_value, traceback):
+            self.writer.end_block(self.semicolon, self.newline)
+
+    class PartialBlock:
+        def __init__(self, writer, scope, semicolon, newline):
+            self.writer = writer
+            self.scope = scope
+            self.semicolon = semicolon
+            self.newline = newline
+
+        def __enter__(self):
+            return self.scope
+
+        def __exit__(self, exc_type, exc_value, traceback):
+            self.writer.end_block(self.semicolon, self.newline)
+
+    class NoBlock:
+        def __init__(self, scope):
+            self.scope = scope
+
+        def __enter__(self):
+            return self.scope
+
+        def __exit__(self, exc_type, exc_value, traceback):
+            pass
+
+    def block(self, prefix= "", comment = "", semicolon=False, newline=True):
+        self.begin_block(prefix, comment)
+        return self.Block(self, semicolon, newline)
+
+    def partial_block(self, scope, semicolon=False, newline=True):
+        return self.PartialBlock(self, scope, semicolon, newline)
+
+    def no_block(self, scope):
+        return self.NoBlock(scope)
+
+    def optional_block(self, scope):
+        if scope != None:
+            return self.NoBlock(scope)
+        return self.block()
+
+    def for_loop(self, index, limit):
+        return self.block("for (%s = 0; %s < %s; %s++)" % (index, index, limit, index))
+
+    def while_loop(self, expr):
+        return self.block("while (%s)" % (expr))
+
+    def if_block(self, check, elseif=False, newline=True):
+        s = "if (%s)" % (check)
+        if elseif:
+            s = " else " + s
+        self.begin_block(s, "")
+        return self.Block(self, False, newline)
+
+    def variable_defined(self, name):
+        for n in self.vars:
+            if n == name:
+                return True
+        return False
+
+    def variable_def(self, ctype, *names):
+        for n in names:
+            # Strip away initialization
+            i = n.find("=")
+            if i != -1:
+                n = n[0:i]
+            self.vars.append(n.strip())
+        # only add space for non-pointer types
+        if ctype[-1] == "*":
+            ctype = ctype[:-1].rstrip()
+            self.writeln("%s *%s;"%(ctype, ", *".join(names)))
+        else:
+            self.writeln("%s %s;"%(ctype, ", ".join(names)))
+        return self
+
+    def function_helper(self):
+        if self.function_helper_writer != None:
+            writer = self.function_helper_writer.get_subwriter()
+            self.function_helper_writer.newline()
+        else:
+            writer = self.get_subwriter()
+        return writer
+
+    def function(self, name, return_type, args, static = False):
+        self.has_error_check = False
+        self.function_helper_writer = self.get_subwriter()
+        if static:
+            self.write("static ")
+        self.write(return_type)
+        self.write(" %s(%s)"% (name, args)).newline()
+        self.begin_block()
+        self.function_variables_writer = self.get_subwriter()
+        self.function_variables = {}
+        return self.function_variables_writer
+
+    def macro(self, name, args, define):
+        self.write("#define %s(%s) %s" % (name, args, define)).newline()
+
+    def ifdef(self, name):
+        indentation = self.indentation
+        self.indentation = 0;
+        self.write("#ifdef %s" % (name)).newline()
+        self.indentation = indentation
+
+    def ifdef_else(self, name):
+        indentation = self.indentation
+        self.indentation = 0;
+        self.write("#else /* %s */" % (name)).newline()
+        self.indentation = indentation
+
+    def endif(self, name):
+        indentation = self.indentation
+        self.indentation = 0;
+        self.write("#endif /* %s */" % (name)).newline()
+        self.indentation = indentation
+
+    def add_function_variable(self, ctype, name):
+        if name in self.function_variables:
+            assert(self.function_variables[name] == ctype)
+        else:
+            self.function_variables[name] = ctype
+            self.function_variables_writer.variable_def(ctype, name)
+
+    def pop_index(self):
+        index = self.indexes[self.current_index]
+        self.current_index = self.current_index + 1
+        self.add_function_variable("uint32_t", index)
+        return index
+
+    def push_index(self):
+        self.current_index = self.current_index - 1
+
+    class Index:
+        def __init__(self, writer, val):
+            self.writer = writer
+            self.val = val
+
+        def __enter__(self):
+            return self.val
+
+        def __exit__(self, exc_type, exc_value, traceback):
+            self.writer.push_index()
+
+    def index(self, no_block = False):
+        if no_block:
+            return self.no_block(None)
+        val = self.pop_index()
+        return self.Index(self, val)
diff --git a/python_modules/demarshal.py b/python_modules/demarshal.py
new file mode 100644
index 0000000..209eafc
--- /dev/null
+++ b/python_modules/demarshal.py
@@ -0,0 +1,1270 @@
+
+from . import ptypes
+from . import codegen
+
+# The handling of sizes is somewhat complex, as there are several types of size:
+# * nw_size
+#   This is the network size, i.e. the number of bytes on the network
+#
+# * mem_size
+#   The total amount of memory used for the representation of something inside
+#   spice. This is generally sizeof(C struct), but can be larger if for instance
+#   the type has a variable size array at the end or has a pointer in it that
+#   points to another data chunk (which will be allocated after the main
+#   data chunk). This is essentially how much memory you need to allocate to
+#   contain the data type.
+#
+# * extra_size
+#   This is the size of anything that is not part of the containing structure.
+#   For instance, a primitive (say uint32_t) member has no extra size, because
+#   when allocating its part of the sizeof(MessageStructType) struct. However
+#   a variable array can be places at the end of a structure (@end) and its
+#   size is then extra_size. Note that this extra_size is included in the
+#   mem_size of the enclosing struct, and even if you request the mem_size
+#   of the array itself. However, extra_size is typically not requested
+#   when the full mem_size is also requested.
+#
+#   extra sizes come in two flavours. contains_extra_size means that the item
+#   has a normal presence in the parent container, but has some additional
+#   extra_size it references. For instance via a pointer somewhere in it.
+#   There is also is_extra_size(). This indicates that the whole elements
+#   "normal" mem size should be considered extra size for the container, so
+#   when computing the parent mem_size you should add the mem_size of this
+#   part as extra_size
+
+def write_parser_helpers(writer):
+    if writer.is_generated("helper", "demarshaller"):
+        return
+
+    writer.set_is_generated("helper", "demarshaller")
+
+    writer = writer.function_helper()
+
+    writer.writeln("#ifdef WORDS_BIGENDIAN")
+    for size in [8, 16, 32, 64]:
+        for sign in ["", "u"]:
+            utype = "uint%d" % (size)
+            type = "%sint%d" % (sign, size)
+            swap = "SPICE_BYTESWAP%d" % size
+            if size == 8:
+                writer.macro("read_%s" % type, "ptr", "(*((%s_t *)(ptr)))" % type)
+                writer.macro("write_%s" % type, "ptr, val", "*(%s_t *)(ptr) = val" % (type))
+            else:
+                writer.macro("read_%s" % type, "ptr", "((%s_t)%s(*((%s_t *)(ptr))))" % (type, swap, utype))
+                writer.macro("write_%s" % type, "ptr, val", "*(%s_t *)(ptr) = %s((%s_t)val)" % (utype, swap, utype))
+    writer.writeln("#else")
+    for size in [8, 16, 32, 64]:
+        for sign in ["", "u"]:
+            type = "%sint%d" % (sign, size)
+            writer.macro("read_%s" % type, "ptr", "(*((%s_t *)(ptr)))" % type)
+            writer.macro("write_%s" % type, "ptr, val", "(*((%s_t *)(ptr))) = val" % type)
+    writer.writeln("#endif")
+
+    for size in [8, 16, 32, 64]:
+        for sign in ["", "u"]:
+            writer.newline()
+            type = "%sint%d" % (sign, size)
+            ctype = "%s_t" % type
+            scope = writer.function("SPICE_GNUC_UNUSED consume_%s" % type, ctype, "uint8_t **ptr", True)
+            scope.variable_def(ctype, "val")
+            writer.assign("val", "read_%s(*ptr)" % type)
+            writer.increment("*ptr", size // 8)
+            writer.statement("return val")
+            writer.end_block()
+
+    writer.newline()
+    writer.statement("typedef struct PointerInfo PointerInfo")
+    writer.statement("typedef void (*message_destructor_t)(uint8_t *message)")
+    writer.statement("typedef uint8_t * (*parse_func_t)(uint8_t *message_start, uint8_t *message_end, uint8_t *struct_data, PointerInfo *ptr_info, int minor)")
+    writer.statement("typedef uint8_t * (*parse_msg_func_t)(uint8_t *message_start, uint8_t *message_end, int minor, size_t *size_out, message_destructor_t *free_message)")
+    writer.statement("typedef uint8_t * (*spice_parse_channel_func_t)(uint8_t *message_start, uint8_t *message_end, uint16_t message_type, int minor, size_t *size_out, message_destructor_t *free_message)")
+
+    writer.newline()
+    writer.begin_block("struct PointerInfo")
+    writer.variable_def("uint64_t", "offset")
+    writer.variable_def("parse_func_t", "parse")
+    writer.variable_def("void **", "dest")
+    writer.variable_def("uint32_t", "nelements")
+    writer.end_block(semicolon=True)
+
+def write_read_primitive(writer, start, container, name, scope):
+    m = container.lookup_member(name)
+    assert(m.is_primitive())
+    writer.assign("pos", start + " + " + container.get_nw_offset(m, "", "__nw_size"))
+    writer.error_check("pos + %s > message_end" % m.member_type.get_fixed_nw_size())
+
+    var = "%s__value" % (name.replace(".", "_"))
+    if not scope.variable_defined(var):
+        scope.variable_def(m.member_type.c_type(), var)
+    writer.assign(var, "read_%s(pos)" % (m.member_type.primitive_type()))
+    return var
+
+def write_write_primitive(writer, start, container, name, val):
+    m = container.lookup_member(name)
+    assert(m.is_primitive())
+    writer.assign("pos", start + " + " + container.get_nw_offset(m, "", "__nw_size"))
+
+    var = "%s__value" % (name)
+    writer.statement("write_%s(pos, %s)" % (m.member_type.primitive_type(), val))
+    return var
+
+def write_read_primitive_item(writer, item, scope):
+    assert(item.type.is_primitive())
+    writer.assign("pos", item.get_position())
+    writer.error_check("pos + %s > message_end" % item.type.get_fixed_nw_size())
+    var = "%s__value" % (item.subprefix.replace(".", "_"))
+    scope.variable_def(item.type.c_type(), var)
+    writer.assign(var, "read_%s(pos)" % (item.type.primitive_type()))
+    return var
+
+class ItemInfo:
+    def __init__(self, type, prefix, position):
+        self.type = type
+        self.prefix = prefix
+        self.subprefix = prefix
+        self.position = position
+        self.member = None
+
+    def nw_size(self):
+        return self.prefix + "__nw_size"
+
+    def mem_size(self):
+        return self.prefix + "__mem_size"
+
+    def extra_size(self):
+        return self.prefix + "__extra_size"
+
+    def get_position(self):
+        return self.position
+
+class MemberItemInfo(ItemInfo):
+    def __init__(self, member, mprefix, container, start):
+        if not member.is_switch():
+            self.type = member.member_type
+        self.prefix = member.name
+        if mprefix:
+            mprefix = mprefix + "_"
+            self.prefix = mprefix + self.prefix
+        self.subprefix = member.name
+        self.position = "(%s + %s)" % (start, container.get_nw_offset(member, mprefix or "", "__nw_size"))
+        self.member = member
+
+def write_validate_switch_member(writer, mprefix, container, switch_member, scope, parent_scope, start,
+                                 want_nw_size, want_mem_size, want_extra_size):
+    var = container.lookup_member(switch_member.variable)
+    var_type = var.member_type
+
+    v = write_read_primitive(writer, start, container, switch_member.variable, parent_scope)
+
+    item = MemberItemInfo(switch_member, mprefix, container, start)
+
+    first = True
+    for c in switch_member.cases:
+        check = c.get_check(v, var_type)
+        m = c.member
+        with writer.if_block(check, not first, False) as if_scope:
+            item.type = c.member.member_type
+            item.subprefix = item.prefix + "_" + m.name
+            item.member = c.member
+
+            all_as_extra_size = m.is_extra_size() and want_extra_size
+            if not want_mem_size and all_as_extra_size and not scope.variable_defined(item.mem_size()):
+                scope.variable_def("uint32_t", item.mem_size())
+
+            sub_want_mem_size = want_mem_size or all_as_extra_size
+            sub_want_extra_size = want_extra_size and not all_as_extra_size
+
+            write_validate_item(writer, container, item, if_scope, scope, start,
+                                want_nw_size, sub_want_mem_size, sub_want_extra_size)
+
+            if all_as_extra_size:
+                writer.assign(item.extra_size(), item.mem_size())
+
+        first = False
+
+    with writer.block(" else"):
+        if want_nw_size:
+            writer.assign(item.nw_size(), 0)
+        if want_mem_size:
+            writer.assign(item.mem_size(), 0)
+        if want_extra_size:
+            writer.assign(item.extra_size(), 0)
+
+    writer.newline()
+
+def write_validate_struct_function(writer, struct):
+    validate_function = "validate_%s" % struct.c_type()
+    if writer.is_generated("validator", validate_function):
+        return validate_function
+
+    writer.set_is_generated("validator", validate_function)
+    writer = writer.function_helper()
+    scope = writer.function(validate_function, "static intptr_t", "uint8_t *message_start, uint8_t *message_end, uint64_t offset, SPICE_GNUC_UNUSED int minor")
+    scope.variable_def("uint8_t *", "start = message_start + offset")
+    scope.variable_def("SPICE_GNUC_UNUSED uint8_t *", "pos")
+    scope.variable_def("size_t", "mem_size", "nw_size")
+    num_pointers = struct.get_num_pointers()
+    if  num_pointers != 0:
+        scope.variable_def("SPICE_GNUC_UNUSED intptr_t", "ptr_size")
+
+    writer.newline()
+    with writer.if_block("offset == 0"):
+        writer.statement("return 0")
+
+    writer.newline()
+    writer.error_check("start >= message_end")
+
+    writer.newline()
+    write_validate_container(writer, None, struct, "start", scope, True, True, False)
+
+    writer.newline()
+    writer.comment("Check if struct fits in reported side").newline()
+    writer.error_check("start + nw_size > message_end")
+
+    writer.statement("return mem_size")
+
+    writer.newline()
+    writer.label("error")
+    writer.statement("return -1")
+
+    writer.end_block()
+
+    return validate_function
+
+def write_validate_pointer_item(writer, container, item, scope, parent_scope, start,
+                                want_nw_size, want_mem_size, want_extra_size):
+    if want_nw_size:
+        writer.assign(item.nw_size(), item.type.get_fixed_nw_size())
+
+    if want_mem_size or want_extra_size:
+        target_type = item.type.target_type
+
+        v = write_read_primitive_item(writer, item, scope)
+        if item.type.has_attr("nonnull"):
+            writer.error_check("%s == 0" % v)
+
+        # pointer target is struct, or array of primitives
+        # if array, need no function check
+
+        if target_type.is_array():
+            writer.error_check("message_start + %s >= message_end" % v)
+
+
+            assert target_type.element_type.is_primitive()
+
+            array_item = ItemInfo(target_type, "%s__array" % item.prefix, start)
+            scope.variable_def("uint32_t", array_item.nw_size())
+            # don't create a variable that isn't used, fixes -Werror=unused-but-set-variable
+            need_mem_size = want_mem_size or (
+                want_extra_size and not item.member.has_attr("chunk")
+                and not target_type.is_cstring_length())
+            if need_mem_size:
+                scope.variable_def("uint32_t", array_item.mem_size())
+            if target_type.is_cstring_length():
+                writer.assign(array_item.nw_size(), "spice_strnlen((char *)message_start + %s, message_end - (message_start + %s))" % (v, v))
+                writer.error_check("*(message_start + %s + %s) != 0" % (v, array_item.nw_size()))
+            else:
+                write_validate_array_item(writer, container, array_item, scope, parent_scope, start,
+                                          True, want_mem_size=need_mem_size, want_extra_size=False)
+                writer.error_check("message_start + %s + %s > message_end" % (v, array_item.nw_size()))
+
+            if want_extra_size:
+                if item.member and item.member.has_attr("chunk"):
+                    writer.assign(item.extra_size(), "sizeof(SpiceChunks) + sizeof(SpiceChunk)")
+                elif item.member and item.member.has_attr("nocopy"):
+                    writer.comment("@nocopy, so no extra size").newline()
+                    writer.assign(item.extra_size(), 0)
+                elif target_type.element_type.get_fixed_nw_size == 1:
+                    writer.assign(item.extra_size(), array_item.mem_size())
+                # If not bytes or zero, add padding needed for alignment
+                else:
+                    writer.assign(item.extra_size(), "%s + /* for alignment */ 3" % array_item.mem_size())
+            if want_mem_size:
+                writer.assign(item.mem_size(), "sizeof(void *) + %s" % array_item.mem_size())
+
+        elif target_type.is_struct():
+            validate_function = write_validate_struct_function(writer, target_type)
+            writer.assign("ptr_size", "%s(message_start, message_end, %s, minor)" % (validate_function, v))
+            writer.error_check("ptr_size < 0")
+
+            if want_extra_size:
+                writer.assign(item.extra_size(), "ptr_size + /* for alignment */ 3")
+            if want_mem_size:
+                writer.assign(item.mem_size(), "sizeof(void *) + ptr_size")
+        else:
+            raise NotImplementedError("pointer to unsupported type %s" % target_type)
+
+
+def write_validate_array_item(writer, container, item, scope, parent_scope, start,
+                              want_nw_size, want_mem_size, want_extra_size):
+    array = item.type
+    is_byte_size = False
+    element_type = array.element_type
+    if array.is_bytes_length():
+        nelements = "%s__nbytes" %(item.prefix)
+        real_nelements = "%s__nelements" %(item.prefix)
+        if not parent_scope.variable_defined(real_nelements):
+            parent_scope.variable_def("uint32_t", real_nelements)
+    else:
+        nelements = "%s__nelements" %(item.prefix)
+    if not parent_scope.variable_defined(nelements):
+        parent_scope.variable_def("uint32_t", nelements)
+
+    if array.is_constant_length():
+        writer.assign(nelements, array.size)
+    elif array.is_remaining_length():
+        if element_type.is_fixed_nw_size():
+            if element_type.get_fixed_nw_size() == 1:
+                writer.assign(nelements, "message_end - %s" % item.get_position())
+            else:
+                writer.assign(nelements, "(message_end - %s) / (%s)" %(item.get_position(), element_type.get_fixed_nw_size()))
+        else:
+            raise NotImplementedError("TODO array[] of dynamic element size not done yet")
+    elif array.is_identifier_length():
+        v = write_read_primitive(writer, start, container, array.size, scope)
+        writer.assign(nelements, v)
+    elif array.is_image_size_length():
+        bpp = array.size[1]
+        width = array.size[2]
+        rows = array.size[3]
+        width_v = write_read_primitive(writer, start, container, width, scope)
+        rows_v = write_read_primitive(writer, start, container, rows, scope)
+        # TODO: Handle multiplication overflow
+        if bpp == 8:
+            writer.assign(nelements, "%s * %s" % (width_v, rows_v))
+        elif bpp == 1:
+            writer.assign(nelements, "((%s + 7) / 8 ) * %s" % (width_v, rows_v))
+        else:
+            writer.assign(nelements, "((%s * %s + 7) / 8 ) * %s" % (bpp, width_v, rows_v))
+    elif array.is_bytes_length():
+        is_byte_size = True
+        v = write_read_primitive(writer, start, container, array.size[1], scope)
+        writer.assign(nelements, v)
+        writer.assign(real_nelements, 0)
+    elif array.is_cstring_length():
+        writer.todo("cstring array size type not handled yet")
+    else:
+        writer.todo("array size type not handled yet")
+
+    writer.newline()
+
+    nw_size = item.nw_size()
+    mem_size = item.mem_size()
+    extra_size = item.extra_size()
+
+    if is_byte_size and want_nw_size:
+        writer.assign(nw_size, nelements)
+        want_nw_size = False
+
+    if element_type.is_fixed_nw_size() and want_nw_size:
+        element_size = element_type.get_fixed_nw_size()
+        # TODO: Overflow check the multiplication
+        if element_size == 1:
+            writer.assign(nw_size, nelements)
+        else:
+            writer.assign(nw_size, "(%s) * %s" % (element_size, nelements))
+        want_nw_size = False
+
+    if array.has_attr("as_ptr") and want_mem_size:
+        writer.assign(mem_size, "sizeof(void *)")
+        want_mem_size = False
+
+    if array.has_attr("chunk"):
+        if want_mem_size:
+            writer.assign(extra_size, "sizeof(SpiceChunks *)")
+            want_mem_size = False
+        if want_extra_size:
+            writer.assign(extra_size, "sizeof(SpiceChunks) + sizeof(SpiceChunk)")
+            want_extra_size = False
+
+    if element_type.is_fixed_sizeof() and want_mem_size and not is_byte_size:
+        # TODO: Overflow check the multiplication
+        if array.has_attr("ptr_array"):
+            writer.assign(mem_size, "sizeof(void *) + SPICE_ALIGN(%s * %s, 4)" % (element_type.sizeof(), nelements))
+        else:
+            writer.assign(mem_size, "%s * %s" % (element_type.sizeof(), nelements))
+        want_mem_size = False
+
+    if not element_type.contains_extra_size() and want_extra_size:
+        writer.assign(extra_size, 0)
+        want_extra_size = False
+
+    if not (want_mem_size or want_nw_size or want_extra_size):
+        return
+
+    start2 = codegen.increment_identifier(start)
+    scope.variable_def("uint8_t *", "%s = %s" % (start2, item.get_position()))
+    if is_byte_size:
+        start2_end = "%s_array_end" % start2
+        scope.variable_def("uint8_t *", start2_end)
+
+    element_item = ItemInfo(element_type, "%s__element" % item.prefix, start2)
+
+    element_nw_size = element_item.nw_size()
+    element_mem_size = element_item.mem_size()
+    element_extra_size = element_item.extra_size()
+    scope.variable_def("uint32_t", element_nw_size)
+    scope.variable_def("uint32_t", element_mem_size)
+    if want_extra_size:
+        scope.variable_def("uint32_t", element_extra_size)
+
+    if want_nw_size:
+        writer.assign(nw_size, 0)
+    if want_mem_size:
+        writer.assign(mem_size, 0)
+    if want_extra_size:
+        writer.assign(extra_size, 0)
+
+    want_element_nw_size = want_nw_size
+    if element_type.is_fixed_nw_size():
+        start_increment = element_type.get_fixed_nw_size()
+    else:
+        want_element_nw_size = True
+        start_increment = element_nw_size
+
+    if is_byte_size:
+        writer.assign(start2_end, "%s + %s" % (start2, nelements))
+
+    with writer.index(no_block = is_byte_size) as index:
+        with writer.while_loop("%s < %s" % (start2, start2_end) ) if is_byte_size else writer.for_loop(index, nelements) as scope:
+            if is_byte_size:
+                writer.increment(real_nelements, 1)
+            write_validate_item(writer, container, element_item, scope, parent_scope, start2,
+                                want_element_nw_size, want_mem_size, want_extra_size)
+
+            if want_nw_size:
+                writer.increment(nw_size, element_nw_size)
+            if want_mem_size:
+                if array.has_attr("ptr_array"):
+                    writer.increment(mem_size, "sizeof(void *) + SPICE_ALIGN(%s, 4)" % element_mem_size)
+                else:
+                    writer.increment(mem_size, element_mem_size)
+            if want_extra_size:
+                writer.increment(extra_size, element_extra_size)
+
+            writer.increment(start2, start_increment)
+    if is_byte_size:
+        writer.error_check("%s != %s" % (start2, start2_end))
+        write_write_primitive(writer, start, container, array.size[1], real_nelements)
+
+def write_validate_struct_item(writer, container, item, scope, parent_scope, start,
+                               want_nw_size, want_mem_size, want_extra_size):
+    struct = item.type
+    start2 = codegen.increment_identifier(start)
+    scope.variable_def("SPICE_GNUC_UNUSED uint8_t *", start2 + " = %s" % (item.get_position()))
+
+    write_validate_container(writer, item.prefix, struct, start2, scope, want_nw_size, want_mem_size, want_extra_size)
+
+def write_validate_primitive_item(writer, container, item, scope, parent_scope, start,
+                                  want_nw_size, want_mem_size, want_extra_size):
+    if want_nw_size:
+        nw_size = item.nw_size()
+        writer.assign(nw_size, item.type.get_fixed_nw_size())
+    if want_mem_size:
+        mem_size = item.mem_size()
+        writer.assign(mem_size, item.type.sizeof())
+    if want_extra_size:
+        writer.assign(item.extra_size(), 0)
+
+def write_validate_item(writer, container, item, scope, parent_scope, start,
+                        want_nw_size, want_mem_size, want_extra_size):
+    if item.member and item.member.has_attr("to_ptr"):
+        want_nw_size = True
+    if item.type.is_pointer():
+        write_validate_pointer_item(writer, container, item, scope, parent_scope, start,
+                                    want_nw_size, want_mem_size, want_extra_size)
+    elif item.type.is_array():
+        write_validate_array_item(writer, container, item, scope, parent_scope, start,
+                                  want_nw_size, want_mem_size, want_extra_size)
+    elif item.type.is_struct():
+        write_validate_struct_item(writer, container, item, scope, parent_scope, start,
+                                   want_nw_size, want_mem_size, want_extra_size)
+    elif item.type.is_primitive():
+        write_validate_primitive_item(writer, container, item, scope, parent_scope, start,
+                                      want_nw_size, want_mem_size, want_extra_size)
+    else:
+        writer.todo("Implement validation of %s" % item.type)
+
+    if item.member and item.member.has_attr("to_ptr"):
+        saved_size = "%s__saved_size" % item.member.name
+        writer.add_function_variable("uint32_t", saved_size + " = 0")
+        writer.assign(saved_size, item.nw_size())
+
+def write_validate_member(writer, mprefix, container, member, parent_scope, start,
+                          want_nw_size, want_mem_size, want_extra_size):
+    if member.has_attr("virtual"):
+        return
+
+    if member.has_minor_attr():
+        prefix = "if (minor >= %s)" % (member.get_minor_attr())
+        newline = False
+    else:
+        prefix = ""
+        newline = True
+    item = MemberItemInfo(member, mprefix, container, start)
+    with writer.block(prefix, newline=newline, comment=member.name) as scope:
+        if member.is_switch():
+            write_validate_switch_member(writer, mprefix, container, member, scope, parent_scope, start,
+                                         want_nw_size, want_mem_size, want_extra_size)
+        else:
+            write_validate_item(writer, container, item, scope, parent_scope, start,
+                                want_nw_size, want_mem_size, want_extra_size)
+
+    if member.has_minor_attr():
+        with writer.block(" else", comment = "minor < %s" % (member.get_minor_attr())):
+            if member.is_array():
+                nelements = "%s__nelements" %(item.prefix)
+                writer.assign(nelements, 0)
+            if want_nw_size:
+                writer.assign(item.nw_size(), 0)
+
+            if want_mem_size:
+                if member.is_fixed_sizeof():
+                    writer.assign(item.mem_size(), member.sizeof())
+                elif member.is_array():
+                    writer.assign(item.mem_size(), 0)
+                else:
+                    raise NotImplementedError("TODO minor check for non-constant items")
+
+            assert not want_extra_size
+
+def write_validate_container(writer, prefix, container, start, parent_scope, want_nw_size, want_mem_size, want_extra_size):
+    def prefix_m(prefix, m):
+        name = m.name
+        if prefix:
+            name = prefix + "_" + name
+        return name
+
+    for m in container.members:
+        sub_want_nw_size = want_nw_size and not m.is_fixed_nw_size()
+        sub_want_mem_size = m.is_extra_size() and want_mem_size
+        sub_want_extra_size = not m.is_extra_size() and m.contains_extra_size()
+        defs = ["size_t"]
+        name = prefix_m(prefix, m)
+        if sub_want_nw_size:
+
+            defs.append (name + "__nw_size")
+        if sub_want_mem_size:
+            defs.append (name + "__mem_size")
+        if sub_want_extra_size:
+            defs.append (name + "__extra_size")
+
+        if sub_want_nw_size or sub_want_mem_size or sub_want_extra_size:
+            parent_scope.variable_def(*defs)
+            write_validate_member(writer, prefix, container, m, parent_scope, start,
+                                  sub_want_nw_size, sub_want_mem_size, sub_want_extra_size)
+            writer.newline()
+
+    if want_nw_size:
+        if prefix:
+            nw_size = prefix + "__nw_size"
+        else:
+            nw_size = "nw_size"
+
+        size = 0
+        for m in container.members:
+            if m.is_fixed_nw_size():
+                size = size + m.get_fixed_nw_size()
+
+        nm_sum = str(size)
+        for m in container.members:
+            name = prefix_m(prefix, m)
+            if not m.is_fixed_nw_size():
+                nm_sum = nm_sum + " + " + name + "__nw_size"
+
+        writer.assign(nw_size, nm_sum)
+
+    if want_mem_size:
+        if prefix:
+            mem_size = prefix + "__mem_size"
+        else:
+            mem_size = "mem_size"
+
+        mem_sum = container.sizeof()
+        for m in container.members:
+            name = prefix_m(prefix, m)
+            if m.is_extra_size():
+                mem_sum = mem_sum + " + " + name + "__mem_size"
+            elif m.contains_extra_size():
+                mem_sum = mem_sum + " + " + name + "__extra_size"
+
+        writer.assign(mem_size, mem_sum)
+
+    if want_extra_size:
+        if prefix:
+            extra_size = prefix + "__extra_size"
+        else:
+            extra_size = "extra_size"
+
+        extra_sum = []
+        for m in container.members:
+            name = prefix_m(prefix, m)
+            if m.is_extra_size():
+                extra_sum.append(name + "__mem_size")
+            elif m.contains_extra_size():
+                extra_sum.append(name + "__extra_size")
+        writer.assign(extra_size, codegen.sum_array(extra_sum))
+
+class DemarshallingDestination:
+    def __init__(self):
+        pass
+
+    def child_at_end(self, writer, t):
+        return RootDemarshallingDestination(self, t.c_type(), t.sizeof())
+
+    def child_sub(self, member):
+        return SubDemarshallingDestination(self, member)
+
+    def declare(self, writer):
+        return writer.optional_block(self.reuse_scope)
+
+    def is_toplevel(self):
+        return self.parent_dest == None and not self.is_helper
+
+class RootDemarshallingDestination(DemarshallingDestination):
+    def __init__(self, parent_dest, c_type, sizeof, pointer = None):
+        self.is_helper = False
+        self.reuse_scope = None
+        self.parent_dest = parent_dest
+        if parent_dest:
+            self.base_var = codegen.increment_identifier(parent_dest.base_var)
+        else:
+            self.base_var = "out"
+        self.c_type = c_type
+        self.sizeof = sizeof
+        self.pointer = pointer # None == at "end"
+
+    def get_ref(self, member):
+        return self.base_var + "->" + member
+
+    def declare(self, writer):
+        if self.reuse_scope:
+            scope = self.reuse_scope
+        else:
+            writer.begin_block()
+            scope = writer.get_subwriter()
+
+        scope.variable_def(self.c_type + " *", self.base_var)
+        if not self.reuse_scope:
+            scope.newline()
+
+        if self.pointer:
+            writer.assign(self.base_var, "(%s *)%s" % (self.c_type, self.pointer))
+        else:
+            writer.assign(self.base_var, "(%s *)end" % (self.c_type))
+            writer.increment("end", self.sizeof)
+        writer.newline()
+
+        if self.reuse_scope:
+            return writer.no_block(self.reuse_scope)
+        else:
+            return writer.partial_block(scope)
+
+class SubDemarshallingDestination(DemarshallingDestination):
+    def __init__(self, parent_dest, member):
+        self.reuse_scope = None
+        self.parent_dest = parent_dest
+        self.base_var = parent_dest.base_var
+        self.member = member
+        self.is_helper = False
+
+    def get_ref(self, member):
+        return self.parent_dest.get_ref(self.member) + "." + member
+
+# Note: during parsing, byte_size types have been converted to count during validation
+def read_array_len(writer, prefix, array, dest, scope, is_ptr):
+    if is_ptr:
+        nelements = "%s__array__nelements" % prefix
+    else:
+        nelements = "%s__nelements" % prefix
+    if dest.is_toplevel() and scope.variable_defined(nelements):
+        return nelements # Already there for toplevel, need not recalculate
+    element_type = array.element_type
+    scope.variable_def("uint32_t", nelements)
+    if array.is_constant_length():
+        writer.assign(nelements, array.size)
+    elif array.is_identifier_length():
+        writer.assign(nelements, dest.get_ref(array.size))
+    elif array.is_remaining_length():
+        if element_type.is_fixed_nw_size():
+            writer.assign(nelements, "(message_end - in) / (%s)" %(element_type.get_fixed_nw_size()))
+        else:
+            raise NotImplementedError("TODO array[] of dynamic element size not done yet")
+    elif array.is_image_size_length():
+        bpp = array.size[1]
+        width = array.size[2]
+        rows = array.size[3]
+        width_v = dest.get_ref(width)
+        rows_v = dest.get_ref(rows)
+        # TODO: Handle multiplication overflow
+        if bpp == 8:
+            writer.assign(nelements, "%s * %s" % (width_v, rows_v))
+        elif bpp == 1:
+            writer.assign(nelements, "((%s + 7) / 8 ) * %s" % (width_v, rows_v))
+        else:
+            writer.assign(nelements, "((%s * %s + 7) / 8 ) * %s" % (bpp, width_v, rows_v))
+    elif array.is_bytes_length():
+        writer.assign(nelements, dest.get_ref(array.size[2]))
+    else:
+        raise NotImplementedError("TODO array size type not handled yet")
+    return nelements
+
+def write_switch_parser(writer, container, switch, dest, scope):
+    var = container.lookup_member(switch.variable)
+    var_type = var.member_type
+
+    if switch.has_attr("fixedsize"):
+        scope.variable_def("uint8_t *", "in_save")
+        writer.assign("in_save", "in")
+
+    first = True
+    for c in switch.cases:
+        check = c.get_check(dest.get_ref(switch.variable), var_type)
+        m = c.member
+        with writer.if_block(check, not first, False) as block:
+            t = m.member_type
+            if switch.has_end_attr():
+                dest2 = dest.child_at_end(writer, m.member_type)
+            elif switch.has_attr("anon"):
+                if t.is_struct() and not m.has_attr("to_ptr"):
+                    dest2 = dest.child_sub(m.name)
+                else:
+                    dest2 = dest
+            else:
+                if t.is_struct():
+                    dest2 = dest.child_sub(switch.name + "." + m.name)
+                else:
+                    dest2 = dest.child_sub(switch.name)
+            dest2.reuse_scope = block
+
+            if m.has_attr("to_ptr"):
+                write_parse_to_pointer(writer, t, False, dest2, m.name, block)
+            elif t.is_pointer():
+                write_parse_pointer(writer, t, False, dest2, m.name, block)
+            elif t.is_struct():
+                write_container_parser(writer, t, dest2)
+            elif t.is_primitive():
+                if m.has_attr("zero"):
+                    writer.statement("consume_%s(&in)" % (t.primitive_type()))
+                else:
+                    writer.assign(dest2.get_ref(m.name), "consume_%s(&in)" % (t.primitive_type()))
+                #TODO validate e.g. flags and enums
+            elif t.is_array():
+                nelements = read_array_len(writer, m.name, t, dest, block, False)
+                write_array_parser(writer, m, nelements, t, dest2, block)
+            else:
+                writer.todo("Can't handle type %s" % m.member_type)
+
+        first = False
+
+    writer.newline()
+
+    if switch.has_attr("fixedsize"):
+        writer.assign("in", "in_save + %s" % switch.get_fixed_nw_size())
+
+def write_parse_ptr_function(writer, target_type):
+    if target_type.is_array():
+        parse_function = "parse_array_%s" % target_type.element_type.primitive_type()
+    else:
+        parse_function = "parse_struct_%s" % target_type.c_type()
+    if writer.is_generated("parser", parse_function):
+        return parse_function
+
+    writer.set_is_generated("parser", parse_function)
+
+    writer = writer.function_helper()
+    scope = writer.function(parse_function, "static uint8_t *", "uint8_t *message_start, SPICE_GNUC_UNUSED uint8_t *message_end, uint8_t *struct_data, PointerInfo *this_ptr_info, SPICE_GNUC_UNUSED int minor")
+    scope.variable_def("uint8_t *", "in = message_start + this_ptr_info->offset")
+    scope.variable_def("uint8_t *", "end")
+
+    num_pointers = target_type.get_num_pointers()
+    if  num_pointers != 0:
+        scope.variable_def("SPICE_GNUC_UNUSED intptr_t", "ptr_size")
+        scope.variable_def("uint32_t", "n_ptr=0")
+        scope.variable_def("PointerInfo", "ptr_info[%s]" % num_pointers)
+
+    writer.newline()
+    if target_type.is_array():
+        writer.assign("end", "struct_data")
+    else:
+        writer.assign("end", "struct_data + %s" % (target_type.sizeof()))
+
+    dest = RootDemarshallingDestination(None, target_type.c_type(), target_type.sizeof(), "struct_data")
+    dest.is_helper = True
+    dest.reuse_scope = scope
+    if target_type.is_array():
+        write_array_parser(writer, None, "this_ptr_info->nelements", target_type, dest, scope)
+    else:
+        write_container_parser(writer, target_type, dest)
+
+    if num_pointers != 0:
+        write_ptr_info_check(writer)
+
+    writer.statement("return end")
+
+    if writer.has_error_check:
+        writer.newline()
+        writer.label("error")
+        writer.statement("return NULL")
+
+    writer.end_block()
+
+    return parse_function
+
+def write_array_parser(writer, member, nelements, array, dest, scope):
+    is_byte_size = array.is_bytes_length()
+
+    element_type = array.element_type
+    if member:
+        array_start = dest.get_ref(member.name)
+        at_end = member.has_attr("end")
+    else:
+        array_start = "end"
+        at_end = True
+
+    if element_type == ptypes.uint8 or element_type == ptypes.int8:
+        writer.statement("memcpy(%s, in, %s)" % (array_start, nelements))
+        writer.increment("in", nelements)
+        if at_end:
+            writer.increment("end", nelements)
+    else:
+        with writer.index() as index:
+            if member:
+                array_pos = "%s[%s]" % (array_start, index)
+            else:
+                array_pos = "*(%s *)end" % (element_type.c_type())
+
+            if array.has_attr("ptr_array"):
+                scope.variable_def("void **", "ptr_array")
+                scope.variable_def("int", "ptr_array_index")
+                writer.assign("ptr_array_index", 0)
+                writer.assign("ptr_array", "(void **)%s" % array_start)
+                writer.increment("end", "sizeof(void *) * %s" % nelements)
+                array_start = "end"
+                array_pos = "*(%s *)end" % (element_type.c_type())
+                at_end = True
+
+            with writer.for_loop(index, nelements) as array_scope:
+                if array.has_attr("ptr_array"):
+                    writer.statement("ptr_array[ptr_array_index++] = end")
+                if element_type.is_primitive():
+                    writer.statement("%s = consume_%s(&in)" % (array_pos, element_type.primitive_type()))
+                    if at_end:
+                        writer.increment("end", element_type.sizeof())
+                else:
+                    if at_end:
+                        dest2 = dest.child_at_end(writer, element_type)
+                    else:
+                        dest2 = RootDemarshallingDestination(dest, element_type.c_type(), element_type.c_type(), array_pos)
+                    dest2.reuse_scope = array_scope
+                    write_container_parser(writer, element_type, dest2)
+                if array.has_attr("ptr_array"):
+                    writer.comment("Align ptr_array element to 4 bytes").newline()
+                    writer.assign("end", "(uint8_t *)SPICE_ALIGN((size_t)end, 4)")
+
+def write_parse_pointer_core(writer, target_type, offset, at_end, dest, member_name, scope):
+    writer.assign("ptr_info[n_ptr].offset", offset)
+    writer.assign("ptr_info[n_ptr].parse", write_parse_ptr_function(writer, target_type))
+    if at_end:
+        writer.assign("ptr_info[n_ptr].dest", "(void **)end")
+        writer.increment("end", "sizeof(void *)")
+    else:
+        writer.assign("ptr_info[n_ptr].dest", "(void **)&%s" % dest.get_ref(member_name))
+    if target_type.is_array():
+        nelements = read_array_len(writer, member_name, target_type, dest, scope, True)
+        writer.assign("ptr_info[n_ptr].nelements", nelements)
+
+    writer.statement("n_ptr++")
+
+def write_parse_pointer(writer, t, at_end, dest, member_name, scope):
+    write_parse_pointer_core(writer, t.target_type, "consume_%s(&in)" % t.primitive_type(),
+                             at_end, dest, member_name, scope)
+
+def write_parse_to_pointer(writer, t, at_end, dest, member_name, scope):
+    write_parse_pointer_core(writer, t, "in - start",
+                             at_end, dest, member_name, scope)
+    writer.increment("in", "%s__saved_size" % member_name)
+
+def write_member_parser(writer, container, member, dest, scope):
+    if member.has_attr("virtual"):
+        writer.assign(dest.get_ref(member.name), member.attributes["virtual"][0])
+        return
+
+    if member.is_switch():
+        write_switch_parser(writer, container, member, dest, scope)
+        return
+
+    t = member.member_type
+
+    if member.has_attr("to_ptr"):
+        write_parse_to_pointer(writer, t, member.has_end_attr(), dest, member.name, scope)
+    elif t.is_pointer():
+        if member.has_attr("chunk"):
+            assert(t.target_type.is_array())
+            nelements = read_array_len(writer, member.name, t.target_type, dest, scope, True)
+            writer.comment("Reuse data from network message as chunk").newline()
+            scope.variable_def("SpiceChunks *", "chunks")
+            writer.assign("chunks", "(SpiceChunks *)end")
+            writer.increment("end", "sizeof(SpiceChunks) + sizeof(SpiceChunk)")
+            writer.assign(dest.get_ref(member.name), "chunks")
+            writer.assign("chunks->data_size", nelements)
+            writer.assign("chunks->flags", 0)
+            writer.assign("chunks->num_chunks", 1)
+            writer.assign("chunks->chunk[0].len", nelements)
+            writer.assign("chunks->chunk[0].data", "message_start + consume_%s(&in)" % t.primitive_type())
+        elif member.has_attr("nocopy"):
+            writer.comment("Reuse data from network message").newline()
+            writer.assign(dest.get_ref(member.name), "(size_t)(message_start + consume_%s(&in))" % t.primitive_type())
+        else:
+            write_parse_pointer(writer, t, member.has_end_attr(), dest, member.name, scope)
+    elif t.is_primitive():
+        if member.has_attr("zero"):
+            writer.statement("consume_%s(&in)" % t.primitive_type())
+        elif member.has_end_attr():
+            writer.statement("*(%s *)end = consume_%s(&in)" % (t.c_type(), t.primitive_type()))
+            writer.increment("end", t.sizeof())
+        else:
+            if member.has_attr("bytes_count"):
+                dest_var = dest.get_ref(member.attributes["bytes_count"][0])
+            else:
+                dest_var = dest.get_ref(member.name)
+            writer.assign(dest_var, "consume_%s(&in)" % (t.primitive_type()))
+        #TODO validate e.g. flags and enums
+    elif t.is_array():
+        nelements = read_array_len(writer, member.name, t, dest, scope, False)
+        if member.has_attr("chunk") and t.element_type.is_fixed_nw_size() and t.element_type.get_fixed_nw_size() == 1:
+            writer.comment("use array as chunk").newline()
+
+            scope.variable_def("SpiceChunks *", "chunks")
+            writer.assign("chunks", "(SpiceChunks *)end")
+            writer.increment("end", "sizeof(SpiceChunks) + sizeof(SpiceChunk)")
+            writer.assign(dest.get_ref(member.name), "chunks")
+            writer.assign("chunks->data_size", nelements)
+            writer.assign("chunks->flags", 0)
+            writer.assign("chunks->num_chunks", 1)
+            writer.assign("chunks->chunk[0].len", nelements)
+            writer.assign("chunks->chunk[0].data", "in")
+            writer.increment("in", "%s" % (nelements))
+        elif member.has_attr("as_ptr") and t.element_type.is_fixed_nw_size():
+            writer.comment("use array as pointer").newline()
+            writer.assign(dest.get_ref(member.name), "(%s *)in" % t.element_type.c_type())
+            len_var = member.attributes["as_ptr"]
+            if len(len_var) > 0:
+                writer.assign(dest.get_ref(len_var[0]), nelements)
+            el_size = t.element_type.get_fixed_nw_size()
+            if el_size != 1:
+                writer.increment("in", "%s * %s" % (nelements, el_size))
+            else:
+                writer.increment("in", "%s" % (nelements))
+        else:
+            write_array_parser(writer, member, nelements, t, dest, scope)
+    elif t.is_struct():
+        if member.has_end_attr():
+            dest2 = dest.child_at_end(writer, t)
+        else:
+            dest2 = dest.child_sub(member.name)
+        writer.comment(member.name)
+        write_container_parser(writer, t, dest2)
+    else:
+        raise NotImplementedError("TODO can't handle parsing of %s" % t)
+
+def write_container_parser(writer, container, dest):
+    with dest.declare(writer) as scope:
+        for m in container.members:
+            if m.has_minor_attr():
+                writer.begin_block("if (minor >= %s)" % m.get_minor_attr())
+            write_member_parser(writer, container, m, dest, scope)
+            if m.has_minor_attr():
+                # We need to zero out the fixed part of all optional fields
+                if not m.member_type.is_array():
+                    writer.end_block(newline=False)
+                    writer.begin_block(" else")
+                    # TODO: This is not right for fields that don't exist in the struct
+                    if m.has_attr("zero"):
+                        pass
+                    elif m.member_type.is_primitive():
+                        writer.assign(dest.get_ref(m.name), "0")
+                    elif m.is_fixed_sizeof():
+                        writer.statement("memset ((char *)&%s, 0, %s)" % (dest.get_ref(m.name), m.sizeof()))
+                    else:
+                        raise NotImplementedError("TODO Clear optional dynamic fields")
+                writer.end_block()
+
+def write_ptr_info_check(writer):
+    writer.newline()
+    with writer.index() as index:
+        with writer.for_loop(index, "n_ptr") as scope:
+            offset = "ptr_info[%s].offset" % index
+            function = "ptr_info[%s].parse" % index
+            dest = "ptr_info[%s].dest" % index
+            with writer.if_block("%s == 0" % offset, newline=False):
+                writer.assign("*%s" % dest, "NULL")
+            with writer.block(" else"):
+                writer.comment("Align to 32 bit").newline()
+                writer.assign("end", "(uint8_t *)SPICE_ALIGN((size_t)end, 4)")
+                writer.assign("*%s" % dest, "(void *)end")
+                writer.assign("end", "%s(message_start, message_end, end, &ptr_info[%s], minor)" % (function, index))
+                writer.error_check("end == NULL")
+    writer.newline()
+
+def write_nofree(writer):
+    if writer.is_generated("helper", "nofree"):
+        return
+    writer = writer.function_helper()
+    scope = writer.function("nofree", "static void", "SPICE_GNUC_UNUSED uint8_t *data")
+    writer.end_block()
+
+def write_msg_parser(writer, message):
+    msg_name = message.c_name()
+    function_name = "parse_%s" % msg_name
+    if writer.is_generated("demarshaller", function_name):
+        return function_name
+    writer.set_is_generated("demarshaller", function_name)
+
+    msg_type = message.c_type()
+    msg_sizeof = message.sizeof()
+
+    want_mem_size = (len(message.members) != 1 or message.members[0].is_fixed_nw_size()
+                         or not message.members[0].is_array())
+
+    writer.newline()
+    if message.has_attr("ifdef"):
+        writer.ifdef(message.attributes["ifdef"][0])
+    parent_scope = writer.function(function_name,
+                                   "uint8_t *",
+                                   "uint8_t *message_start, uint8_t *message_end, SPICE_GNUC_UNUSED int minor, size_t *size, message_destructor_t *free_message", True)
+    parent_scope.variable_def("SPICE_GNUC_UNUSED uint8_t *", "pos")
+    parent_scope.variable_def("uint8_t *", "start = message_start")
+    parent_scope.variable_def("uint8_t *", "data = NULL")
+    parent_scope.variable_def("size_t", "nw_size")
+    if want_mem_size:
+        parent_scope.variable_def("size_t", "mem_size")
+    if not message.has_attr("nocopy"):
+        parent_scope.variable_def("uint8_t *", "in", "end")
+    num_pointers = message.get_num_pointers()
+    if  num_pointers != 0:
+        parent_scope.variable_def("SPICE_GNUC_UNUSED intptr_t", "ptr_size")
+        parent_scope.variable_def("uint32_t", "n_ptr=0")
+        parent_scope.variable_def("PointerInfo", "ptr_info[%s]" % num_pointers)
+    writer.newline()
+
+    write_parser_helpers(writer)
+
+    write_validate_container(writer, None, message, "start", parent_scope, True,
+                             want_mem_size=want_mem_size, want_extra_size=False)
+
+    writer.newline()
+
+    writer.comment("Check if message fits in reported side").newline()
+    with writer.block("if (start + nw_size > message_end)"):
+        writer.statement("return NULL")
+
+    writer.newline().comment("Validated extents and calculated size").newline()
+
+    if message.has_attr("nocopy"):
+        write_nofree(writer)
+        writer.assign("data", "message_start")
+        writer.assign("*size", "message_end - message_start")
+        writer.assign("*free_message", "nofree")
+    else:
+        writer.assign("data", "(uint8_t *)malloc(mem_size)")
+        writer.error_check("data == NULL")
+        writer.assign("end", "data + %s" % (msg_sizeof))
+        writer.assign("in", "start").newline()
+
+        # avoid defined and assigned but not used warnings of gcc 4.6.0+
+        if message.is_extra_size() or not message.is_fixed_nw_size() or message.get_fixed_nw_size() > 0:
+            dest = RootDemarshallingDestination(None, msg_type, msg_sizeof, "data")
+            dest.reuse_scope = parent_scope
+            write_container_parser(writer, message, dest)
+
+        writer.newline()
+        writer.statement("assert(in <= message_end)")
+
+        if num_pointers != 0:
+            write_ptr_info_check(writer)
+
+        writer.statement("assert(end <= data + mem_size)")
+
+        writer.newline()
+        writer.assign("*size", "end - data")
+        writer.assign("*free_message", "(message_destructor_t) free")
+
+    writer.statement("return data")
+    writer.newline()
+    if writer.has_error_check:
+        writer.label("error")
+        with writer.block("if (data != NULL)"):
+            writer.statement("free(data)")
+        writer.statement("return NULL")
+    writer.end_block()
+
+    if message.has_attr("ifdef"):
+        writer.endif(message.attributes["ifdef"][0])
+
+    return function_name
+
+def write_channel_parser(writer, channel, server):
+    writer.newline()
+    ids = {}
+    min_id = 1000000
+    if server:
+        messages = channel.server_messages
+    else:
+        messages = channel.client_messages
+    for m in messages:
+        ids[m.value] = m
+
+    ranges = []
+    ids2 = ids.copy()
+    while len(ids2) > 0:
+        end = start = min(ids2.keys())
+        while end in ids2:
+            del ids2[end]
+            end = end + 1
+
+        ranges.append( (start, end) )
+
+    if server:
+        function_name = "parse_%s_msg" % channel.name
+    else:
+        function_name = "parse_%s_msgc" % channel.name
+    writer.newline()
+    if channel.has_attr("ifdef"):
+        writer.ifdef(channel.attributes["ifdef"][0])
+    scope = writer.function(function_name,
+                            "static uint8_t *",
+                            "uint8_t *message_start, uint8_t *message_end, uint16_t message_type, SPICE_GNUC_UNUSED int minor, size_t *size_out, message_destructor_t *free_message")
+
+    helpers = writer.function_helper()
+
+    d = 0
+    for r in ranges:
+        d = d + 1
+        writer.write("static parse_msg_func_t funcs%d[%d] = " % (d, r[1] - r[0]))
+        writer.begin_block()
+        for i in range(r[0], r[1]):
+            func = write_msg_parser(helpers, ids[i].message_type)
+            writer.write(func)
+            if i != r[1] -1:
+                writer.write(",")
+            writer.newline()
+
+        writer.end_block(semicolon = True)
+
+    d = 0
+    for r in ranges:
+        d = d + 1
+        with writer.if_block("message_type >= %d && message_type < %d" % (r[0], r[1]), d > 1, False):
+            writer.statement("return funcs%d[message_type-%d](message_start, message_end, minor, size_out, free_message)" % (d, r[0]))
+    writer.newline()
+
+    writer.statement("return NULL")
+    writer.end_block()
+    if channel.has_attr("ifdef"):
+        writer.endif(channel.attributes["ifdef"][0])
+
+    return function_name
+
+def write_get_channel_parser(writer, channel_parsers, max_channel, is_server):
+    writer.newline()
+    if is_server:
+        function_name = "spice_get_server_channel_parser" + writer.public_prefix
+    else:
+        function_name = "spice_get_client_channel_parser" + writer.public_prefix
+
+    scope = writer.function(function_name,
+                            "spice_parse_channel_func_t",
+                            "uint32_t channel, unsigned int *max_message_type")
+
+    writer.write("static struct {spice_parse_channel_func_t func; unsigned int max_messages; } channels[%d] = " % (max_channel+1))
+    writer.begin_block()
+    channel = None
+    for i in range(0, max_channel + 1):
+        if i in channel_parsers:
+            channel = channel_parsers[i][0]
+            if channel.has_attr("ifdef"):
+                writer.ifdef(channel.attributes["ifdef"][0])
+            writer.write("{ ")
+            writer.write(channel_parsers[i][1])
+            writer.write(", ")
+
+            max_msg = 0
+            if is_server:
+                messages = channel.server_messages
+            else:
+                messages = channel.client_messages
+            for m in messages:
+                max_msg = max(max_msg, m.value)
+            writer.write(max_msg)
+            writer.write("}")
+        else:
+            writer.write("{ NULL, 0 }")
+
+        if i != max_channel:
+            writer.write(",")
+        writer.newline()
+        if channel and channel.has_attr("ifdef"):
+            writer.ifdef_else(channel.attributes["ifdef"][0])
+            writer.write("{ NULL, 0 }")
+            if i != max_channel:
+                writer.write(",")
+            writer.newline()
+            writer.endif(channel.attributes["ifdef"][0])
+    writer.end_block(semicolon = True)
+
+    with writer.if_block("channel < %d" % (max_channel + 1)):
+        with writer.if_block("max_message_type != NULL"):
+            writer.assign("*max_message_type", "channels[channel].max_messages")
+        writer.statement("return channels[channel].func")
+
+    writer.statement("return NULL")
+    writer.end_block()
+
+
+def write_full_protocol_parser(writer, is_server):
+    writer.newline()
+    if is_server:
+        function_name = "spice_parse_msg"
+    else:
+        function_name = "spice_parse_reply"
+    scope = writer.function(function_name + writer.public_prefix,
+                            "uint8_t *",
+                            "uint8_t *message_start, uint8_t *message_end, uint32_t channel, uint16_t message_type, SPICE_GNUC_UNUSED int minor, size_t *size_out, message_destructor_t *free_message")
+    scope.variable_def("spice_parse_channel_func_t", "func" )
+
+    if is_server:
+        writer.assign("func", "spice_get_server_channel_parser%s(channel, NULL)" % writer.public_prefix)
+    else:
+        writer.assign("func", "spice_get_client_channel_parser%s(channel, NULL)" % writer.public_prefix)
+
+    with writer.if_block("func != NULL"):
+        writer.statement("return func(message_start, message_end, message_type, minor, size_out, free_message)")
+
+    writer.statement("return NULL")
+    writer.end_block()
+
+def write_protocol_parser(writer, proto, is_server):
+    max_channel = 0
+    parsers = {}
+
+    for channel in proto.channels:
+        max_channel = max(max_channel, channel.value)
+
+        parsers[channel.value] = (channel.channel_type, write_channel_parser(writer, channel.channel_type, is_server))
+
+    write_get_channel_parser(writer, parsers, max_channel, is_server)
+    write_full_protocol_parser(writer, is_server)
+
+def write_includes(writer):
+    writer.writeln("#include <string.h>")
+    writer.writeln("#include <assert.h>")
+    writer.writeln("#include <stdlib.h>")
+    writer.writeln("#include <stdio.h>")
+    writer.writeln("#include <spice/protocol.h>")
+    writer.writeln("#include <spice/macros.h>")
+    writer.writeln('#include <common/mem.h>')
+    writer.newline()
+    writer.writeln("#ifdef _MSC_VER")
+    writer.writeln("#pragma warning(disable:4101)")
+    writer.writeln("#endif")
diff --git a/python_modules/marshal.py b/python_modules/marshal.py
new file mode 100644
index 0000000..b77b910
--- /dev/null
+++ b/python_modules/marshal.py
@@ -0,0 +1,427 @@
+
+from . import ptypes
+from . import codegen
+
+def write_includes(writer):
+    writer.header.writeln("#include <spice/protocol.h>")
+    writer.header.writeln('#include "common/marshaller.h"')
+    writer.header.newline()
+    writer.header.writeln("#ifndef _GENERATED_HEADERS_H")
+    writer.header.writeln("#define _GENERATED_HEADERS_H")
+
+    writer.writeln("#include <string.h>")
+    writer.writeln("#include <assert.h>")
+    writer.writeln("#include <stdlib.h>")
+    writer.writeln("#include <stdio.h>")
+    writer.writeln("#include <spice/protocol.h>")
+    writer.writeln("#include <spice/macros.h>")
+    writer.writeln('#include "common/marshaller.h"')
+    writer.newline()
+    writer.writeln("#ifdef _MSC_VER")
+    writer.writeln("#pragma warning(disable:4101)")
+    writer.writeln("#pragma warning(disable:4018)")
+    writer.writeln("#endif")
+    writer.newline()
+
+class MarshallingSource:
+    def __init__(self):
+        pass
+
+    def child_at_end(self, t):
+        return RootMarshallingSource(self, t.c_type(), t.sizeof())
+
+    def child_sub(self, containee):
+        return SubMarshallingSource(self, containee)
+
+    def declare(self, writer):
+        return writer.optional_block(self.reuse_scope)
+
+    def is_toplevel(self):
+        return self.parent_src == None and not self.is_helper
+
+class RootMarshallingSource(MarshallingSource):
+    def __init__(self, parent_src, c_type, sizeof, pointer = None):
+        self.is_helper = False
+        self.reuse_scope = None
+        self.parent_src = parent_src
+        if parent_src:
+            self.base_var = codegen.increment_identifier(parent_src.base_var)
+        else:
+            self.base_var = "src"
+        self.c_type = c_type
+        self.sizeof = sizeof
+        self.pointer = pointer
+        assert pointer != None
+
+    def get_self_ref(self):
+        return self.base_var
+
+    def get_ref(self, member):
+        return self.base_var + "->" + member
+
+    def declare(self, writer):
+        if self.reuse_scope:
+            scope = self.reuse_scope
+        else:
+            writer.begin_block()
+            scope = writer.get_subwriter()
+
+        scope.variable_def(self.c_type + " *", self.base_var)
+        if not self.reuse_scope:
+            scope.newline()
+
+        writer.assign(self.base_var, "(%s *)%s" % (self.c_type, self.pointer))
+        writer.newline()
+
+        if self.reuse_scope:
+            return writer.no_block(self.reuse_scope)
+        else:
+            return writer.partial_block(scope)
+
+class SubMarshallingSource(MarshallingSource):
+    def __init__(self, parent_src, containee):
+        self.reuse_scope = None
+        self.parent_src = parent_src
+        self.base_var = parent_src.base_var
+        self.containee = containee
+        self.name = containee.name
+        self.is_helper = False
+
+    def get_self_ref(self):
+        if self.containee.has_attr("to_ptr"):
+            return "%s" % self.parent_src.get_ref(self.name)
+        else:
+            return "&%s" % self.parent_src.get_ref(self.name)
+
+    def get_ref(self, member):
+        if self.containee.has_attr("to_ptr"):
+            return self.parent_src.get_ref(self.name) + "->" + member
+        else:
+            return self.parent_src.get_ref(self.name) + "." + member
+
+def write_marshal_ptr_function(writer, target_type, is_helper=True):
+    if target_type.is_array():
+        marshal_function = "spice_marshall_array_%s" % target_type.element_type.primitive_type()
+    else:
+        marshal_function = "spice_marshall_%s" % target_type.name
+    if writer.is_generated("marshaller", marshal_function):
+        return marshal_function
+
+    writer.set_is_generated("marshaller", marshal_function)
+
+    names = target_type.get_pointer_names(False)
+    names_args = ""
+    if len(names) > 0:
+        n = [", SpiceMarshaller **%s_out" % name for name in names]
+        names_args = "".join(n)
+
+    header = writer.header
+    if is_helper:
+        writer = writer.function_helper()
+    writer.header = header
+    writer.out_prefix = ""
+    if target_type.is_array():
+        scope = writer.function(marshal_function, "SPICE_GNUC_UNUSED static void", "SpiceMarshaller *m, %s_t *ptr, unsigned count" % target_type.element_type.primitive_type() + names_args)
+    else:
+        scope = writer.function(marshal_function, "void", "SpiceMarshaller *m, %s *ptr" % target_type.c_type() + names_args)
+        header.writeln("void " + marshal_function + "(SpiceMarshaller *m, %s *msg" % target_type.c_type() + names_args + ");")
+    scope.variable_def("SPICE_GNUC_UNUSED SpiceMarshaller *", "m2")
+
+    for n in names:
+        writer.assign("*%s_out" % n, "NULL")
+
+    writer.newline()
+
+    if target_type.is_struct():
+        src = RootMarshallingSource(None, target_type.c_type(), target_type.sizeof(), "ptr")
+        src.reuse_scope = scope
+        write_container_marshaller(writer, target_type, src)
+    elif target_type.is_array() and target_type.element_type.is_primitive():
+        with writer.index() as index:
+            with writer.for_loop(index, "count") as array_scope:
+                writer.statement("spice_marshaller_add_%s(m, *ptr++)" % (target_type.element_type.primitive_type()))
+    else:
+        writer.todo("Unsuppored pointer marshaller type")
+
+    writer.end_block()
+
+    return marshal_function
+
+def get_array_size(array, container_src):
+    if array.is_constant_length():
+        return array.size
+    elif array.is_identifier_length():
+        return container_src.get_ref(array.size)
+    elif array.is_remaining_length():
+        raise NotImplementedError("remaining size array sizes marshalling not supported")
+    elif array.is_image_size_length():
+        bpp = array.size[1]
+        width = array.size[2]
+        rows = array.size[3]
+        width_v = container_src.get_ref(width)
+        rows_v = container_src.get_ref(rows)
+        # TODO: Handle multiplication overflow
+        if bpp == 8:
+            return "(unsigned) (%s * %s)" % (width_v, rows_v)
+        elif bpp == 1:
+            return "(unsigned) (((%s + 7) / 8 ) * %s)" % (width_v, rows_v)
+        else:
+            return "(unsigned) (((%s * %s + 7) / 8 ) * %s)" % (bpp, width_v, rows_v)
+    elif array.is_bytes_length():
+        return container_src.get_ref(array.size[2])
+    else:
+        raise NotImplementedError("TODO array size type not handled yet: %s"  % array)
+
+def write_array_marshaller(writer, member, array, container_src, scope):
+    element_type = array.element_type
+
+    if array.is_remaining_length():
+        writer.comment("Remaining data must be appended manually").newline()
+        return
+
+    nelements = get_array_size(array, container_src)
+    is_byte_size = array.is_bytes_length()
+
+    element = "%s__element" % member.name
+
+    if not scope.variable_defined(element):
+        if array.has_attr("ptr_array"):
+            stars = " **"
+        else:
+            stars = " *"
+        scope.variable_def(element_type.c_type() + stars, element)
+    element_array = element
+    if array.has_attr("ptr_array"):
+        element = "*" + element
+
+    writer.assign(element_array, container_src.get_ref(member.name))
+
+    if is_byte_size:
+        size_start_var = "%s__size_start" % member.name
+        scope.variable_def("size_t", size_start_var)
+        writer.assign(size_start_var, "spice_marshaller_get_size(m)")
+
+    with writer.index() as index:
+        with writer.for_loop(index, nelements) as array_scope:
+            if element_type.is_primitive():
+                writer.statement("spice_marshaller_add_%s(m, *%s)" % (element_type.primitive_type(), element))
+            elif element_type.is_struct():
+                src2 = RootMarshallingSource(container_src, element_type.c_type(), element_type.sizeof(), element)
+                src2.reuse_scope = array_scope
+                write_container_marshaller(writer, element_type, src2)
+            else:
+                writer.todo("array element unhandled type").newline()
+
+            writer.statement("%s++" % element_array)
+
+    if is_byte_size:
+        size_var = member.container.lookup_member(array.size[1])
+        size_var_type = size_var.member_type
+        var = "%s__ref" % array.size[1]
+        writer.statement("spice_marshaller_set_%s(m, %s, spice_marshaller_get_size(m) - %s)" % (size_var_type.primitive_type(), var, size_start_var))
+
+def write_pointer_marshaller(writer, member, src):
+    t = member.member_type
+    ptr_func = write_marshal_ptr_function(writer, t.target_type)
+    submarshaller = "spice_marshaller_get_ptr_submarshaller(m, %d)" % (1 if member.get_fixed_nw_size() == 8 else 0)
+    if member.has_attr("marshall"):
+        rest_args = ""
+        if t.target_type.is_array():
+            rest_args = ", %s" % get_array_size(t.target_type, src)
+        writer.assign("m2", submarshaller)
+        if t.has_attr("nonnull"):
+            writer.statement("%s(m2, %s%s)" % (ptr_func, src.get_ref(member.name), rest_args))
+        else:
+            with writer.if_block("%s != NULL" % src.get_ref(member.name)) as block:
+                writer.statement("%s(m2, %s%s)" % (ptr_func, src.get_ref(member.name), rest_args))
+    else:
+        writer.assign("*%s_out" % (writer.out_prefix + member.name), submarshaller)
+
+def write_switch_marshaller(writer, container, switch, src, scope):
+    var = container.lookup_member(switch.variable)
+    var_type = var.member_type
+
+    saved_out_prefix = writer.out_prefix
+    first = True
+    for c in switch.cases:
+        check = c.get_check(src.get_ref(switch.variable), var_type)
+        m = c.member
+        writer.out_prefix = saved_out_prefix
+        if m.has_attr("outvar"):
+            writer.out_prefix = "%s_%s" % (m.attributes["outvar"][0], writer.out_prefix)
+        with writer.if_block(check, not first, False) as block:
+            t = m.member_type
+            if switch.has_attr("anon"):
+                if t.is_struct():
+                    src2 = src.child_sub(m)
+                else:
+                    src2 = src
+            else:
+                if t.is_struct():
+                    src2 = src.child_sub(switch).child_sub(m)
+                else:
+                    src2 = src.child_sub(switch)
+            src2.reuse_scope = block
+
+            if t.is_struct():
+                write_container_marshaller(writer, t, src2)
+            elif t.is_pointer():
+                write_pointer_marshaller(writer, m, src2)
+            elif t.is_primitive():
+                if m.has_attr("zero"):
+                    writer.statement("spice_marshaller_add_%s(m, 0)" % (t.primitive_type()))
+                else:
+                    writer.statement("spice_marshaller_add_%s(m, %s)" % (t.primitive_type(), src2.get_ref(m.name)))
+                #TODO validate e.g. flags and enums
+            elif t.is_array():
+                write_array_marshaller(writer, m, t, src2, scope)
+            else:
+                writer.todo("Can't handle type %s" % m.member_type)
+
+            if switch.has_attr("fixedsize"):
+                remaining = switch.get_fixed_nw_size() - t.get_fixed_nw_size()
+                if remaining != 0:
+                    writer.statement("spice_marshaller_reserve_space(m, %s)" % remaining)
+
+        first = False
+    if switch.has_attr("fixedsize"):
+        with writer.block(" else"):
+            writer.statement("spice_marshaller_reserve_space(m, %s)" % switch.get_fixed_nw_size())
+
+    writer.newline()
+
+def write_member_marshaller(writer, container, member, src, scope):
+    if member.has_attr("outvar"):
+        writer.out_prefix = "%s_%s" % (member.attributes["outvar"][0], writer.out_prefix)
+    if member.has_attr("virtual"):
+        writer.comment("Don't marshall @virtual %s" % member.name).newline()
+        return
+    if member.has_attr("nomarshal"):
+        writer.comment("Don't marshall @nomarshal %s" % member.name).newline()
+        return
+    if member.is_switch():
+        write_switch_marshaller(writer, container, member, src, scope)
+        return
+
+    t = member.member_type
+
+    if t.is_pointer():
+        write_pointer_marshaller(writer, member, src)
+    elif t.is_primitive():
+        if member.has_attr("zero"):
+            writer.statement("spice_marshaller_add_%s(m, 0)" % (t.primitive_type()))
+        if member.has_attr("bytes_count"):
+            var = "%s__ref" % member.name
+            scope.variable_def("void *", var)
+            writer.statement("%s = spice_marshaller_add_%s(m, %s)" % (var, t.primitive_type(), 0))
+
+        else:
+            writer.statement("spice_marshaller_add_%s(m, %s)" % (t.primitive_type(), src.get_ref(member.name)))
+    elif t.is_array():
+        write_array_marshaller(writer, member, t, src, scope)
+    elif t.is_struct():
+        src2 = src.child_sub(member)
+        writer.comment(member.name)
+        write_container_marshaller(writer, t, src2)
+    else:
+        raise NotImplementedError("TODO can't handle parsing of %s" % t)
+
+def write_container_marshaller(writer, container, src):
+    saved_out_prefix = writer.out_prefix
+    with src.declare(writer) as scope:
+        for m in container.members:
+            writer.out_prefix = saved_out_prefix
+            write_member_marshaller(writer, container, m, src, scope)
+
+def write_message_marshaller(writer, message, is_server, private):
+    if message.has_attr("ifdef"):
+        writer.ifdef(message.attributes["ifdef"][0])
+    writer.out_prefix = ""
+    function_name = "spice_marshall_" + message.c_name()
+    if writer.is_generated("marshaller", function_name):
+        return function_name
+    writer.set_is_generated("marshaller", function_name)
+
+    names = message.get_pointer_names(False)
+    names_args = ""
+    if len(names) > 0:
+        n = [", SpiceMarshaller **%s_out" % name for name in names]
+        names_args = "".join(n)
+
+    if not private:
+        writer.header.writeln("void " + function_name + "(SpiceMarshaller *m, %s *msg" % message.c_type() + names_args + ");")
+
+    scope = writer.function(function_name,
+                            "static void" if private else "void",
+                            "SPICE_GNUC_UNUSED SpiceMarshaller *m, SPICE_GNUC_UNUSED %s *msg" % message.c_type() + names_args)
+    scope.variable_def("SPICE_GNUC_UNUSED SpiceMarshaller *", "m2")
+
+    for n in names:
+        writer.assign("*%s_out" % n, "NULL")
+
+    # fix warnings about unused variables by not creating body if no members to parse
+    if any(x.is_fixed_nw_size() for x in message.members):
+        src = RootMarshallingSource(None, message.c_type(), message.sizeof(), "msg")
+        src.reuse_scope = scope
+
+        write_container_marshaller(writer, message, src)
+
+    writer.end_block()
+    if message.has_attr("ifdef"):
+        writer.endif(message.attributes["ifdef"][0])
+    writer.newline()
+    return function_name
+
+def write_protocol_marshaller(writer, proto, is_server, private_marshallers):
+    functions = {}
+    for c in proto.channels:
+        channel = c.channel_type
+        if channel.has_attr("ifdef"):
+            writer.ifdef(channel.attributes["ifdef"][0])
+            writer.header.ifdef(channel.attributes["ifdef"][0])
+        if is_server:
+            for m in channel.client_messages:
+                message = m.message_type
+                f = write_message_marshaller(writer, message, is_server, private_marshallers)
+                if channel.has_attr("ifdef") and f not in functions:
+                    functions[f] = channel.attributes["ifdef"][0]
+                elif message.has_attr("ifdef") and f not in functions:
+                    functions[f] = message.attributes["ifdef"][0]
+                else:
+                    functions[f] = True
+        else:
+            for m in channel.server_messages:
+                message = m.message_type
+                f = write_message_marshaller(writer, message, is_server, private_marshallers)
+                if channel.has_attr("ifdef") and f not in functions:
+                    functions[f] = channel.attributes["ifdef"][0]
+                elif message.has_attr("ifdef") and f not in functions:
+                    functions[f] = message.attributes["ifdef"][0]
+                else:
+                    functions[f] = True
+        if channel.has_attr("ifdef"):
+            writer.endif(channel.attributes["ifdef"][0])
+            writer.header.endif(channel.attributes["ifdef"][0])
+
+    if private_marshallers:
+        scope = writer.function("spice_message_marshallers_get" +  writer.public_prefix,
+                                "SpiceMessageMarshallers *",
+                                "void")
+        writer.writeln("static SpiceMessageMarshallers marshallers = {NULL};").newline()
+        for f in sorted(functions.keys()):
+            member = f[len("spice_marshall_"):]
+            if not member.startswith("msg"):
+                member = "msg_" + member
+            if functions[f] != True:
+                writer.ifdef(functions[f])
+            writer.assign("marshallers.%s" % member, f)
+            if functions[f] != True:
+                writer.endif(functions[f])
+
+        writer.newline()
+        writer.statement("return &marshallers")
+        writer.end_block()
+        writer.newline()
+
+def write_trailer(writer):
+    writer.header.writeln("#endif")
diff --git a/python_modules/ptypes.py b/python_modules/ptypes.py
new file mode 100644
index 0000000..d031d09
--- /dev/null
+++ b/python_modules/ptypes.py
@@ -0,0 +1,1061 @@
+from . import codegen
+import types
+
+_types_by_name = {}
+_types = []
+
+default_pointer_size = 4
+
+def type_exists(name):
+    return name in _types_by_name
+
+def lookup_type(name):
+    return _types_by_name[name]
+
+def get_named_types():
+    return _types
+
+class FixedSize:
+    def __init__(self, val = 0, minor = 0):
+        if isinstance(val, FixedSize):
+            self.vals = val.vals
+        else:
+            self.vals = [0] * (minor + 1)
+            self.vals[minor] = val
+
+    def __add__(self, other):
+        if isinstance(other, int):
+            other = FixedSize(other)
+
+        new = FixedSize()
+        l = max(len(self.vals), len(other.vals))
+        shared = min(len(self.vals), len(other.vals))
+
+        new.vals = [0] * l
+
+        for i in range(shared):
+            new.vals[i] = self.vals[i] + other.vals[i]
+
+        for i in range(shared,len(self.vals)):
+            new.vals[i] = self.vals[i]
+
+        for i in range(shared,len(other.vals)):
+            new.vals[i] = new.vals[i] + other.vals[i]
+
+        return new
+
+    def __radd__(self, other):
+        return self.__add__(other)
+
+    def __str__(self):
+        s = "%d" % (self.vals[0])
+
+        for i in range(1,len(self.vals)):
+            if self.vals[i] > 0:
+                s = s + " + ((minor >= %d)?%d:0)" % (i, self.vals[i])
+        return s
+
+# Some attribute are propagated from member to the type as they really
+# are part of the type definition, rather than the member. This applies
+# only to attributes that affect pointer or array attributes, as these
+# are member local types, unlike e.g. a Struct that may be used by
+# other members
+propagated_attributes=["ptr_array", "nonnull", "chunk"]
+
+class Type:
+    def __init__(self):
+        self.attributes = {}
+        self.registred = False
+        self.name = None
+
+    def has_name(self):
+        return self.name != None
+
+    def get_type(self, recursive=False):
+        return self
+
+    def is_primitive(self):
+        return False
+
+    def is_fixed_sizeof(self):
+        return True
+
+    def is_extra_size(self):
+        return False
+
+    def contains_extra_size(self):
+        return False
+
+    def is_fixed_nw_size(self):
+        return True
+
+    def is_array(self):
+        return isinstance(self, ArrayType)
+
+    def contains_member(self, member):
+        return False
+
+    def is_struct(self):
+        return isinstance(self, StructType)
+
+    def is_pointer(self):
+        return isinstance(self, PointerType)
+
+    def get_num_pointers(self):
+        return 0
+
+    def get_pointer_names(self, marshalled):
+        return []
+
+    def sizeof(self):
+        return "sizeof(%s)" % (self.c_type())
+
+    def __repr__(self):
+        return self.__str__()
+
+    def __str__(self):
+        if self.name != None:
+            return self.name
+        return "anonymous type"
+
+    def resolve(self):
+        return self
+
+    def register(self):
+        if self.registred or self.name == None:
+            return
+        self.registred = True
+        if self.name in _types_by_name:
+            raise Exception("Type %s already defined" % self.name)
+        _types.append(self)
+        _types_by_name[self.name] = self
+
+    def has_attr(self, name):
+        return name in self.attributes
+
+class TypeRef(Type):
+    def __init__(self, name):
+        Type.__init__(self)
+        self.name = name
+
+    def __str__(self):
+        return "ref to %s" % (self.name)
+
+    def resolve(self):
+        if self.name not in _types_by_name:
+            raise Exception("Unknown type %s" % self.name)
+        return _types_by_name[self.name]
+
+    def register(self):
+        assert True, "Can't register TypeRef!"
+
+
+class IntegerType(Type):
+    def __init__(self, bits, signed):
+        Type.__init__(self)
+        self.bits = bits
+        self.signed = signed
+
+        if signed:
+            self.name = "int%d" % bits
+        else:
+            self.name = "uint%d" % bits
+
+    def primitive_type(self):
+        return self.name
+
+    def c_type(self):
+        return self.name + "_t"
+
+    def get_fixed_nw_size(self):
+        return self.bits // 8
+
+    def is_primitive(self):
+        return True
+
+class TypeAlias(Type):
+    def __init__(self, name, the_type, attribute_list):
+        Type.__init__(self)
+        self.name = name
+        self.the_type = the_type
+        for attr in attribute_list:
+            self.attributes[attr[0][1:]] = attr[1:]
+
+    def get_type(self, recursive=False):
+        if recursive:
+            return self.the_type.get_type(True)
+        else:
+            return self.the_type
+
+    def primitive_type(self):
+        return self.the_type.primitive_type()
+
+    def resolve(self):
+        self.the_type = self.the_type.resolve()
+        return self
+
+    def __str__(self):
+        return "alias %s" % self.name
+
+    def is_primitive(self):
+        return self.the_type.is_primitive()
+
+    def is_fixed_sizeof(self):
+        return self.the_type.is_fixed_sizeof()
+
+    def is_fixed_nw_size(self):
+        return self.the_type.is_fixed_nw_size()
+
+    def get_fixed_nw_size(self):
+        return self.the_type.get_fixed_nw_size()
+
+    def get_num_pointers(self):
+        return self.the_type.get_num_pointers()
+
+    def get_pointer_names(self, marshalled):
+        return self.the_type.get_pointer_names(marshalled)
+
+    def c_type(self):
+        if self.has_attr("ctype"):
+            return self.attributes["ctype"][0]
+        return self.name
+
+class EnumBaseType(Type):
+    def is_enum(self):
+        return isinstance(self, EnumType)
+
+    def primitive_type(self):
+        return "uint%d" % (self.bits)
+
+    def c_type(self):
+        return "uint%d_t" % (self.bits)
+
+    def c_name(self):
+        return codegen.prefix_camel(self.name)
+
+    def c_enumname(self, value):
+        return self.c_enumname_by_name(self.names[value])
+
+    def c_enumname_by_name(self, name):
+        if self.has_attr("prefix"):
+            return self.attributes["prefix"][0] + name
+        return codegen.prefix_underscore_upper(self.name.upper(), name)
+
+    def is_primitive(self):
+        return True
+
+    def get_fixed_nw_size(self):
+        return self.bits // 8
+
+    # generates a value-name table suitable for use with the wireshark protocol
+    # dissector
+    def c_describe(self, writer):
+        writer.write("static const value_string %s_vs[] = " % codegen.prefix_underscore_lower(self.name))
+        writer.begin_block()
+        values = list(self.names.keys())
+        values.sort()
+        for i in values:
+            writer.write("{ ")
+            writer.write(self.c_enumname(i))
+            writer.write(", \"%s\" }," % self.names[i])
+            writer.newline()
+        writer.write("{ 0, NULL }")
+        writer.end_block(semicolon=True)
+        writer.newline()
+
+
+class EnumType(EnumBaseType):
+    def __init__(self, bits, name, enums, attribute_list):
+        Type.__init__(self)
+        self.bits = bits
+        self.name = name
+
+        last = -1
+        names = {}
+        values = {}
+        for v in enums:
+            name = v[0]
+            if len(v) > 1:
+                value = v[1]
+            else:
+                value = last + 1
+            last = value
+
+            assert value not in names
+            names[value] = name
+            values[name] = value
+
+        self.names = names
+        self.values = values
+
+        for attr in attribute_list:
+            self.attributes[attr[0][1:]] = attr[1:]
+
+    def __str__(self):
+        return "enum %s" % self.name
+
+    def c_define(self, writer):
+        writer.write("typedef enum ")
+        writer.write(self.c_name())
+        writer.begin_block()
+        values = list(self.names.keys())
+        values.sort()
+        current_default = 0
+        for i in values:
+            writer.write(self.c_enumname(i))
+            if i != current_default:
+                writer.write(" = %d" % (i))
+            writer.write(",")
+            writer.newline()
+            current_default = i + 1
+        writer.newline()
+        writer.write(codegen.prefix_underscore_upper(self.name.upper(), "ENUM_END"))
+        writer.newline()
+        writer.end_block(newline=False)
+        writer.write(" ")
+        writer.write(self.c_name())
+        writer.write(";")
+        writer.newline()
+        writer.newline()
+
+class FlagsType(EnumBaseType):
+    def __init__(self, bits, name, flags, attribute_list):
+        Type.__init__(self)
+        self.bits = bits
+        self.name = name
+
+        last = -1
+        names = {}
+        values = {}
+        for v in flags:
+            name = v[0]
+            if len(v) > 1:
+                value = v[1]
+            else:
+                value = last + 1
+            last = value
+
+            assert value not in names
+            names[value] = name
+            values[name] = value
+
+        self.names = names
+        self.values = values
+
+        for attr in attribute_list:
+            self.attributes[attr[0][1:]] = attr[1:]
+
+    def __str__(self):
+        return "flags %s" % self.name
+
+    def c_define(self, writer):
+        writer.write("typedef enum ")
+        writer.write(self.c_name())
+        writer.begin_block()
+        values = list(self.names.keys())
+        values.sort()
+        mask = 0
+        for i in values:
+            writer.write(self.c_enumname(i))
+            mask = mask |  (1<<i)
+            writer.write(" = (1 << %d)" % (i))
+            writer.write(",")
+            writer.newline()
+            current_default = i + 1
+        writer.newline()
+        writer.write(codegen.prefix_underscore_upper(self.name.upper(), "MASK"))
+        writer.write(" = 0x%x" % (mask))
+        writer.newline()
+        writer.end_block(newline=False)
+        writer.write(" ")
+        writer.write(self.c_name())
+        writer.write(";")
+        writer.newline()
+        writer.newline()
+
+class ArrayType(Type):
+    def __init__(self, element_type, size):
+        Type.__init__(self)
+        self.name = None
+
+        self.element_type = element_type
+        self.size = size
+
+    def __str__(self):
+        if self.size == None:
+            return "%s[]" % (str(self.element_type))
+        else:
+            return "%s[%s]" % (str(self.element_type), str(self.size))
+
+    def resolve(self):
+        self.element_type = self.element_type.resolve()
+        return self
+
+    def is_constant_length(self):
+        return isinstance(self.size, int)
+
+    def is_remaining_length(self):
+        return isinstance(self.size, str) and len(self.size) == 0
+
+    def is_identifier_length(self):
+        return isinstance(self.size, str) and len(self.size) > 0
+
+    def is_image_size_length(self):
+        if isinstance(self.size, int) or isinstance(self.size, str):
+            return False
+        return self.size[0] == "image_size"
+
+    def is_bytes_length(self):
+        if isinstance(self.size, int) or isinstance(self.size, str):
+            return False
+        return self.size[0] == "bytes"
+
+    def is_cstring_length(self):
+        if isinstance(self.size, int) or isinstance(self.size, str):
+            return False
+        return self.size[0] == "cstring"
+
+    def is_fixed_sizeof(self):
+        return self.is_constant_length() and self.element_type.is_fixed_sizeof()
+
+    def is_fixed_nw_size(self):
+        return self.is_constant_length() and self.element_type.is_fixed_nw_size()
+
+    def get_fixed_nw_size(self):
+        if not self.is_fixed_nw_size():
+            raise Exception("Not a fixed size type")
+
+        return self.element_type.get_fixed_nw_size() * self.size
+
+    def get_num_pointers(self):
+        element_count = self.element_type.get_num_pointers()
+        if element_count  == 0:
+            return 0
+        if self.is_constant_length(self):
+            return element_count * self.size
+        raise Exception("Pointers in dynamic arrays not supported")
+
+    def get_pointer_names(self, marshalled):
+        element_count = self.element_type.get_num_pointers()
+        if element_count  == 0:
+            return []
+        raise Exception("Pointer names in arrays not supported")
+
+    def is_extra_size(self):
+        return self.has_attr("ptr_array")
+
+    def contains_extra_size(self):
+        return self.element_type.contains_extra_size() or self.has_attr("chunk")
+
+    def sizeof(self):
+        return "%s * %s" % (self.element_type.sizeof(), self.size)
+
+    def c_type(self):
+        return self.element_type.c_type()
+
+class PointerType(Type):
+    def __init__(self, target_type):
+        Type.__init__(self)
+        self.name = None
+        self.target_type = target_type
+        self.pointer_size = default_pointer_size
+
+    def __str__(self):
+        return "%s*" % (str(self.target_type))
+
+    def resolve(self):
+        self.target_type = self.target_type.resolve()
+        return self
+
+    def set_ptr_size(self, new_size):
+        self.pointer_size = new_size
+
+    def is_fixed_nw_size(self):
+        return True
+
+    def is_primitive(self):
+        return True
+
+    def primitive_type(self):
+        if self.pointer_size == 4:
+            return "uint32"
+        else:
+            return "uint64"
+
+    def get_fixed_nw_size(self):
+        return self.pointer_size
+
+    def c_type(self):
+        if self.pointer_size == 4:
+            return "uint32_t"
+        else:
+            return "uint64_t"
+
+    def contains_extra_size(self):
+        return True
+
+    def get_num_pointers(self):
+        return 1
+
+class Containee:
+    def __init__(self):
+        self.attributes = {}
+
+    def is_switch(self):
+        return False
+
+    def is_pointer(self):
+        return not self.is_switch() and self.member_type.is_pointer()
+
+    def is_array(self):
+        return not self.is_switch() and self.member_type.is_array()
+
+    def is_struct(self):
+        return not self.is_switch() and self.member_type.is_struct()
+
+    def is_primitive(self):
+        return not self.is_switch() and self.member_type.is_primitive()
+
+    def has_attr(self, name):
+        return name in self.attributes
+
+    def has_minor_attr(self):
+        return self.has_attr("minor")
+
+    def has_end_attr(self):
+        return self.has_attr("end")
+
+    def get_minor_attr(self):
+        return self.attributes["minor"][0]
+
+class Member(Containee):
+    def __init__(self, name, member_type, attribute_list):
+        Containee.__init__(self)
+        self.name = name
+        self.member_type = member_type
+        for attr in attribute_list:
+            self.attributes[attr[0][1:]] = attr[1:]
+
+    def resolve(self, container):
+        self.container = container
+        self.member_type = self.member_type.resolve()
+        self.member_type.register()
+        if self.has_attr("ptr32") and self.member_type.is_pointer():
+            self.member_type.set_ptr_size(4)
+        for i in propagated_attributes:
+            if self.has_attr(i):
+                self.member_type.attributes[i] = self.attributes[i]
+        return self
+
+    def contains_member(self, member):
+        return self.member_type.contains_member(member)
+
+    def is_primitive(self):
+        return self.member_type.is_primitive()
+
+    def is_fixed_sizeof(self):
+        if self.has_end_attr():
+            return False
+        return self.member_type.is_fixed_sizeof()
+
+    def is_extra_size(self):
+        return self.has_end_attr() or self.has_attr("to_ptr") or self.member_type.is_extra_size()
+
+    def is_fixed_nw_size(self):
+        if self.has_attr("virtual"):
+            return True
+        return self.member_type.is_fixed_nw_size()
+
+    def get_fixed_nw_size(self):
+        if self.has_attr("virtual"):
+            return 0
+        size = self.member_type.get_fixed_nw_size()
+        if self.has_minor_attr():
+            minor = self.get_minor_attr()
+            size = FixedSize(size, minor)
+        return size
+
+    def contains_extra_size(self):
+        return self.member_type.contains_extra_size()
+
+    def sizeof(self):
+        return self.member_type.sizeof()
+
+    def __repr__(self):
+        return "%s (%s)" % (str(self.name), str(self.member_type))
+
+    def get_num_pointers(self):
+        if self.has_attr("to_ptr"):
+            return 1
+        return self.member_type.get_num_pointers()
+
+    def get_pointer_names(self, marshalled):
+        if self.member_type.is_pointer():
+            if self.has_attr("marshall") == marshalled:
+                names = [self.name]
+            else:
+                names = []
+        else:
+            names = self.member_type.get_pointer_names(marshalled)
+        if self.has_attr("outvar"):
+            prefix = self.attributes["outvar"][0]
+            names = [prefix + "_" + name for name in names]
+        return names
+
+class SwitchCase:
+    def __init__(self, values, member):
+        self.values = values
+        self.member = member
+        self.members = [member]
+
+    def get_check(self, var_cname, var_type):
+        checks = []
+        for v in self.values:
+            if v == None:
+                return "1"
+            elif var_type.is_enum():
+                checks.append("%s == %s" % (var_cname, var_type.c_enumname_by_name(v[1])))
+            else:
+                checks.append("%s(%s & %s)" % (v[0], var_cname, var_type.c_enumname_by_name(v[1])))
+        return " || ".join(checks)
+
+    def resolve(self, container):
+        self.switch = container
+        self.member = self.member.resolve(self)
+        return self
+
+    def get_num_pointers(self):
+        return self.member.get_num_pointers()
+
+    def get_pointer_names(self, marshalled):
+        return self.member.get_pointer_names(marshalled)
+
+class Switch(Containee):
+    def __init__(self, variable, cases, name, attribute_list):
+        Containee.__init__(self)
+        self.variable = variable
+        self.name = name
+        self.cases = cases
+        for attr in attribute_list:
+            self.attributes[attr[0][1:]] = attr[1:]
+
+    def is_switch(self):
+        return True
+
+    def lookup_case_member(self, name):
+        for c in self.cases:
+            if c.member.name == name:
+                return c.member
+        return None
+
+    def has_switch_member(self, member):
+        for c in self.cases:
+            if c.member == member:
+                return True
+        return False
+
+    def resolve(self, container):
+        self.container = container
+        self.cases = [c.resolve(self) for c in self.cases]
+        return self
+
+    def __repr__(self):
+        return "switch on %s %s" % (str(self.variable),str(self.name))
+
+    def is_fixed_sizeof(self):
+        # Kinda weird, but we're unlikely to have a real struct if there is an @end
+        if self.has_end_attr():
+            return False
+        return True
+
+    def is_fixed_nw_size(self):
+        if self.has_attr("fixedsize"):
+            return True
+
+        size = None
+        has_default = False
+        for c in self.cases:
+            for v in c.values:
+                if v == None:
+                    has_default = True
+            if not c.member.is_fixed_nw_size():
+                return False
+            if size == None:
+                size = c.member.get_fixed_nw_size()
+            elif size != c.member.get_fixed_nw_size():
+                return False
+        # Fixed size if all elements listed, or has default
+        if has_default:
+            return True
+        key = self.container.lookup_member(self.variable)
+        return len(self.cases) == len(key.member_type.values)
+
+    def is_extra_size(self):
+        return self.has_end_attr()
+
+    def contains_extra_size(self):
+        for c in self.cases:
+            if c.member.is_extra_size():
+                return True
+            if c.member.contains_extra_size():
+                return True
+        return False
+
+    def get_fixed_nw_size(self):
+        if not self.is_fixed_nw_size():
+            raise Exception("Not a fixed size type")
+        size = 0
+        for c in self.cases:
+            size = max(size, c.member.get_fixed_nw_size())
+        return size
+
+    def sizeof(self):
+        return "sizeof(((%s *)NULL)->%s)" % (self.container.c_type(),
+                                             self.name)
+
+    def contains_member(self, member):
+        return False # TODO: Don't support switch deep member lookup yet
+
+    def get_num_pointers(self):
+        count = 0
+        for c in self.cases:
+            count = max(count, c.get_num_pointers())
+        return count
+
+    def get_pointer_names(self, marshalled):
+        names = []
+        for c in self.cases:
+            names = names + c.get_pointer_names(marshalled)
+        return names
+
+class ContainerType(Type):
+    def is_fixed_sizeof(self):
+        for m in self.members:
+            if not m.is_fixed_sizeof():
+                return False
+        return True
+
+    def contains_extra_size(self):
+        for m in self.members:
+            if m.is_extra_size():
+                return True
+            if m.contains_extra_size():
+                return True
+        return False
+
+    def is_fixed_nw_size(self):
+        for i in self.members:
+            if not i.is_fixed_nw_size():
+                return False
+        return True
+
+    def get_fixed_nw_size(self):
+        size = 0
+        for i in self.members:
+            size = size + i.get_fixed_nw_size()
+        return size
+
+    def contains_member(self, member):
+        for m in self.members:
+            if m == member or m.contains_member(member):
+                return True
+        return False
+
+    def get_fixed_nw_offset(self, member):
+        size = 0
+        for i in self.members:
+            if i == member:
+                break
+            if i.contains_member(member):
+                size = size  + i.member_type.get_fixed_nw_offset(member)
+                break
+            if i.is_fixed_nw_size():
+                size = size + i.get_fixed_nw_size()
+        return size
+
+    def resolve(self):
+        self.members = [m.resolve(self) for m in self.members]
+        return self
+
+    def get_num_pointers(self):
+        count = 0
+        for m in self.members:
+            count = count + m.get_num_pointers()
+        return count
+
+    def get_pointer_names(self, marshalled):
+        names = []
+        for m in self.members:
+            names = names + m.get_pointer_names(marshalled)
+        return names
+
+    def get_nw_offset(self, member, prefix = "", postfix = ""):
+        fixed = self.get_fixed_nw_offset(member)
+        v = []
+        container = self
+        while container != None:
+            members = container.members
+            container = None
+            for m in members:
+                if m == member:
+                    break
+                if m.contains_member(member):
+                    container = m.member_type
+                    break
+                if m.is_switch() and m.has_switch_member(member):
+                    break
+                if not m.is_fixed_nw_size():
+                    v.append(prefix + m.name + postfix)
+        if len(v) > 0:
+            return str(fixed) + " + " + (" + ".join(v))
+        else:
+            return str(fixed)
+
+    def lookup_member(self, name):
+        dot = name.find('.')
+        rest = None
+        if dot >= 0:
+            rest = name[dot+1:]
+            name = name[:dot]
+
+        member = None
+        if name in self.members_by_name:
+            member = self.members_by_name[name]
+        else:
+            for m in self.members:
+                if m.is_switch():
+                    member = m.lookup_case_member(name)
+                    if member != None:
+                        break
+                if member != None:
+                    break
+
+        if member == None:
+            raise Exception("No member called %s found" % name)
+
+        if rest != None:
+            return member.member_type.lookup_member(rest)
+
+        return member
+
+class StructType(ContainerType):
+    def __init__(self, name, members, attribute_list):
+        Type.__init__(self)
+        self.name = name
+        self.members = members
+        self.members_by_name = {}
+        for m in members:
+            self.members_by_name[m.name] = m
+        for attr in attribute_list:
+            self.attributes[attr[0][1:]] = attr[1:]
+
+    def __str__(self):
+        if self.name == None:
+            return "anonymous struct"
+        else:
+            return "struct %s" % self.name
+
+    def c_type(self):
+        if self.has_attr("ctype"):
+            return self.attributes["ctype"][0]
+        return codegen.prefix_camel(self.name)
+
+class MessageType(ContainerType):
+    def __init__(self, name, members, attribute_list):
+        Type.__init__(self)
+        self.name = name
+        self.members = members
+        self.members_by_name = {}
+        for m in members:
+            self.members_by_name[m.name] = m
+        self.reverse_members = {} # ChannelMembers referencing this message
+        for attr in attribute_list:
+            self.attributes[attr[0][1:]] = attr[1:]
+
+    def __str__(self):
+        if self.name == None:
+            return "anonymous message"
+        else:
+            return "message %s" % self.name
+
+    def c_name(self):
+        if self.name == None:
+            cms = list(self.reverse_members.keys())
+            if len(cms) != 1:
+                raise "Unknown typename for message"
+            cm = cms[0]
+            channelname = cm.channel.member_name
+            if channelname == None:
+                channelname = ""
+            else:
+                channelname = channelname + "_"
+            if cm.is_server:
+                return "msg_" + channelname +  cm.name
+            else:
+                return "msgc_" + channelname +  cm.name
+        else:
+            return codegen.prefix_camel("Msg", self.name)
+
+    def c_type(self):
+        if self.has_attr("ctype"):
+            return self.attributes["ctype"][0]
+        if self.name == None:
+            cms = list(self.reverse_members.keys())
+            if len(cms) != 1:
+                raise "Unknown typename for message"
+            cm = cms[0]
+            channelname = cm.channel.member_name
+            if channelname == None:
+                channelname = ""
+            if cm.is_server:
+                return codegen.prefix_camel("Msg", channelname, cm.name)
+            else:
+                return codegen.prefix_camel("Msgc", channelname, cm.name)
+        else:
+            return codegen.prefix_camel("Msg", self.name)
+
+class ChannelMember(Containee):
+    def __init__(self, name, message_type, value):
+        Containee.__init__(self)
+        self.name = name
+        self.message_type = message_type
+        self.value = value
+
+    def resolve(self, channel):
+        self.channel = channel
+        self.message_type = self.message_type.resolve()
+        self.message_type.reverse_members[self] = 1
+
+        return self
+
+    def __repr__(self):
+        return "%s (%s)" % (str(self.name), str(self.message_type))
+
+class ChannelType(Type):
+    def __init__(self, name, base, members, attribute_list):
+        Type.__init__(self)
+        self.name = name
+        self.base = base
+        self.member_name = None
+        self.members = members
+        for attr in attribute_list:
+            self.attributes[attr[0][1:]] = attr[1:]
+
+    def __str__(self):
+        if self.name == None:
+            return "anonymous channel"
+        else:
+            return "channel %s" % self.name
+
+    def is_fixed_nw_size(self):
+        return False
+
+    def get_client_message(self, name):
+        return self.client_messages_byname[name]
+
+    def get_server_message(self, name):
+        return self.server_messages_byname[name]
+
+    def resolve(self):
+        if self.base != None:
+            self.base = self.base.resolve()
+
+            server_messages = self.base.server_messages[:]
+            server_messages_byname = self.base.server_messages_byname.copy()
+            client_messages = self.base.client_messages[:]
+            client_messages_byname = self.base.client_messages_byname.copy()
+
+            # Set default member_name, FooChannel -> foo
+            self.member_name = self.name[:-7].lower()
+        else:
+            server_messages = []
+            server_messages_byname = {}
+            client_messages = []
+            client_messages_byname = {}
+
+        server_count = 1
+        client_count = 1
+
+        server = True
+        for m in self.members:
+            if m == "server":
+                server = True
+            elif m == "client":
+                server = False
+            elif server:
+                m.is_server = True
+                m = m.resolve(self)
+                if m.value:
+                    server_count = m.value + 1
+                else:
+                    m.value = server_count
+                    server_count = server_count + 1
+                server_messages.append(m)
+                server_messages_byname[m.name] = m
+            else:
+                m.is_server = False
+                m = m.resolve(self)
+                if m.value:
+                    client_count = m.value + 1
+                else:
+                    m.value = client_count
+                    client_count = client_count + 1
+                client_messages.append(m)
+                client_messages_byname[m.name] = m
+
+        self.server_messages = server_messages
+        self.server_messages_byname = server_messages_byname
+        self.client_messages = client_messages
+        self.client_messages_byname = client_messages_byname
+
+        return self
+
+class ProtocolMember:
+    def __init__(self, name, channel_type, value):
+        self.name = name
+        self.channel_type = channel_type
+        self.value = value
+
+    def resolve(self, protocol):
+        self.channel_type = self.channel_type.resolve()
+        self.channel_type.member_name = self.name
+        return self
+
+    def __repr__(self):
+        return "%s (%s)" % (str(self.name), str(self.channel_type))
+
+class ProtocolType(Type):
+    def __init__(self, name, channels):
+        Type.__init__(self)
+        self.name = name
+        self.channels = channels
+
+    def __str__(self):
+        if self.name == None:
+            return "anonymous protocol"
+        else:
+            return "protocol %s" % self.name
+
+    def is_fixed_nw_size(self):
+        return False
+
+    def resolve(self):
+        count = 1
+        for m in self.channels:
+            m = m.resolve(self)
+            if m.value:
+                count = m.value + 1
+            else:
+                m.value = count
+                count = count + 1
+
+        return self
+
+int8 = IntegerType(8, True)
+uint8 = IntegerType(8, False)
+int16 = IntegerType(16, True)
+uint16 = IntegerType(16, False)
+int32 = IntegerType(32, True)
+uint32 = IntegerType(32, False)
+int64 = IntegerType(64, True)
+uint64 = IntegerType(64, False)
diff --git a/python_modules/spice_parser.py b/python_modules/spice_parser.py
new file mode 100644
index 0000000..d60bb10
--- /dev/null
+++ b/python_modules/spice_parser.py
@@ -0,0 +1,162 @@
+try:
+    from pyparsing import Literal, CaselessLiteral, Word, OneOrMore, ZeroOrMore, \
+            Forward, delimitedList, Group, Optional, Combine, alphas, nums, restOfLine, cStyleComment, \
+            alphanums, ParseException, ParseResults, Keyword, StringEnd, replaceWith
+except ImportError:
+    six.print_("Module pyparsing not found.")
+    exit(1)
+
+
+from . import ptypes
+import six
+import sys
+
+cvtInt = lambda toks: int(toks[0])
+
+def parseVariableDef(toks):
+    t = toks[0][0]
+    pointer = toks[0][1]
+    name = toks[0][2]
+    array_size = toks[0][3]
+    attributes = toks[0][4]
+
+    if array_size != None:
+        t = ptypes.ArrayType(t, array_size)
+
+    if pointer != None:
+        t = ptypes.PointerType(t)
+
+    return ptypes.Member(name, t, attributes)
+
+bnf = None
+def SPICE_BNF():
+    global bnf
+
+    if not bnf:
+
+        # punctuation
+        colon  = Literal(":").suppress()
+        lbrace = Literal("{").suppress()
+        rbrace = Literal("}").suppress()
+        lbrack = Literal("[").suppress()
+        rbrack = Literal("]").suppress()
+        lparen = Literal("(").suppress()
+        rparen = Literal(")").suppress()
+        equals = Literal("=").suppress()
+        comma  = Literal(",").suppress()
+        semi   = Literal(";").suppress()
+
+        # primitive types
+        int8_      = Keyword("int8").setParseAction(replaceWith(ptypes.int8))
+        uint8_     = Keyword("uint8").setParseAction(replaceWith(ptypes.uint8))
+        int16_     = Keyword("int16").setParseAction(replaceWith(ptypes.int16))
+        uint16_    = Keyword("uint16").setParseAction(replaceWith(ptypes.uint16))
+        int32_     = Keyword("int32").setParseAction(replaceWith(ptypes.int32))
+        uint32_    = Keyword("uint32").setParseAction(replaceWith(ptypes.uint32))
+        int64_     = Keyword("int64").setParseAction(replaceWith(ptypes.int64))
+        uint64_    = Keyword("uint64").setParseAction(replaceWith(ptypes.uint64))
+
+        # keywords
+        channel_   = Keyword("channel")
+        enum32_    = Keyword("enum32").setParseAction(replaceWith(32))
+        enum16_    = Keyword("enum16").setParseAction(replaceWith(16))
+        enum8_     = Keyword("enum8").setParseAction(replaceWith(8))
+        flags32_   = Keyword("flags32").setParseAction(replaceWith(32))
+        flags16_   = Keyword("flags16").setParseAction(replaceWith(16))
+        flags8_    = Keyword("flags8").setParseAction(replaceWith(8))
+        channel_   = Keyword("channel")
+        server_    = Keyword("server")
+        client_    = Keyword("client")
+        protocol_  = Keyword("protocol")
+        typedef_   = Keyword("typedef")
+        struct_    = Keyword("struct")
+        message_   = Keyword("message")
+        image_size_ = Keyword("image_size")
+        bytes_     = Keyword("bytes")
+        cstring_   = Keyword("cstring")
+        switch_    = Keyword("switch")
+        default_   = Keyword("default")
+        case_      = Keyword("case")
+
+        identifier = Word( alphas, alphanums + "_" )
+        enumname = Word( alphanums + "_" )
+
+        integer = ( Combine( CaselessLiteral("0x") + Word( nums+"abcdefABCDEF" ) ) |
+                    Word( nums+"+-", nums ) ).setName("int").setParseAction(cvtInt)
+
+        typename = identifier.copy().setParseAction(lambda toks : ptypes.TypeRef(str(toks[0])))
+
+        # This is just normal "types", i.e. not channels or messages
+        typeSpec = Forward()
+
+        attributeValue = integer ^ identifier
+        attribute = Group(Combine ("@" + identifier) + Optional(lparen + delimitedList(attributeValue) + rparen))
+        attributes = Group(ZeroOrMore(attribute))
+        arraySizeSpecImage = Group(image_size_ + lparen + integer + comma + identifier + comma + identifier + rparen)
+        arraySizeSpecBytes = Group(bytes_ + lparen + identifier + comma + identifier + rparen)
+        arraySizeSpecCString = Group(cstring_ + lparen + rparen)
+        arraySizeSpec = lbrack + Optional(identifier ^ integer ^ arraySizeSpecImage ^ arraySizeSpecBytes ^arraySizeSpecCString, default="") + rbrack
+        variableDef = Group(typeSpec + Optional("*", default=None) + identifier + Optional(arraySizeSpec, default=None) + attributes - semi) \
+            .setParseAction(parseVariableDef)
+
+        switchCase = Group(Group(OneOrMore(default_.setParseAction(replaceWith(None)) + colon | Group(case_.suppress() + Optional("!", default="") + identifier) + colon)) + variableDef) \
+            .setParseAction(lambda toks: ptypes.SwitchCase(toks[0][0], toks[0][1]))
+        switchBody = Group(switch_ + lparen + delimitedList(identifier,delim='.', combine=True) + rparen + lbrace + Group(OneOrMore(switchCase)) + rbrace + identifier + attributes - semi) \
+            .setParseAction(lambda toks: ptypes.Switch(toks[0][1], toks[0][2], toks[0][3], toks[0][4]))
+        messageBody = structBody = Group(lbrace + ZeroOrMore(variableDef | switchBody)  + rbrace)
+        structSpec = Group(struct_ + identifier + structBody + attributes).setParseAction(lambda toks: ptypes.StructType(toks[0][1], toks[0][2], toks[0][3]))
+
+        # have to use longest match for type, in case a user-defined type name starts with a keyword type, like "channel_type"
+        typeSpec << ( structSpec ^ int8_ ^ uint8_ ^ int16_ ^ uint16_ ^
+                     int32_ ^ uint32_ ^ int64_ ^ uint64_ ^
+                     typename).setName("type")
+
+        flagsBody = enumBody = Group(lbrace + delimitedList(Group (enumname + Optional(equals + integer))) + Optional(comma) + rbrace)
+
+        messageSpec = Group(message_ + messageBody + attributes).setParseAction(lambda toks: ptypes.MessageType(None, toks[0][1], toks[0][2])) | typename
+
+        channelParent = Optional(colon + typename, default=None)
+        channelMessage = Group(messageSpec + identifier + Optional(equals + integer, default=None) + semi) \
+            .setParseAction(lambda toks: ptypes.ChannelMember(toks[0][1], toks[0][0], toks[0][2]))
+        channelBody = channelParent + Group(lbrace + ZeroOrMore( server_ + colon | client_ + colon | channelMessage)  + rbrace)
+
+        enum_ = (enum32_ | enum16_ | enum8_)
+        flags_ = (flags32_ | flags16_ | flags8_)
+        enumDef = Group(enum_ + identifier + enumBody + attributes - semi).setParseAction(lambda toks: ptypes.EnumType(toks[0][0], toks[0][1], toks[0][2], toks[0][3]))
+        flagsDef = Group(flags_ + identifier + flagsBody + attributes  - semi).setParseAction(lambda toks: ptypes.FlagsType(toks[0][0], toks[0][1], toks[0][2], toks[0][3]))
+        messageDef = Group(message_ + identifier + messageBody + attributes - semi).setParseAction(lambda toks: ptypes.MessageType(toks[0][1], toks[0][2], toks[0][3]))
+        channelDef = Group(channel_ + identifier + channelBody + attributes - semi).setParseAction(lambda toks: ptypes.ChannelType(toks[0][1], toks[0][2], toks[0][3], toks[0][4]))
+        structDef = Group(struct_ + identifier + structBody + attributes - semi).setParseAction(lambda toks: ptypes.StructType(toks[0][1], toks[0][2], toks[0][3]))
+        typedefDef = Group(typedef_ + identifier  + typeSpec + attributes - semi).setParseAction(lambda toks: ptypes.TypeAlias(toks[0][1], toks[0][2], toks[0][3]))
+
+        definitions = typedefDef | structDef | enumDef | flagsDef | messageDef | channelDef
+
+        protocolChannel = Group(typename + identifier +  Optional(equals + integer, default=None) + semi) \
+            .setParseAction(lambda toks: ptypes.ProtocolMember(toks[0][1], toks[0][0], toks[0][2]))
+        protocolDef = Group(protocol_ + identifier + Group(lbrace + ZeroOrMore(protocolChannel) + rbrace) + semi) \
+            .setParseAction(lambda toks: ptypes.ProtocolType(toks[0][1], toks[0][2]))
+
+        bnf = ZeroOrMore (definitions) +  protocolDef + StringEnd()
+
+        singleLineComment = "//" + restOfLine
+        bnf.ignore( singleLineComment )
+        bnf.ignore( cStyleComment )
+
+    return bnf
+
+
+def parse(filename):
+    try:
+        bnf = SPICE_BNF()
+        types = bnf.parseFile(filename)
+    except ParseException as err:
+        six.print_(err.line, file=sys.stderr)
+        six.print_(" "*(err.column-1) + "^", file=sys.stderr)
+        six.print_(err, file=sys.stderr)
+        return None
+
+    for t in types:
+        t.resolve()
+        t.register()
+    protocol = types[-1]
+    return protocol
diff --git a/spice.proto b/spice.proto
new file mode 100644
index 0000000..2889802
--- /dev/null
+++ b/spice.proto
@@ -0,0 +1,1388 @@
+/* built in types:
+   int8, uint8, 16, 32, 64
+*/
+
+typedef fixed28_4 int32 @ctype(SPICE_FIXED28_4);
+
+struct Point {
+    int32 x;
+    int32 y;
+};
+
+struct Point16 {
+    int16 x;
+    int16 y;
+};
+
+struct PointFix {
+    fixed28_4 x;
+    fixed28_4 y;
+};
+
+struct Rect {
+    int32 top;
+    int32 left;
+    int32 bottom;
+    int32 right;
+};
+
+struct Transform {
+    uint32 t00;
+    uint32 t01;
+    uint32 t02;
+    uint32 t10;
+    uint32 t11;
+    uint32 t12;
+};
+
+enum32 link_err {
+    OK,
+    ERROR,
+    INVALID_MAGIC,
+    INVALID_DATA,
+    VERSION_MISMATCH,
+    NEED_SECURED,
+    NEED_UNSECURED,
+    PERMISSION_DENIED,
+    BAD_CONNECTION_ID,
+    CHANNEL_NOT_AVAILABLE
+};
+
+enum32 warn_code {
+    WARN_GENERAL
+} @prefix(SPICE_);
+
+enum32 info_code {
+    INFO_GENERAL
+} @prefix(SPICE_);
+
+flags32 migrate_flags {
+    NEED_FLUSH,
+    NEED_DATA_TRANSFER
+} @prefix(SPICE_MIGRATE_);
+
+flags32 composite_flags {
+    OP0, OP1, OP2, OP3, OP4, OP5, OP6, OP7,
+    SRC_FILTER0, SRC_FILTER1, SRC_FILTER2,
+    MASK_FILTER0, MASK_FITLER1, MASK_FILTER2,
+
+    SRC_REPEAT0, SRC_REPEAT1,
+    MASK_REPEAT0, MASK_REPEAT1,
+    COMPONENT_ALPHA,
+
+    HAS_MASK,
+    HAS_SRC_TRANSFORM,
+    HAS_MASK_TRANSFORM,
+
+    /* These are used to override the formats given in the images. For
+     * example, if the mask image has format a8r8g8b8, but MASK_OPAQUE
+     * is set, the image should be treated as if it were x8r8g8b8
+     */
+    SOURCE_OPAQUE,
+    MASK_OPAQUE,
+    DEST_OPAQUE,
+} @prefix(SPICE_COMPOSITE_);
+
+enum32 notify_severity {
+    INFO,
+    WARN,
+    ERROR,
+};
+
+enum32 notify_visibility {
+    LOW,
+    MEDIUM,
+    HIGH,
+};
+
+flags16 mouse_mode {
+    SERVER,
+    CLIENT,
+};
+
+enum16 pubkey_type {
+    INVALID,
+    RSA,
+    RSA2,
+    DSA,
+    DSA1,
+    DSA2,
+    DSA3,
+    DSA4,
+    DH,
+    EC,
+};
+
+message Empty {
+};
+
+message Data {
+    uint8 data[] @end @ctype(uint8_t);
+} @nocopy;
+
+struct ChannelWait {
+    uint8 channel_type;
+    uint8 channel_id;
+    uint64 message_serial;
+} @ctype(SpiceWaitForChannel);
+
+channel BaseChannel {
+ server:
+    message {
+	migrate_flags flags;
+    } migrate;
+
+    Data migrate_data;
+
+    message {
+	uint32 generation;
+	uint32 window;
+    } set_ack;
+
+    message {
+	uint32 id;
+	uint64 timestamp;
+	uint8 data[] @ctype(uint8_t) @as_ptr(data_len);
+    } ping;
+
+    message {
+	uint8 wait_count;
+	ChannelWait wait_list[wait_count] @end;
+    } wait_for_channels;
+
+    message {
+	uint64 time_stamp;
+	link_err reason;
+    } @ctype(SpiceMsgDisconnect) disconnecting;
+
+    message {
+	uint64 time_stamp;
+	notify_severity severity;
+	notify_visibility visibilty;
+	uint32 what; /* error_code/warn_code/info_code */
+	uint32 message_len;
+	uint8 message[message_len] @end @nomarshal;
+    } notify;
+
+    Data list; /* the msg body is SpiceSubMessageList */
+
+    Empty base_last = 100;
+
+ client:
+    message {
+	uint32 generation;
+    } ack_sync;
+
+    Empty ack;
+
+    message {
+	uint32 id;
+	uint64 timestamp;
+    } @ctype(SpiceMsgPing) pong;
+
+    Empty migrate_flush_mark;
+
+    Data migrate_data;
+
+    message {
+    	uint64 time_stamp;
+	link_err reason;
+    } @ctype(SpiceMsgDisconnect) disconnecting;
+};
+
+struct ChannelId {
+    uint8 type;
+    uint8 id;
+};
+
+struct DstInfo {
+	uint16 port;
+	uint16 sport;
+	uint32 host_size;
+	uint8 *host_data[host_size] @zero_terminated @marshall @nonnull;
+	uint32 cert_subject_size;
+	uint8 *cert_subject_data[cert_subject_size] @zero_terminated @marshall;
+} @ctype(SpiceMigrationDstInfo);
+
+channel MainChannel : BaseChannel {
+ server:
+     message {
+        DstInfo dst_info;
+    } @ctype(SpiceMsgMainMigrationBegin) migrate_begin = 101;
+
+    Empty migrate_cancel;
+
+    message {
+	uint32 session_id;
+	uint32 display_channels_hint;
+	uint32 supported_mouse_modes;
+	uint32 current_mouse_mode;
+	uint32 agent_connected;
+	uint32 agent_tokens;
+	uint32 multi_media_time;
+	uint32 ram_hint;
+    } init;
+
+    message {
+	uint32 num_of_channels;
+	ChannelId channels[num_of_channels] @end;
+    } @ctype(SpiceMsgChannels) channels_list;
+
+    message {
+	mouse_mode supported_modes;
+	mouse_mode current_mode @unique_flag;
+    } mouse_mode;
+
+    message {
+	uint32 time;
+    } @ctype(SpiceMsgMainMultiMediaTime) multi_media_time;
+
+    Empty agent_connected;
+
+    message {
+	link_err error_code;
+    } @ctype(SpiceMsgMainAgentDisconnect) agent_disconnected;
+
+    Data agent_data;
+
+    message {
+	uint32 num_tokens;
+    } @ctype(SpiceMsgMainAgentTokens) agent_token;
+
+    message {
+      uint16 port;
+      uint16 sport;
+      uint32 host_size;
+      uint8 *host_data[host_size] @zero_terminated @marshall;
+      uint32 cert_subject_size;
+      uint8 *cert_subject_data[cert_subject_size] @zero_terminated  @marshall;
+    } @ctype(SpiceMsgMainMigrationSwitchHost) migrate_switch_host;
+
+    Empty migrate_end;
+
+    message {
+       uint32 name_len;
+       uint8 name[name_len] @end;
+    } name;
+
+    message {
+       uint8 uuid[16];
+    } uuid;
+
+    message {
+        uint32 num_tokens;
+    } agent_connected_tokens;
+
+    message {
+        DstInfo dst_info;
+        uint32 src_mig_version;
+    } migrate_begin_seamless;
+
+    Empty migrate_dst_seamless_ack;
+    Empty migrate_dst_seamless_nack;
+
+ client:
+    message {
+	uint64 cache_size;
+    } @ctype(SpiceMsgcClientInfo) client_info = 101;
+
+    Empty migrate_connected;
+
+    Empty migrate_connect_error;
+
+    Empty attach_channels;
+
+    message {
+	mouse_mode mode;
+    } mouse_mode_request;
+
+    message {
+	uint32 num_tokens;
+    } agent_start;
+
+    Data agent_data;
+
+    message {
+        uint32 num_tokens;
+    } @ctype(SpiceMsgcMainAgentTokens) agent_token;
+
+    Empty migrate_end;
+
+    message {
+        uint32 src_version;
+    } migrate_dst_do_seamless;
+
+    Empty migrate_connected_seamless;
+};
+
+enum8 clip_type {
+    NONE,
+    RECTS
+};
+
+flags8 path_flags { /* TODO: C enum names changes */
+    BEGIN = 0,
+    END = 1,
+    CLOSE = 3,
+    BEZIER = 4,
+} @prefix(SPICE_PATH_);
+
+enum8 video_codec_type {
+    MJPEG = 1,
+};
+
+flags8 stream_flags {
+    TOP_DOWN = 0,
+};
+
+enum8 brush_type {
+    NONE,
+    SOLID,
+    PATTERN,
+};
+
+flags8 mask_flags {
+    INVERS,
+};
+
+enum8 image_type {
+    BITMAP,
+    QUIC,
+    RESERVED,
+    LZ_PLT = 100,
+    LZ_RGB,
+    GLZ_RGB,
+    FROM_CACHE,
+    SURFACE,
+    JPEG,
+    FROM_CACHE_LOSSLESS,
+    ZLIB_GLZ_RGB,
+    JPEG_ALPHA,
+    LZ4,
+};
+
+enum8 image_compress {
+    INVALID  = 0,
+    OFF,
+    AUTO_GLZ,
+    AUTO_LZ,
+    QUIC,
+    GLZ,
+    LZ,
+    LZ4,
+};
+
+flags8 image_flags {
+    CACHE_ME,
+    HIGH_BITS_SET,
+    CACHE_REPLACE_ME,
+};
+
+enum8 bitmap_fmt {
+    INVALID,
+    1BIT_LE,
+    1BIT_BE,
+    4BIT_LE,
+    4BIT_BE,
+    8BIT /* 8bit indexed mode */,
+    16BIT, /* 0555 mode */
+    24BIT /* 3 byte, brg */,
+    32BIT /* 4 byte, xrgb in little endian format */,
+    RGBA /* 4 byte, argb in little endian format */,
+    8BIT_A /* 1 byte, alpha */
+};
+
+flags8 bitmap_flags {
+    PAL_CACHE_ME,
+    PAL_FROM_CACHE,
+    TOP_DOWN,
+};
+
+flags8 jpeg_alpha_flags {
+    TOP_DOWN,
+};
+
+enum8 image_scale_mode {
+    INTERPOLATE,
+    NEAREST,
+};
+
+flags16 ropd {
+    INVERS_SRC,
+    INVERS_BRUSH,
+    INVERS_DEST,
+    OP_PUT,
+    OP_OR,
+    OP_AND,
+    OP_XOR,
+    OP_BLACKNESS,
+    OP_WHITENESS,
+    OP_INVERS,
+    INVERS_RES,
+};
+
+/* This *must* remain with values identical to api/winddi.h
+   LA_STYLED == 0x8 (log_2)=> 3
+   LA_STARTGAP == 0x4 (log_2)=> 2
+   This is used by the windows driver.
+ */
+flags8 line_flags {
+    STYLED = 3,
+    START_WITH_GAP = 2,
+};
+
+flags8 string_flags {
+    RASTER_A1,
+    RASTER_A4,
+    RASTER_A8,
+    RASTER_TOP_DOWN,
+};
+
+flags32 surface_flags {
+    /* Adding flags requires some caps check, since old clients only
+       treat the value as an enum and not as a flag (flag == PRIMARY) */
+    PRIMARY
+};
+
+enum32 surface_fmt {
+    INVALID,
+    1_A     = 1,
+    8_A     = 8,
+    16_555  = 16 ,
+    16_565  = 80,
+    32_xRGB = 32,
+    32_ARGB = 96
+};
+
+flags8 alpha_flags {
+    DEST_HAS_ALPHA,
+    SRC_SURFACE_HAS_ALPHA
+};
+
+enum8 resource_type {
+      INVALID,
+      PIXMAP
+} @prefix(SPICE_RES_TYPE_);
+
+struct ClipRects {
+    uint32 num_rects;
+    Rect rects[num_rects] @end;
+};
+
+struct PathSegment {
+    path_flags flags;
+    uint32 count;
+    PointFix points[count] @end;
+}  @ctype(SpicePathSeg);
+
+struct Path {
+    uint32 num_segments;
+    PathSegment segments[num_segments] @ptr_array;
+};
+
+struct Clip {
+    clip_type type;
+    switch (type) {
+    case RECTS:
+        ClipRects rects @outvar(cliprects) @to_ptr;
+    } u @anon;
+};
+
+struct DisplayBase {
+    uint32 surface_id;
+    Rect box;
+    Clip clip;
+} @ctype(SpiceMsgDisplayBase);
+
+struct ResourceID {
+    uint8 type;
+    uint64 id;
+};
+
+struct WaitForChannel {
+    uint8 channel_type;
+    uint8 channel_id;
+    uint64 message_serial;
+};
+
+struct Palette {
+    uint64 unique;
+    uint16 num_ents;
+    uint32 ents[num_ents] @end;
+};
+
+struct BitmapData {
+    bitmap_fmt format;
+    bitmap_flags flags;
+    uint32 x;
+    uint32 y;
+    uint32 stride;
+    switch (flags) {
+    case PAL_FROM_CACHE:
+	uint64 palette_id;
+    default:
+	Palette *palette @outvar(bitmap);
+    } pal @anon;
+    uint8 data[image_size(8, stride, y)] @chunk @nomarshal;
+} @ctype(SpiceBitmap);
+
+struct BinaryData {
+    uint32 data_size;
+    uint8 data[data_size] @nomarshal @chunk;
+} @ctype(SpiceQUICData);
+
+struct LZPLTData {
+    bitmap_flags flags;
+    uint32 data_size;
+    switch (flags) {
+    case PAL_FROM_CACHE:
+	uint64 palette_id;
+    default:
+	Palette *palette @nonnull @outvar(lzplt);
+    } pal @anon;
+    uint8 data[data_size] @nomarshal @chunk;
+};
+
+struct ZlibGlzRGBData {
+    uint32 glz_data_size;
+    uint32 data_size;
+    uint8 data[data_size] @nomarshal @chunk;
+} @ctype(SpiceZlibGlzRGBData);
+
+struct JPEGAlphaData {
+    jpeg_alpha_flags flags;
+    uint32 jpeg_size;
+    uint32 data_size;
+    uint8 data[data_size] @nomarshal @chunk;
+} @ctype(SpiceJPEGAlphaData);
+
+struct Surface {
+    uint32 surface_id;
+};
+
+
+struct Image {
+    struct ImageDescriptor {
+        uint64 id;
+        image_type type;
+        image_flags flags;
+        uint32 width;
+        uint32 height;
+    } descriptor;
+
+    switch (descriptor.type) {
+    case BITMAP:
+        BitmapData bitmap;
+    case QUIC:
+        BinaryData quic;
+    case LZ_RGB:
+    case GLZ_RGB:
+        BinaryData lz_rgb;
+    case JPEG:
+        BinaryData jpeg;
+    case LZ4:
+        BinaryData lz4;
+    case LZ_PLT:
+        LZPLTData lz_plt;
+    case ZLIB_GLZ_RGB:
+        ZlibGlzRGBData zlib_glz;
+    case JPEG_ALPHA:
+        JPEGAlphaData jpeg_alpha;
+    case SURFACE:
+        Surface surface;
+    } u;
+};
+
+struct Pattern {
+    Image *pat @nonnull;
+    Point pos;
+};
+
+struct Brush {
+    brush_type type;
+    switch (type) {
+    case SOLID:
+        uint32 color;
+    case PATTERN:
+        Pattern pattern;
+    } u;
+};
+
+struct QMask {
+    mask_flags flags;
+    Point pos;
+    Image *bitmap;
+};
+
+struct LineAttr {
+    line_flags flags;
+    switch (flags) {
+    case STYLED:
+        uint8 style_nseg;
+   } u1 @anon;
+   switch (flags) {
+   case STYLED:
+        fixed28_4 *style[style_nseg];
+   } u2 @anon;
+};
+
+struct RasterGlyphA1 {
+    Point render_pos;
+    Point glyph_origin;
+    uint16 width;
+    uint16 height;
+    uint8 data[image_size(1, width, height)] @end;
+} @ctype(SpiceRasterGlyph);
+
+struct RasterGlyphA4 {
+    Point render_pos;
+    Point glyph_origin;
+    uint16 width;
+    uint16 height;
+    uint8 data[image_size(4, width, height)] @end;
+} @ctype(SpiceRasterGlyph);
+
+struct RasterGlyphA8 {
+    Point render_pos;
+    Point glyph_origin;
+    uint16 width;
+    uint16 height;
+    uint8 data[image_size(8, width, height)] @end;
+} @ctype(SpiceRasterGlyph);
+
+struct String {
+    uint16 length;
+    string_flags flags; /* Special: Only one of a1/a4/a8 set */
+    switch (flags) {
+    case RASTER_A1:
+	RasterGlyphA1 glyphs[length] @ctype(SpiceRasterGlyph) @ptr_array;
+    case RASTER_A4:
+	RasterGlyphA4 glyphs[length] @ctype(SpiceRasterGlyph) @ptr_array;
+    case RASTER_A8:
+	RasterGlyphA8 glyphs[length] @ctype(SpiceRasterGlyph) @ptr_array;
+    } u @anon;
+};
+
+struct StreamDataHeader {
+	uint32 id;
+	uint32 multi_media_time;
+};
+
+struct Head {
+    uint32 id;
+    uint32 surface_id;
+    uint32 width;
+    uint32 height;
+    uint32 x;
+    uint32 y;
+    uint32 flags;
+};
+
+channel DisplayChannel : BaseChannel {
+ server:
+    message {
+	uint32 x_res;
+	uint32 y_res;
+	uint32 bits;
+    } mode = 101;
+
+    Empty mark;
+    Empty reset;
+    message {
+	DisplayBase base;
+	Point src_pos;
+    } copy_bits;
+
+    message {
+	uint16 count;
+	ResourceID resources[count] @end;
+    } @ctype(SpiceResourceList) inval_list;
+
+    message {
+	uint8 wait_count;
+	WaitForChannel wait_list[wait_count] @end;
+    } @ctype(SpiceMsgWaitForChannels) inval_all_pixmaps;
+
+    message {
+	uint64 id;
+    } @ctype(SpiceMsgDisplayInvalOne) inval_palette;
+
+    Empty inval_all_palettes;
+
+    message {
+	uint32 surface_id;
+	uint32 id;
+	stream_flags flags;
+	video_codec_type codec_type;
+	uint64 stamp;
+	uint32 stream_width;
+	uint32 stream_height;
+	uint32 src_width;
+	uint32 src_height;
+	Rect dest;
+	Clip clip;
+    } stream_create = 122;
+
+    message {
+	StreamDataHeader base;
+	uint32 data_size;
+	uint8 data[data_size] @end @nomarshal;
+    } stream_data;
+
+    message {
+	uint32 id;
+	Clip clip;
+    } stream_clip;
+
+    message {
+	uint32 id;
+    } stream_destroy;
+
+    Empty stream_destroy_all;
+
+    message {
+	DisplayBase base;
+	struct Fill {
+	    Brush brush @outvar(brush);
+	    ropd rop_descriptor;
+	    QMask mask @outvar(mask);
+	} data;
+    } draw_fill = 302;
+
+    message {
+	DisplayBase base;
+	struct Opaque {
+	    Image *src_bitmap;
+	    Rect src_area;
+	    Brush brush;
+	    ropd rop_descriptor;
+	    image_scale_mode scale_mode;
+	    QMask mask @outvar(mask);
+	} data;
+    } draw_opaque;
+
+    message {
+	DisplayBase base;
+	struct Copy {
+	    Image *src_bitmap;
+	    Rect src_area;
+	    ropd rop_descriptor;
+	    image_scale_mode scale_mode;
+	    QMask mask @outvar(mask);
+	} data;
+    } draw_copy;
+
+    message {
+	DisplayBase base;
+	struct Blend {
+	    Image *src_bitmap;
+	    Rect src_area;
+	    ropd rop_descriptor;
+	    image_scale_mode scale_mode;
+	    QMask mask @outvar(mask);
+	} @ctype(SpiceCopy) data;
+    } draw_blend;
+
+    message {
+	DisplayBase base;
+	struct Blackness {
+	    QMask mask @outvar(mask);
+	} data;
+    } draw_blackness;
+
+    message {
+	DisplayBase base;
+	struct Whiteness {
+	    QMask mask @outvar(mask);
+	} data;
+    } draw_whiteness;
+
+    message {
+	DisplayBase base;
+	struct Invers {
+	    QMask mask @outvar(mask);
+	} data;
+    } draw_invers;
+
+    message {
+	DisplayBase base;
+	struct Rop3 {
+	    Image *src_bitmap;
+	    Rect src_area;
+	    Brush brush;
+	    uint8 rop3;
+	    image_scale_mode scale_mode;
+	    QMask mask @outvar(mask);
+	} data;
+    } draw_rop3;
+
+    message {
+	DisplayBase base;
+	struct Stroke {
+	    Path *path @marshall @nonnull;
+	    LineAttr attr;
+	    Brush brush;
+	    uint16 fore_mode;
+	    uint16 back_mode;
+	} data;
+    } draw_stroke;
+
+    message {
+	DisplayBase base;
+	struct Text {
+	    String *str @marshall @nonnull;
+	    Rect back_area;
+	    Brush fore_brush @outvar(fore_brush);
+	    Brush back_brush @outvar(back_brush);
+	    uint16 fore_mode;
+	    uint16 back_mode;
+	} data;
+    } draw_text;
+
+    message {
+	DisplayBase base;
+	struct Transparent {
+	    Image *src_bitmap;
+	    Rect src_area;
+	    uint32 src_color;
+	    uint32 true_color;
+	} data;
+    } draw_transparent;
+
+    message {
+	DisplayBase base;
+	struct AlphaBlend {
+	    alpha_flags alpha_flags;
+	    uint8 alpha;
+	    Image *src_bitmap;
+	    Rect src_area;
+	} data;
+    } draw_alpha_blend;
+
+    message {
+	uint32 surface_id;
+	uint32 width;
+	uint32 height;
+	surface_fmt format;
+	surface_flags flags;
+    } @ctype(SpiceMsgSurfaceCreate) surface_create;
+
+    message {
+	uint32 surface_id;
+    } @ctype(SpiceMsgSurfaceDestroy) surface_destroy;
+
+    message {
+	StreamDataHeader base;
+	uint32 width;
+	uint32 height;
+	Rect dest;
+	uint32 data_size;
+	uint8 data[data_size] @end @nomarshal;
+    } stream_data_sized;
+
+    message {
+	uint16 count;
+    uint16 max_allowed;
+	Head heads[count] @end;
+    } monitors_config;
+
+    message {
+    	DisplayBase base;
+	struct Composite {
+	    composite_flags flags;
+	    Image *src_bitmap;
+	    switch (flags) {
+	    case HAS_MASK:
+	        Image *mask_bitmap;
+            } a @anon;
+	    switch (flags) {
+	    case HAS_SRC_TRANSFORM:
+	        Transform src_transform;
+            } b @anon;
+	    switch (flags) {
+	    case HAS_MASK_TRANSFORM:
+	        Transform mask_transform;
+            } c @anon;
+	    Point16 src_origin;
+	    Point16 mask_origin;
+	} data;
+    } draw_composite;
+
+    message {
+        uint32 stream_id;
+        uint32 unique_id;
+        uint32 max_window_size;
+        uint32 timeout_ms;
+    } stream_activate_report;
+
+ client:
+    message {
+	uint8 pixmap_cache_id;
+	int64 pixmap_cache_size; //in pixels
+	uint8 glz_dictionary_id;
+	int32 glz_dictionary_window_size;  // in pixels
+    } init = 101;
+
+    message {
+        uint32 stream_id;
+        uint32 unique_id;
+        uint32 start_frame_mm_time;
+        uint32 end_frame_mm_time;
+        uint32 num_frames;
+        uint32 num_drops;
+        int32 last_frame_delay;
+        uint32 audio_delay;
+    } stream_report;
+
+    message {
+        uint8 image_compression;
+    } preferred_compression;
+};
+
+flags16 keyboard_modifier_flags {
+    SCROLL_LOCK,
+    NUM_LOCK,
+    CAPS_LOCK
+};
+
+enum8 mouse_button {
+    INVALID,
+    LEFT,
+    MIDDLE,
+    RIGHT,
+    UP,
+    DOWN,
+};
+
+flags16 mouse_button_mask {
+    LEFT,
+    MIDDLE,
+    RIGHT
+};
+
+channel InputsChannel : BaseChannel {
+ client:
+    message {
+	uint32 code;
+    } @ctype(SpiceMsgcKeyDown) key_down = 101;
+
+    message {
+	uint32 code;
+    } @ctype(SpiceMsgcKeyUp) key_up;
+
+    message {
+	keyboard_modifier_flags modifiers;
+    } @ctype(SpiceMsgcKeyModifiers) key_modifiers;
+
+    Data key_scancode;
+
+    message {
+	int32 dx;
+	int32 dy;
+	mouse_button_mask buttons_state;
+    } @ctype(SpiceMsgcMouseMotion) mouse_motion = 111;
+
+    message {
+	uint32 x;
+	uint32 y;
+	mouse_button_mask buttons_state;
+	uint8 display_id;
+    } @ctype(SpiceMsgcMousePosition) mouse_position;
+
+    message {
+	mouse_button button;
+	mouse_button_mask buttons_state;
+    } @ctype(SpiceMsgcMousePress) mouse_press;
+
+    message {
+	mouse_button button;
+	mouse_button_mask buttons_state;
+    } @ctype(SpiceMsgcMouseRelease) mouse_release;
+
+ server:
+    message {
+	keyboard_modifier_flags keyboard_modifiers;
+    } init = 101;
+
+    message {
+	keyboard_modifier_flags modifiers;
+    } key_modifiers;
+
+    Empty mouse_motion_ack = 111;
+};
+
+enum8 cursor_type {
+    ALPHA,
+    MONO,
+    COLOR4,
+    COLOR8,
+    COLOR16,
+    COLOR24,
+    COLOR32,
+};
+
+flags16 cursor_flags {
+    NONE, /* Means no cursor */
+    CACHE_ME,
+    FROM_CACHE,
+};
+
+struct CursorHeader {
+    uint64 unique;
+    cursor_type type;
+    uint16 width;
+    uint16 height;
+    uint16 hot_spot_x;
+    uint16 hot_spot_y;
+};
+
+struct Cursor {
+    cursor_flags flags;
+    switch (flags) {
+    case !NONE:
+        CursorHeader header;
+    } u @anon;
+    uint8 data[] @as_ptr(data_size);
+};
+
+channel CursorChannel : BaseChannel {
+ server:
+    message {
+	Point16 position;
+	uint16 trail_length;
+	uint16 trail_frequency;
+	uint8 visible;
+	Cursor cursor;
+    } init = 101;
+
+    Empty reset;
+
+    message {
+	Point16 position;
+	uint8 visible;
+	Cursor cursor;
+    } set;
+
+    message {
+	Point16 position;
+    } move;
+
+    Empty hide;
+
+    message {
+	uint16 length;
+	uint16 frequency;
+    } trail;
+
+    message {
+	uint64 id;
+    } @ctype(SpiceMsgDisplayInvalOne) inval_one;
+
+    Empty inval_all;
+};
+
+enum16 audio_data_mode {
+    INVALID,
+    RAW,
+    CELT_0_5_1,
+    OPUS,
+};
+
+enum16 audio_fmt {
+    INVALID,
+    S16,
+};
+
+message AudioVolume {
+    uint8 nchannels;
+    uint16 volume[nchannels] @end;
+};
+
+message AudioMute {
+    uint8 mute;
+};
+
+channel PlaybackChannel : BaseChannel {
+ server:
+    message {
+	uint32 time;
+	uint8 data[] @as_ptr(data_size);
+    } @ctype(SpiceMsgPlaybackPacket) data = 101;
+
+    message {
+	uint32 time;
+	audio_data_mode mode;
+	uint8 data[] @as_ptr(data_size);
+    } mode;
+
+    message {
+       uint32 channels;
+       audio_fmt format;
+       uint32 frequency;
+       uint32 time;
+    } start;
+
+    Empty stop;
+    AudioVolume volume;
+    AudioMute mute;
+
+    message {
+        uint32 latency_ms;
+    } latency;
+};
+
+channel RecordChannel : BaseChannel {
+ server:
+    message {
+	uint32 channels;
+	audio_fmt format;
+	uint32 frequency;
+    } start = 101;
+
+    Empty stop;
+    AudioVolume volume;
+    AudioMute mute;
+ client:
+    message {
+	uint32 time;
+	uint8 data[] @nomarshal @as_ptr(data_size);
+    } @ctype(SpiceMsgcRecordPacket) data = 101;
+
+    message {
+	uint32 time;
+	audio_data_mode mode;
+	uint8 data[] @as_ptr(data_size);
+    } mode;
+
+    message {
+	uint32 time;
+    } start_mark;
+};
+
+enum16 tunnel_service_type {
+    INVALID,
+    GENERIC,
+    IPP,
+};
+
+enum16 tunnel_ip_type {
+    INVALID,
+    IPv4,
+};
+
+struct TunnelIpInfo {
+    tunnel_ip_type type;
+    switch (type) {
+    case IPv4:
+	uint8 ipv4[4];
+    } u;
+} @ctype(SpiceMsgTunnelIpInfo);
+
+channel TunnelChannel : BaseChannel {
+ server:
+    message {
+	uint16 max_num_of_sockets;
+	uint32 max_socket_data_size;
+    } init = 101;
+
+    message {
+	uint32 service_id;
+	TunnelIpInfo virtual_ip;
+    } service_ip_map;
+
+    message {
+	uint16 connection_id;
+	uint32 service_id;
+	uint32 tokens;
+    } socket_open;
+
+    message {
+	uint16 connection_id;
+    } socket_fin;
+
+    message {
+	uint16 connection_id;
+    } socket_close;
+
+    message {
+	uint16 connection_id;
+	uint8 data[] @end;
+    } socket_data;
+
+    message {
+	uint16 connection_id;
+    } socket_closed_ack;
+
+    message {
+	uint16 connection_id;
+	uint32 num_tokens;
+    } @ctype(SpiceMsgTunnelSocketTokens) socket_token;
+
+ client:
+    message {
+	tunnel_service_type type;
+	uint32 id;
+	uint32 group;
+	uint32 port;
+	uint8 *name[cstring()] @nocopy;
+	uint8 *description[cstring()] @nocopy;
+	switch (type) {
+	case IPP:
+	    TunnelIpInfo ip @ctype(SpiceMsgTunnelIpInfo);
+	} u;
+    } @ctype(SpiceMsgcTunnelAddGenericService) service_add = 101;
+
+    message {
+	uint32 id;
+    } @ctype(SpiceMsgcTunnelRemoveService) service_remove;
+
+    message {
+	uint16 connection_id;
+	uint32 tokens;
+    } socket_open_ack;
+
+    message {
+	uint16 connection_id;
+    } socket_open_nack;
+
+    message {
+	uint16 connection_id;
+    } socket_fin;
+
+    message {
+	uint16 connection_id;
+    } socket_closed;
+
+    message {
+	uint16 connection_id;
+    } socket_closed_ack;
+
+    message {
+	uint16 connection_id;
+	uint8 data[] @end;
+    } socket_data;
+
+    message {
+	uint16 connection_id;
+	uint32 num_tokens;
+    } @ctype(SpiceMsgcTunnelSocketTokens) socket_token;
+};
+
+enum32 vsc_message_type {
+    Init = 1,
+    Error,
+    ReaderAdd,
+    ReaderRemove,
+    ATR,
+    CardRemove,
+    APDU,
+    Flush,
+    FlushComplete
+};
+
+struct VscMessageHeader {
+    vsc_message_type type;
+    uint32 reader_id;
+    uint32 length;
+} @ctype(VSCMsgHeader);
+
+struct VscMessageError {
+    uint32 code;
+} @ctype(VSCMsgError);
+
+struct VscMessageAPDU {
+    uint8 data[];
+} @ctype(VSCMsgAPDU);
+
+struct VscMessageATR {
+    uint8 data[];
+} @ctype(VSCMsgATR);
+
+struct VscMessageReaderAdd {
+    int8 *reader_name[] @zero_terminated @nonnull @end @nomarshal;
+} @ctype(VSCMsgReaderAdd);
+
+channel SmartcardChannel : BaseChannel {
+ server:
+    message {
+	vsc_message_type type;
+	uint32 reader_id;
+	uint32 length;
+	uint8 data[] @end;
+    } @ctype(SpiceMsgSmartcard) data = 101;
+
+ client:
+    message {
+	VscMessageHeader header;
+	switch (header.type) {
+	case ReaderAdd:
+	    VscMessageReaderAdd add;
+	case ATR:
+	case APDU:
+	    VscMessageATR atr_data;
+	case Error:
+	    VscMessageError error;
+	} u @anon;
+    } @ctype(SpiceMsgcSmartcard) data = 101;
+
+    message {
+	vsc_message_type type;
+	uint32 reader_id;
+	uint32 length;
+    } @ctype(VSCMsgHeader) header = 101;
+
+    message {
+	uint32 code;
+    } @ctype(VSCMsgError) error = 101;
+
+    message {
+	uint8 data[];
+    } @ctype(VSCMsgATR) atr = 101;
+
+    message {
+	int8 reader_name[] @zero_terminated @nonnull;
+    } @ctype(VSCMsgReaderAdd) reader_add = 101;
+} @ifdef(USE_SMARTCARD);
+
+channel SpicevmcChannel : BaseChannel {
+server:
+    Data data = 101;
+client:
+    Data data = 101;
+};
+
+channel UsbredirChannel : SpicevmcChannel {
+};
+
+channel PortChannel : SpicevmcChannel {
+ client:
+    message {
+	uint8 event;
+    } event = 201;
+ server:
+    message {
+	uint32 name_size;
+	uint8 *name[name_size] @zero_terminated @marshall @nonnull;
+	uint8 opened;
+    } init = 201;
+    message {
+	uint8 event;
+    } event;
+};
+
+channel WebDAVChannel : PortChannel {
+};
+
+protocol Spice {
+    MainChannel main = 1;
+    DisplayChannel display;
+    InputsChannel inputs;
+    CursorChannel cursor;
+    PlaybackChannel playback;
+    RecordChannel record;
+    TunnelChannel tunnel;
+    SmartcardChannel smartcard;
+    UsbredirChannel usbredir;
+    PortChannel port;
+    WebDAVChannel webdav;
+};
diff --git a/spice1.proto b/spice1.proto
new file mode 100644
index 0000000..67eb0e6
--- /dev/null
+++ b/spice1.proto
@@ -0,0 +1,943 @@
+/* built in types:
+   int8, uint8, 16, 32, 64
+*/
+
+typedef fixed28_4 int32 @ctype(SPICE_FIXED28_4);
+
+struct Point {
+    int32 x;
+    int32 y;
+};
+
+struct Point16 {
+    int16 x;
+    int16 y;
+};
+
+struct PointFix {
+    fixed28_4 x;
+    fixed28_4 y;
+};
+
+struct Rect {
+    int32 top;
+    int32 left;
+    int32 bottom;
+    int32 right;
+};
+
+enum32 link_err {
+    OK,
+    ERROR,
+    INVALID_MAGIC,
+    INVALID_DATA,
+    VERSION_MISMATCH,
+    NEED_SECURED,
+    NEED_UNSECURED,
+    PERMISSION_DENIED,
+    BAD_CONNECTION_ID,
+    CHANNEL_NOT_AVAILABLE
+};
+
+enum32 warn_code {
+    WARN_GENERAL
+} @prefix(SPICE_);
+
+enum32 info_code {
+    INFO_GENERAL
+} @prefix(SPICE_);
+
+flags32 migrate_flags {
+    NEED_FLUSH,
+    NEED_DATA_TRANSFER
+} @prefix(SPICE_MIGRATE_);
+
+enum32 notify_severity {
+    INFO,
+    WARN,
+    ERROR,
+};
+
+enum32 notify_visibility {
+    LOW,
+    MEDIUM,
+    HIGH,
+};
+
+flags32 mouse_mode {
+    SERVER,
+    CLIENT,
+};
+
+enum16 pubkey_type {
+    INVALID,
+    RSA,
+    RSA2,
+    DSA,
+    DSA1,
+    DSA2,
+    DSA3,
+    DSA4,
+    DH,
+    EC,
+};
+
+message Empty {
+};
+
+message Data {
+    uint8 data[] @end @ctype(uint8_t);
+} @nocopy;
+
+struct ChannelWait {
+    uint8 channel_type;
+    uint8 channel_id;
+    uint64 message_serial;
+} @ctype(SpiceWaitForChannel);
+
+channel BaseChannel {
+ server:
+    message {
+	migrate_flags flags;
+    } migrate;
+
+    Data migrate_data;
+
+    message {
+	uint32 generation;
+	uint32 window;
+    } set_ack;
+
+    message {
+	uint32 id;
+	uint64 timestamp;
+	uint8 data[] @ctype(uint8_t) @as_ptr(data_len);
+    } ping;
+
+    message {
+	uint8 wait_count;
+	ChannelWait wait_list[wait_count] @end;
+    } wait_for_channels;
+
+    message {
+	uint64 time_stamp;
+	link_err reason;
+    } @ctype(SpiceMsgDisconnect) disconnecting;
+
+    message {
+	uint64 time_stamp;
+	notify_severity severity;
+	notify_visibility visibilty;
+	uint32 what; /* error_code/warn_code/info_code */
+	uint32 message_len;
+	uint8 message[message_len] @end @nomarshal;
+	uint8 zero @end @ctype(uint8_t) @nomarshal;
+    } notify;
+
+ client:
+    message {
+	uint32 generation;
+    } ack_sync;
+
+    Empty ack;
+
+    message {
+	uint32 id;
+	uint64 timestamp;
+    } @ctype(SpiceMsgPing) pong;
+
+    Empty migrate_flush_mark;
+
+    Data migrate_data;
+
+    message {
+    	uint64 time_stamp;
+	link_err reason;
+    } @ctype(SpiceMsgDisconnect) disconnecting;
+};
+
+struct ChannelId {
+    uint8 type;
+    uint8 id;
+};
+
+struct DstInfo {
+	uint16 port;
+	uint16 sport;
+	uint32 host_offset @zero;
+	uint32 host_size;
+	pubkey_type pub_key_type @minor(1);
+	uint32 pub_key_offset @minor(1) @zero;
+	uint32 pub_key_size @minor(1);
+	uint8 host_data[host_size] @as_ptr @zero_terminated;
+	uint8 pub_key_data[pub_key_size] @minor(1) @as_ptr @zero_terminated;
+} @ctype(SpiceMigrationDstInfo);
+
+channel MainChannel : BaseChannel {
+ server:
+     message {
+        DstInfo dst_info;
+    } @ctype(SpiceMsgMainMigrationBegin) migrate_begin = 101;
+
+    Empty migrate_cancel;
+
+    message {
+	uint32 session_id;
+	uint32 display_channels_hint;
+	uint32 supported_mouse_modes;
+	uint32 current_mouse_mode;
+	uint32 agent_connected;
+	uint32 agent_tokens;
+	uint32 multi_media_time;
+	uint32 ram_hint;
+    } init;
+
+    message {
+	uint32 num_of_channels;
+	ChannelId channels[num_of_channels] @end;
+    } @ctype(SpiceMsgChannels) channels_list;
+
+    message {
+	mouse_mode supported_modes;
+	mouse_mode current_mode @unique_flag;
+    } mouse_mode;
+
+    message {
+	uint32 time;
+    } @ctype(SpiceMsgMainMultiMediaTime) multi_media_time;
+
+    Empty agent_connected;
+
+    message {
+	link_err error_code;
+    } @ctype(SpiceMsgMainAgentDisconnect) agent_disconnected;
+
+    Data agent_data;
+
+    message {
+	uint32 num_tokens;
+    } @ctype(SpiceMsgMainAgentTokens) agent_token;
+
+    message {
+      uint16 port;
+      uint16 sport;
+      uint32 host_offset @zero;
+      uint32 host_size;
+      uint32 cert_subject_offset @zero;
+      uint32 cert_subject_size;
+      uint8 host_data[host_size] @as_ptr @zero_terminated;
+      uint8 cert_subject_data[cert_subject_size] @as_ptr @zero_terminated;
+    } @ctype(SpiceMsgMainMigrationSwitchHost) migrate_switch_host;
+
+ client:
+    message {
+	uint64 cache_size;
+    } @ctype(SpiceMsgcClientInfo) client_info = 101;
+
+    Empty migrate_connected;
+
+    Empty migrate_connect_error;
+
+    Empty attach_channels;
+
+    message {
+	mouse_mode mode;
+    } mouse_mode_request;
+
+    message {
+	uint32 num_tokens;
+    } agent_start;
+
+    Data agent_data;
+
+    message {
+        uint32 num_tokens;
+    } @ctype(SpiceMsgcMainAgentTokens) agent_token;
+};
+
+enum32 clip_type {
+    NONE,
+    RECTS
+};
+
+flags32 path_flags { /* TODO: C enum names changes */
+    BEGIN = 0,
+    END = 1,
+    CLOSE = 3,
+    BEZIER = 4,
+} @prefix(SPICE_PATH_);
+
+enum32 video_codec_type {
+    MJPEG = 1,
+};
+
+flags32 stream_flags {
+    TOP_DOWN = 0,
+};
+
+enum32 brush_type {
+    NONE,
+    SOLID,
+    PATTERN,
+};
+
+flags8 mask_flags {
+    INVERS,
+};
+
+enum8 image_type {
+    BITMAP,
+    QUIC,
+    RESERVED,
+    LZ_PLT = 100,
+    LZ_RGB,
+    GLZ_RGB,
+    FROM_CACHE,
+};
+
+flags8 image_flags {
+    CACHE_ME,
+};
+
+enum8 bitmap_fmt {
+    INVALID,
+    1BIT_LE,
+    1BIT_BE,
+    4BIT_LE,
+    4BIT_BE,
+    8BIT /* 8bit indexed mode */,
+    16BIT, /* 0555 mode */
+    24BIT /* 3 byte, brg */,
+    32BIT /* 4 byte, xrgb in little endian format */,
+    RGBA /* 4 byte, argb in little endian format */
+};
+
+flags8 bitmap_flags {
+    PAL_CACHE_ME,
+    PAL_FROM_CACHE,
+    TOP_DOWN,
+};
+
+enum8 image_scale_mode {
+    INTERPOLATE,
+    NEAREST,
+};
+
+flags16 ropd {
+    INVERS_SRC,
+    INVERS_BRUSH,
+    INVERS_DEST,
+    OP_PUT,
+    OP_OR,
+    OP_AND,
+    OP_XOR,
+    OP_BLACKNESS,
+    OP_WHITENESS,
+    OP_INVERS,
+    INVERS_RES,
+};
+
+flags8 line_flags {
+    STYLED = 3,
+    START_WITH_GAP = 2,
+};
+
+enum8 line_cap {
+    ROUND,
+    SQUARE,
+    BUTT,
+};
+
+enum8 line_join {
+    ROUND,
+    BEVEL,
+    MITER,
+};
+
+flags16 string_flags {
+    RASTER_A1,
+    RASTER_A4,
+    RASTER_A8,
+    RASTER_TOP_DOWN,
+};
+
+enum8 resource_type {
+      INVALID,
+      PIXMAP
+} @prefix(SPICE_RES_TYPE_);
+
+struct ClipRects {
+    uint32 num_rects;
+    Rect rects[num_rects] @end;
+};
+
+struct PathSegment {
+    path_flags flags;
+    uint32 count;
+    PointFix points[count] @end;
+}  @ctype(SpicePathSeg);
+
+struct Path {
+    uint32 segments_size @bytes_count(num_segments);
+    PathSegment segments[bytes(segments_size, num_segments)] @ptr_array;
+};
+
+struct Clip {
+    clip_type type;
+    switch (type) {
+    case RECTS:
+        ClipRects *rects @outvar(cliprects);
+    default:
+        uint64 data @zero;
+    } u @anon;
+};
+
+struct DisplayBase {
+    uint32 surface_id @virtual(0);
+    Rect box;
+    Clip clip;
+} @ctype(SpiceMsgDisplayBase);
+
+struct ResourceID {
+    uint8 type;
+    uint64 id;
+};
+
+struct WaitForChannel {
+    uint8 channel_type;
+    uint8 channel_id;
+    uint64 message_serial;
+};
+
+struct Palette {
+    uint64 unique;
+    uint16 num_ents;
+    uint32 ents[num_ents] @end;
+};
+
+struct BitmapData {
+    bitmap_fmt format;
+    bitmap_flags flags;
+    uint32 x;
+    uint32 y;
+    uint32 stride;
+    switch (flags) {
+    case PAL_FROM_CACHE:
+	uint64 palette_id;
+    default:
+	Palette *palette @outvar(bitmap);
+    } pal @anon;
+    uint8 *data[image_size(8, stride, y)] @chunk; /* pointer to array, not array of pointers as in C */
+} @ctype(SpiceBitmap);
+
+struct BinaryData {
+    uint32 data_size;
+    uint8 data[data_size] @nomarshal @chunk;
+} @ctype(SpiceQUICData);
+
+struct LZPLTData {
+    bitmap_flags flags;
+    uint32 data_size;
+    switch (flags) {
+    case PAL_FROM_CACHE:
+	uint64 palette_id;
+    default:
+	Palette *palette @nonnull @outvar(lzplt);
+    } pal @anon;
+    uint8 data[data_size] @nomarshal @chunk;
+};
+
+struct Image {
+    struct ImageDescriptor {
+        uint64 id;
+        image_type type;
+        image_flags flags;
+        uint32 width;
+        uint32 height;
+    } descriptor;
+
+    switch (descriptor.type) {
+    case BITMAP:
+        BitmapData bitmap;
+    case QUIC:
+        BinaryData quic;
+    case LZ_RGB:
+    case GLZ_RGB:
+        BinaryData lz_rgb;
+    case LZ_PLT:
+        LZPLTData lz_plt;
+    } u;
+};
+
+struct Pattern {
+    Image *pat @nonnull;
+    Point pos;
+};
+
+struct Brush {
+    brush_type type;
+    switch (type) {
+    case SOLID:
+        uint32 color;
+    case PATTERN:
+        Pattern pattern;
+    } u @fixedsize;
+};
+
+struct QMask {
+    mask_flags flags;
+    Point pos;
+    Image *bitmap;
+};
+
+struct LineAttr {
+    line_flags flags;
+    line_join join_style @zero;
+    line_cap end_style @zero;
+    uint8 style_nseg;
+    fixed28_4 width @zero;
+    fixed28_4 miter_limit @zero;
+    fixed28_4 *style[style_nseg];
+};
+
+struct RasterGlyphA1 {
+    Point render_pos;
+    Point glyph_origin;
+    uint16 width;
+    uint16 height;
+    uint8 data[image_size(1, width, height)] @end;
+} @ctype(SpiceRasterGlyph);
+
+struct RasterGlyphA4 {
+    Point render_pos;
+    Point glyph_origin;
+    uint16 width;
+    uint16 height;
+    uint8 data[image_size(4, width, height)] @end;
+} @ctype(SpiceRasterGlyph);
+
+struct RasterGlyphA8 {
+    Point render_pos;
+    Point glyph_origin;
+    uint16 width;
+    uint16 height;
+    uint8 data[image_size(8, width, height)] @end;
+} @ctype(SpiceRasterGlyph);
+
+struct String {
+    uint16 length;
+    string_flags flags; /* Special: Only one of a1/a4/a8 set */
+    switch (flags) {
+    case RASTER_A1:
+	RasterGlyphA1 glyphs[length] @ctype(SpiceRasterGlyph) @ptr_array;
+    case RASTER_A4:
+	RasterGlyphA4 glyphs[length] @ctype(SpiceRasterGlyph) @ptr_array;
+    case RASTER_A8:
+	RasterGlyphA8 glyphs[length] @ctype(SpiceRasterGlyph) @ptr_array;
+    } u @anon;
+};
+
+struct StreamDataHeader {
+	uint32 id;
+	uint32 multi_media_time;
+};
+
+channel DisplayChannel : BaseChannel {
+ server:
+    message {
+	uint32 x_res;
+	uint32 y_res;
+	uint32 bits;
+    } mode = 101;
+
+    Empty mark;
+    Empty reset;
+
+    message {
+	DisplayBase base;
+	Point src_pos;
+    } copy_bits;
+
+    message {
+	uint16 count;
+	ResourceID resources[count] @end;
+    } @ctype(SpiceResourceList) inval_list;
+
+    message {
+	uint8 wait_count;
+	WaitForChannel wait_list[wait_count] @end;
+    } @ctype(SpiceMsgWaitForChannels) inval_all_pixmaps;
+
+    message {
+	uint64 id;
+    } @ctype(SpiceMsgDisplayInvalOne) inval_palette;
+
+    Empty inval_all_palettes;
+
+    message {
+	uint32 surface_id @virtual(0);
+	uint32 id;
+	stream_flags flags;
+	video_codec_type codec_type;
+	uint64 stamp;
+	uint32 stream_width;
+	uint32 stream_height;
+	uint32 src_width;
+	uint32 src_height;
+	Rect dest;
+	Clip clip;
+    } stream_create = 122;
+
+    message {
+	StreamDataHeader base;
+	uint32 data_size;
+	uint32 pad_size @zero;
+	uint8 data[data_size] @end  @nomarshal;
+	/* Ignore: uint8 padding[pad_size] */
+    } stream_data;
+
+    message {
+	uint32 id;
+	Clip clip;
+    } stream_clip;
+
+    message {
+	uint32 id;
+    } stream_destroy;
+
+    Empty stream_destroy_all;
+
+    message {
+	DisplayBase base;
+	struct Fill {
+	    Brush brush @outvar(brush);
+	    uint16 rop_descriptor;
+	    QMask mask @outvar(mask);
+	} data;
+    } draw_fill = 302;
+
+    message {
+	DisplayBase base;
+	struct Opaque {
+	    Image *src_bitmap;
+	    Rect src_area;
+	    Brush brush;
+	    ropd rop_descriptor;
+	    image_scale_mode scale_mode;
+	    QMask mask @outvar(mask);
+	} data;
+    } draw_opaque;
+
+    message {
+	DisplayBase base;
+	struct Copy {
+	    Image *src_bitmap;
+	    Rect src_area;
+	    ropd rop_descriptor;
+	    image_scale_mode scale_mode;
+	    QMask mask @outvar(mask);
+	} data;
+    } draw_copy;
+
+    message {
+	DisplayBase base;
+	struct Blend {
+	    Image *src_bitmap;
+	    Rect src_area;
+	    ropd rop_descriptor;
+	    image_scale_mode scale_mode;
+	    QMask mask @outvar(mask);
+	} @ctype(SpiceCopy) data;
+    } draw_blend;
+
+    message {
+	DisplayBase base;
+	struct Blackness {
+	    QMask mask @outvar(mask);
+	} data;
+    } draw_blackness;
+
+    message {
+	DisplayBase base;
+	struct Whiteness {
+	    QMask mask @outvar(mask);
+	} data;
+    } draw_whiteness;
+
+    message {
+	DisplayBase base;
+	struct Invers {
+	    QMask mask @outvar(mask);
+	} data;
+    } draw_invers;
+
+    message {
+	DisplayBase base;
+	struct Rop3 {
+	    Image *src_bitmap;
+	    Rect src_area;
+	    Brush brush;
+	    uint8 rop3;
+	    image_scale_mode scale_mode;
+	    QMask mask @outvar(mask);
+	} data;
+    } draw_rop3;
+
+    message {
+	DisplayBase base;
+	struct Stroke {
+	    Path *path;
+	    LineAttr attr;
+	    Brush brush;
+	    uint16 fore_mode;
+	    uint16 back_mode;
+	} data;
+    } draw_stroke;
+
+    message {
+	DisplayBase base;
+	struct Text {
+	    String *str;
+	    Rect back_area;
+	    Brush fore_brush @outvar(fore_brush);
+	    Brush back_brush @outvar(back_brush);
+	    uint16 fore_mode;
+	    uint16 back_mode;
+	} data;
+    } draw_text;
+
+    message {
+	DisplayBase base;
+	struct Transparent {
+	    Image *src_bitmap;
+	    Rect src_area;
+	    uint32 src_color;
+	    uint32 true_color;
+	} data;
+    } draw_transparent;
+
+    message {
+	DisplayBase base;
+	struct AlphaBlend {
+	    int8 alpha_flags @virtual(0);
+	    uint8 alpha;
+	    Image *src_bitmap;
+	    Rect src_area;
+	} data;
+    } draw_alpha_blend;
+
+ client:
+    message {
+	uint8 pixmap_cache_id;
+	int64 pixmap_cache_size; //in pixels
+	uint8 glz_dictionary_id;
+	int32 glz_dictionary_window_size;  // in pixels
+    } init = 101;
+};
+
+flags32 keyboard_modifier_flags {
+    SCROLL_LOCK,
+    NUM_LOCK,
+    CAPS_LOCK
+};
+
+enum32 mouse_button {
+    INVALID,
+    LEFT,
+    MIDDLE,
+    RIGHT,
+    UP,
+    DOWN,
+};
+
+flags32 mouse_button_mask {
+    LEFT,
+    MIDDLE,
+    RIGHT
+};
+
+channel InputsChannel : BaseChannel {
+ client:
+    message {
+	uint32 code;
+    } @ctype(SpiceMsgcKeyDown) key_down = 101;
+
+    message {
+	uint32 code;
+    } @ctype(SpiceMsgcKeyUp) key_up;
+
+    message {
+	keyboard_modifier_flags modifiers;
+    } @ctype(SpiceMsgcKeyModifiers) key_modifiers;
+
+    message {
+	int32 dx;
+	int32 dy;
+	mouse_button_mask buttons_state;
+    } @ctype(SpiceMsgcMouseMotion) mouse_motion = 111;
+
+    message {
+	uint32 x;
+	uint32 y;
+	mouse_button_mask buttons_state;
+	uint8 display_id;
+    } @ctype(SpiceMsgcMousePosition) mouse_position;
+
+    message {
+	mouse_button button;
+	mouse_button_mask buttons_state;
+    } @ctype(SpiceMsgcMousePress) mouse_press;
+
+    message {
+	mouse_button button;
+	mouse_button_mask buttons_state;
+    } @ctype(SpiceMsgcMouseRelease) mouse_release;
+
+ server:
+    message {
+	keyboard_modifier_flags keyboard_modifiers;
+    } init = 101;
+
+    message {
+	keyboard_modifier_flags modifiers;
+    } key_modifiers;
+
+    Empty mouse_motion_ack = 111;
+};
+
+enum16 cursor_type {
+    ALPHA,
+    MONO,
+    COLOR4,
+    COLOR8,
+    COLOR16,
+    COLOR24,
+    COLOR32,
+};
+
+flags32 cursor_flags {
+    NONE, /* Means no cursor */
+    CACHE_ME,
+    FROM_CACHE,
+};
+
+struct CursorHeader {
+    uint64 unique;
+    cursor_type type;
+    uint16 width;
+    uint16 height;
+    uint16 hot_spot_x;
+    uint16 hot_spot_y;
+};
+
+struct Cursor {
+    cursor_flags flags;
+    CursorHeader header;
+    uint8 data[] @as_ptr(data_size);
+};
+
+channel CursorChannel : BaseChannel {
+ server:
+    message {
+	Point16 position;
+	uint16 trail_length;
+	uint16 trail_frequency;
+	uint8 visible;
+	Cursor cursor;
+    } init = 101;
+
+    Empty reset;
+
+    message {
+	Point16 position;
+	uint8 visible;
+	Cursor cursor;
+    } set;
+
+    message {
+	Point16 position;
+    } move;
+
+    Empty hide;
+
+    message {
+	uint16 length;
+	uint16 frequency;
+    } trail;
+
+    message {
+	uint64 id;
+    } @ctype(SpiceMsgDisplayInvalOne) inval_one;
+
+    Empty inval_all;
+};
+
+enum32 audio_data_mode {
+    INVALID,
+    RAW,
+    CELT_0_5_1,
+    OPUS,
+};
+
+enum32 audio_fmt {
+    INVALID,
+    S16,
+};
+
+channel PlaybackChannel : BaseChannel {
+ server:
+    message {
+	uint32 time;
+	uint8 data[] @as_ptr(data_size);
+    } @ctype(SpiceMsgPlaybackPacket) data = 101;
+
+    message {
+	uint32 time;
+	audio_data_mode mode;
+	uint8 data[] @as_ptr(data_size);
+    } mode;
+
+    message {
+       uint32 channels;
+       audio_fmt format;
+       uint32 frequency;
+       uint32 time;
+    } start;
+
+    Empty stop;
+};
+
+channel RecordChannel : BaseChannel {
+ server:
+    message {
+	uint32 channels;
+	audio_fmt format;
+	uint32 frequency;
+    } start = 101;
+
+    Empty stop;
+ client:
+    message {
+	uint32 time;
+	uint8 data[] @nomarshal @as_ptr(data_size);
+    } @ctype(SpiceMsgcRecordPacket) data = 101;
+
+    message {
+	uint32 time;
+	audio_data_mode mode;
+	uint8 data[] @as_ptr(data_size);
+    } mode;
+
+    message {
+	uint32 time;
+    } start_mark;
+};
+
+protocol Spice {
+    MainChannel main = 1;
+    DisplayChannel display;
+    InputsChannel inputs;
+    CursorChannel cursor;
+    PlaybackChannel playback;
+    RecordChannel record;
+};
-- 
2.4.3

_______________________________________________
Spice-devel mailing list
Spice-devel@xxxxxxxxxxxxxxxxxxxxx
http://lists.freedesktop.org/mailman/listinfo/spice-devel





[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]     [Monitors]