From 4b25e119e06befc931d8392985d5a61d1949fadb Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Fri, 4 May 2012 22:53:02 -0400 Subject: [PATCH] uobject: Overhaul the UObject property decoding code. Mostly a total rewrite, this version actually handles struct and array properties (read: advances something resembling the correct number of bytes instead of trying to start the next property somewhere in the middle of the last). This version also has (hopefully) much clearer tracking of file offsets. --- src/uobject/uobject.c | 256 +++++++++++++++++++++++++++--------------- 1 file changed, 167 insertions(+), 89 deletions(-) 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; -- 2.43.0