+ 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 (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;