+ 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 (rc == 0 && 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;
+}
+
+/*
+ * TODO: Make this use the uobject_module stuff so packages are not loaded
+ * more than once.
+ */
+static GTypeModule *
+open_import_package(UObject *uo, const struct upkg_import *i)
+{
+ GTypeModule *pkg;
+
+ assert(i->parent == NULL);
+
+ if (strcmp(i->class_package, "Core") != 0
+ || strcmp(i->class_name, "Package") != 0) {
+ u_err(uo, "import root must be Core.Package");
+ return NULL;
+ }
+
+ pkg = u_pkg_open(i->name);
+ if (!pkg || !g_type_module_use(pkg)) {
+ u_err(uo, "failed to open package: %s", i->name);
+ return NULL;
+ }
+
+ if (!U_PKG(pkg)->pkg) {
+ g_type_module_unuse(pkg);
+ u_err(uo, "failed to open package: %s", pkg->name);
+ return NULL;
+ }
+
+ return pkg;
+}
+
+/* Find the export index of a package based on the import chain from another
+ * package. Returns the (non-negative) offset on success, or a negative value
+ * on failure. */
+static long resolve_import(struct upkg *pkg, const struct upkg_import *import)
+{
+ long current, index;
+
+ if (!import->parent)
+ return -1;
+
+ current = resolve_import(pkg, import->parent);
+ if (current != -1 && current >= 0)
+ return -42;
+
+ index = upkg_export_find(pkg, current, import->name);
+ if (index < 0)
+ return -42;
+
+ return index;
+}
+
+static GObject *get_import_object(UObject *uo, unsigned long index)
+{
+ const struct upkg_import *import;
+ GObject *obj = NULL;
+ GTypeModule *pkg;
+ long obj_index;
+
+ import = upkg_get_import(uo->pkg_file->pkg, index);
+ if (!import) {
+ u_err(uo, "invalid package import: %ld", index);
+ return NULL;
+ }
+
+ /* Get the package name at the top of the hierarchy. */
+ for (const struct upkg_import *i = import; i; i = i->parent) {
+ if (i->parent == NULL) {
+ pkg = open_import_package(uo, i);
+ if (!pkg)
+ return NULL;
+ }
+ }
+
+ obj_index = resolve_import(U_PKG(pkg)->pkg, import);
+ if (obj_index < 0) {
+ u_err(uo, "could not find import in package: %s", pkg->name);
+ goto out;
+ }
+
+ obj = u_object_new_from_package(pkg, obj_index);
+out:
+ g_type_module_unuse(pkg);
+ return obj;
+}
+
+static int decode_object_property(UObject *uo, GValue *val, unsigned long len)
+{
+ struct u_object_priv *priv = U_OBJECT_GET_PRIV(uo);
+ GObject *obj = NULL;
+ long index;
+ int rc;
+
+ rc = upkg_decode_index(&index, priv->buf+len, priv->nbuf-len);
+ if (rc == 0 || index == 0)
+ return -1;
+
+ if (index < 0) {
+ obj = get_import_object(uo, -(index+1));
+ } else {
+ obj = u_object_new_from_package(uo->pkg, index-1);
+ }
+
+ g_value_init(val, U_TYPE_OBJECT);
+ g_value_take_object(val, obj);
+ return 0;
+}
+
+/*
+ * Deserialize an IEEE 754 binary32 value in "little endian" (for whatever
+ * that term is worth in this context). That is, when interpreted as a little
+ * endian 32-bit unsigned integer: bit 31 is the sign, bits 30-23 are the
+ * (biased) exponent, and bits 22-0 are the encoded part of the significand.
+ *
+ * The implementation is designed to be agnostic of the platform's actual
+ * float type, but the conversion may be lossy if "float" is not itself a
+ * binary32 format. NaN payloads are not preserved.
+ */
+static float unpack_binary32_le(const unsigned char *buf)
+{
+ unsigned long raw;
+ long significand;
+ int exponent;
+ float result;
+
+ raw = unpack_32_le(buf);
+ exponent = (raw & 0x7f800000) >> 23;
+ significand = (raw & 0x007fffff) >> 0;
+
+ switch (exponent) {
+ case 255:
+ result = significand ? NAN : INFINITY;
+ break;
+ default:
+ significand |= 0x00800000;
+ /* fall through */
+ case 0:
+ result = ldexpf(significand, exponent-126-24);
+ }
+
+ return copysignf(result, raw & 0x80000000 ? -1 : 1);
+}
+
+static unsigned long deserialize_property(UObject *uo, struct prop_head *head)
+{
+ struct u_object_priv *priv = U_OBJECT_GET_PRIV(uo);
+ unsigned long rc, len = 0;
+ GValue val = {0};
+
+ rc = decode_prop_header(uo->pkg_file->pkg, head, priv->buf, priv->nbuf);
+ if (rc == 0)