+ assert(0);
+}
+
+static unsigned long
+decode_array_index(unsigned long *index, const unsigned char *buf,
+ unsigned long n)
+{
+ if (n < 1)
+ return 0;
+
+ /* TODO: Actually implement this instead of aborting. */
+ assert("FIXME" && !(buf[0] & 0x80));
+
+ *index = buf[0];
+ return 1;
+}
+
+/*
+ * Decode the (mostly) generic property header, filling out the struct pointed
+ * to by head. Returns the number of bytes read from the buffer, or 0 on
+ * failure.
+ */
+static unsigned long
+decode_prop_header(struct upkg *upkg, struct prop_head *head,
+ const unsigned char *buf, unsigned long n)
+{
+ unsigned long rc, len = 0;
+ unsigned char tag_size;
+ long index;
+
+ rc = upkg_decode_index(&index, buf+len, n-len);
+ if (rc == 0)
+ return 0;
+ if (!(head->prop_name = upkg_get_name(upkg, index)))
+ return 0;
+ len += rc;
+
+ /* A property called "None" terminates the list, and does not have
+ * the usual header. */
+ if (!strcmp(head->prop_name, "None")) {
+ head->type = PROPERTY_END;
+ head->size = 0;
+ return len;
+ }
+
+ if (n-len < 1)
+ return 0;
+ head->tag_msb = (buf[len] >> 7) & 0x01;
+ tag_size = (buf[len] >> 4) & 0x07;
+ head->type = (buf[len] >> 0) & 0x0f;
+ len++;
+
+ /*
+ * TODO: Confirm the correct relative ordering of the next three
+ * fields.
+ */
+ if (head->type == PROPERTY_STRUCT) {
+ rc = upkg_decode_index(&index, buf+len, n-len);
+ if (rc == 0)
+ return 0;
+ if (!(head->struct_name = upkg_get_name(upkg, index)))
+ return 0;
+ len += rc;
+ }
+
+ rc = decode_tag_size(&head->size, tag_size, buf+len, n-len);
+ if (head->size == 0)
+ return 0;
+ len += rc;
+
+ head->array_idx = 0;
+ if (head->tag_msb && head->type != PROPERTY_BOOLEAN) {
+ rc = decode_array_index(&head->array_idx, buf+len, n-len);
+ if (rc == 0)
+ return 0;
+ len += rc;
+ }
+
+ return len;