X-Git-Url: https://git.draconx.ca/gitweb/upkg.git/blobdiff_plain/45c92fd47dad10216eb2589b81f35a13cd1837f5..4b25e119e06befc931d8392985d5a61d1949fadb:/src/uobject/uobject.c diff --git a/src/uobject/uobject.c b/src/uobject/uobject.c index a64cf17..9174005 100644 --- a/src/uobject/uobject.c +++ b/src/uobject/uobject.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -30,22 +31,30 @@ #define U_OBJECT_GET_PRIV(o) \ G_TYPE_INSTANCE_GET_PRIVATE(o, U_TYPE_OBJECT, struct u_object_priv) -enum { - PROPERTY_BYTE = 1, - PROPERTY_INTEGER, - PROPERTY_BOOLEAN, - PROPERTY_FLOAT, - PROPERTY_OBJECT, - PROPERTY_NAME, - PROPERTY_STRING, - PROPERTY_CLASS, - PROPERTY_ARRAY, - PROPERTY_STRUCT, - PROPERTY_VECTOR, - PROPERTY_ROTATOR, - PROPERTY_STR, - PROPERTY_MAP, - PROPERTY_FIXEDARRAY, + +struct prop_head { + const char *prop_name, *struct_name; + unsigned long size, array_idx; + bool tag_msb; + + enum { + PROPERTY_END, + PROPERTY_BYTE, + PROPERTY_INTEGER, + PROPERTY_BOOLEAN, + PROPERTY_FLOAT, + PROPERTY_OBJECT, + PROPERTY_NAME, + PROPERTY_STRING, + PROPERTY_CLASS, + PROPERTY_ARRAY, + PROPERTY_STRUCT, + PROPERTY_VECTOR, + PROPERTY_ROTATOR, + PROPERTY_STR, + PROPERTY_MAP, + PROPERTY_FIXEDARRAY, + } type; }; struct u_object_priv { @@ -58,33 +67,124 @@ struct u_object_priv { G_DEFINE_TYPE(UObject, u_object, G_TYPE_OBJECT); +/* + * Determine the actual size (in bytes) of a property, given the 3-bit encoded + * size from the tag byte. Depending on the tag size, there will be 0, 1, 2 + * or 4 additional size bytes, which are to be found in the provided buffer + * which is at least n bytes long. The result is stored in *size. + * + * Returns the number of bytes that were read from the buffer, which may be 0. + * On failure, (i.e., there was not enough material in the buffer) 0 is stored + * in *size. + */ static unsigned long -get_real_size(unsigned long *real, unsigned size, unsigned char *buf, size_t n) +decode_tag_size(unsigned long *size, unsigned tag_size, + const unsigned char *buf, unsigned long n) { - assert(size < 8); - - *real = 0; - switch (size) { - case 0: *real = 1; return 0; - case 1: *real = 2; return 0; - case 2: *real = 4; return 0; - case 3: *real = 12; return 0; - case 4: *real = 16; return 0; + *size = 0; + + switch (tag_size) { + case 0: *size = 1; return 0; + case 1: *size = 2; return 0; + case 2: *size = 4; return 0; + case 3: *size = 12; return 0; + case 4: *size = 16; return 0; case 5: - if (n < 1) return 0; - *real = *buf; + if (n < 1) + return 0; + *size = buf[0]; return 1; case 6: - if (n < 2) return 0; - *real = unpack_16_le(buf); + if (n < 2) + return 0; + *size = unpack_16_le(buf); return 2; case 7: - if (n < 4) return 0; - *real = unpack_32_le(buf); + if (n < 4) + return 0; + *size = unpack_32_le(buf); return 4; } - return 0; + 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; } static int decode_object_property(UObject *uo, GValue *val, unsigned long len) @@ -109,70 +209,61 @@ static int decode_object_property(UObject *uo, GValue *val, unsigned long len) return 0; } -/* - * XXX: I must have been smoking the happy plant when I started property - * decoding. The tracking of various sizes in these functions makes no - * sense at all. - */ - -static unsigned long -decode_property(UObject *o, const char *name, struct upkg_file *f, unsigned long len) +static unsigned long deserialize_property(UObject *uo, struct prop_head *head) { - struct u_object_priv *priv = U_OBJECT_GET_PRIV(o); - unsigned long real_size, rc; - int type, size, top; + struct u_object_priv *priv = U_OBJECT_GET_PRIV(uo); + unsigned long rc, len = 0; GValue val = {0}; - if (priv->nbuf-len < 1) - return 0; - - type = (priv->buf[len] >> 0) & 0x0f; - size = (priv->buf[len] >> 4) & 0x07; - top = (priv->buf[len] >> 7) & 0x01; - len += 1; - - rc = get_real_size(&real_size, size, priv->buf, priv->nbuf-len); - if (real_size == 0) + rc = decode_prop_header(uo->pkg, head, priv->buf, priv->nbuf); + if (rc == 0) return 0; len += rc; - switch (type) { + switch (head->type) { + case PROPERTY_END: + break; case PROPERTY_BYTE: if (priv->nbuf-len < 1) return 0; g_value_init(&val, G_TYPE_UCHAR); g_value_set_uchar(&val, priv->buf[len]); - g_object_set_property(G_OBJECT(o), name, &val); + g_object_set_property(G_OBJECT(uo), head->prop_name, &val); break; case PROPERTY_INTEGER: if (priv->nbuf-len < 4) return 0; g_value_init(&val, G_TYPE_ULONG); g_value_set_ulong(&val, unpack_32_le(priv->buf+len)); - g_object_set_property(G_OBJECT(o), name, &val); + g_object_set_property(G_OBJECT(uo), head->prop_name, &val); break; case PROPERTY_OBJECT: - rc = decode_object_property(o, &val, len); + rc = decode_object_property(uo, &val, len); if (rc != 0) return 0; - g_object_set_property(G_OBJECT(o), name, &val); + g_object_set_property(G_OBJECT(uo), head->prop_name, &val); break; default: - fprintf(stderr, "Unhandled property type %x\n", (unsigned)type); + fprintf(stderr, "Unhandled property type %x\n", + (unsigned)head->type); } + len += head->size; - real_size += len; - if (real_size + len <= priv->nbuf) { - priv->nbuf -= real_size; - memmove(priv->buf, priv->buf+real_size, priv->nbuf); - } else { - long skip = real_size - priv->nbuf; - if (upkg_export_seek(f, skip, SEEK_CUR) != 0) + if (len > priv->nbuf) { + long skip = len - priv->nbuf; + + /* XXX: Long properties are not supported yet, so just seek + * past them for now. */ + if (upkg_export_seek(uo->pkg_file, skip, SEEK_CUR) != 0) return 0; + priv->nbuf = 0; + } else { + priv->nbuf -= len; + memmove(priv->buf, priv->buf+len, priv->nbuf-len); } - return real_size; + return len; } /* Deserialize properties from an Unreal package. */ @@ -183,11 +274,10 @@ static int deserialize(UObject *uo) unsigned long rc, tot_len = 0; while (1) { - unsigned long len = 0; - const char *name; - long tmp; + struct prop_head head; - /* Read some data into buffer. */ + /* Prime the buffer; deserialize_property assumes that there's + * enough data for "small" properties already available. */ if (!f->eof) { void *buf = priv->buf + priv->nbuf; size_t amt = sizeof priv->buf - priv->nbuf; @@ -197,26 +287,14 @@ static int deserialize(UObject *uo) priv->nbuf += rc; } - /* Get the property name. */ - rc = upkg_decode_index(&tmp, priv->buf+len, priv->nbuf-len); - if (rc == 0) + rc = deserialize_property(uo, &head); + if (rc == 0) { return -1; - len = rc; - - name = upkg_get_name(uo->pkg, tmp); - if (!name) { - return -1; - } else if (strcmp(name, "None") == 0) { - tot_len += len; - break; } + tot_len += rc; - rc = decode_property(uo, name, f, len); - if (rc == 0) - return -1; - len = rc; - - tot_len += len; + if (head.type == PROPERTY_END) + break; } f->base += tot_len;