From: Nick Bowler Date: Wed, 10 Jun 2009 22:42:40 +0000 (-0400) Subject: Reorganize object hierarchy to root at "UObject". X-Git-Url: https://git.draconx.ca/gitweb/upkg.git/commitdiff_plain/0ea001fde2555a1335161632b2d78c9397d8f53d Reorganize object hierarchy to root at "UObject". Contrary to past belief, _every_ object serialization has a common header. This removes the need for a specific serializable interface. --- diff --git a/src/Makefile.am b/src/Makefile.am index e244fbd..8bc2f18 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,16 +2,16 @@ lib_LTLIBRARIES = libupkg.la libupkg_la_SOURCES = libupkg.c pack.c include_HEADERS = upkg.h -noinst_HEADERS = pack.h serializable.h exportable.h module.h avl.h +noinst_HEADERS = pack.h exportable.h module.h avl.h if BUILD_UPKG include engine/Makefile.inc bin_PROGRAMS = upkg -upkg_SOURCES = upkg.c avl.c module.c exportable.c serializable.c +upkg_SOURCES = upkg.c avl.c module.c exportable.c uobject.c upkg_CPPFLAGS = $(GLIB_CFLAGS) $(LTDLINCL) -upkg_LDFLAGS = $(GLIB_LIBS) -export-dynamic -upkg_LDADD = libupkg.la $(LIBLTDL) +upkg_LDFLAGS = -export-dynamic +upkg_LDADD = libupkg.la $(LIBLTDL) $(GLIB_LIBS) endif diff --git a/src/engine/music.c b/src/engine/music.c index f095c46..7d57bdb 100644 --- a/src/engine/music.c +++ b/src/engine/music.c @@ -2,8 +2,8 @@ #include #include -#include "serializable.h" #include "exportable.h" +#include "uobject.h" #include "music.h" #include "upkg.h" @@ -20,6 +20,12 @@ struct music_priv { unsigned long nbuf; }; +static void exportable_init(UObjectExportable *); + +G_DEFINE_DYNAMIC_TYPE_EXTENDED(EngineMusic, engine_music, U_OBJECT_TYPE, 0, + G_IMPLEMENT_INTERFACE(U_OBJECT_TYPE_EXPORTABLE, exportable_init) +); + static int export(GObject *o, FILE *f) { struct music_priv *priv = MUSIC_GET_PRIV(o); @@ -51,13 +57,13 @@ static int export_name(GObject *o, char *buf, size_t n) return snprintf(buf, n, "%s", priv->f ? priv->f->name : ""); } -static void exportable_init(UPkgExportable *e) +static void exportable_init(UObjectExportable *e) { e->export = export; e->export_name = export_name; } -static int deserialize(GObject *o, struct upkg_file *f) +static int deserialize(UObject *o, struct upkg_file *f) { struct music_priv *priv = MUSIC_GET_PRIV(o); EngineMusic *m = ENGINE_MUSIC(o); @@ -65,14 +71,16 @@ static int deserialize(GObject *o, struct upkg_file *f) size_t rc; + U_OBJECT_CLASS(engine_music_parent_class)->deserialize(o, f); + priv->nbuf = upkg_export_read(f, priv->buf, sizeof priv->buf); priv->base = 0; /* Random field #1 */ - if (priv->nbuf - priv->base < 2) + if (priv->nbuf - priv->base < 1) return -1; // unpack_16_le(priv->buf+0); - priv->base += 2; + priv->base += 1; if (f->pkg->version > 61) { /* Random field #2 */ @@ -96,9 +104,9 @@ static int deserialize(GObject *o, struct upkg_file *f) return 0; } -static void serializable_init(UPkgSerializable *s) +void music_register(GTypeModule *m) { - s->deserialize = deserialize; + engine_music_register_type(m); } static void engine_music_init(EngineMusic *m) @@ -107,19 +115,13 @@ static void engine_music_init(EngineMusic *m) static void engine_music_class_init(EngineMusicClass *class) { + UObjectClass *uo = U_OBJECT_CLASS(class); + g_type_class_add_private(class, sizeof (struct music_priv)); -} -static void engine_music_class_finalize(EngineMusicClass *class) -{ + uo->deserialize = deserialize; } -G_DEFINE_DYNAMIC_TYPE_EXTENDED(EngineMusic, engine_music, G_TYPE_OBJECT, 0, - G_IMPLEMENT_INTERFACE(UPKG_TYPE_SERIALIZABLE, serializable_init) - G_IMPLEMENT_INTERFACE(UPKG_TYPE_EXPORTABLE, exportable_init) -); - -void music_register(GTypeModule *m) +static void engine_music_class_finalize(EngineMusicClass *class) { - engine_music_register_type(m); } diff --git a/src/engine/music.h b/src/engine/music.h index 92de5f8..24f2a0c 100644 --- a/src/engine/music.h +++ b/src/engine/music.h @@ -2,6 +2,7 @@ #define MUSIC_H_ #include +#include "uobject.h" #define ENGINE_MUSIC_TYPE (engine_music_get_type()) #define ENGINE_MUSIC(obj) \ @@ -17,11 +18,11 @@ typedef struct EngineMusic EngineMusic; typedef struct EngineMusicClass EngineMusicClass; struct EngineMusic { - GObject parent; + UObject parent; }; struct EngineMusicClass { - GObjectClass parent; + UObjectClass parent; }; GType engine_music_get_type(void); diff --git a/src/exportable.c b/src/exportable.c index 8947fcc..c22a448 100644 --- a/src/exportable.c +++ b/src/exportable.c @@ -2,30 +2,30 @@ #include #include "exportable.h" -GType upkg_exportable_get_type(void) +GType u_object_exportable_get_type(void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { - sizeof (UPkgExportable), + sizeof (UObjectExportable), NULL, NULL, }; type = g_type_register_static(G_TYPE_INTERFACE, - "UPkgExportable", &info, 0); + "UObjectExportable", &info, 0); } return type; } -int upkg_export(GObject *obj, FILE *f) +int u_object_export(GObject *obj, FILE *f) { - g_return_val_if_fail(UPKG_IS_EXPORTABLE(obj), -1); - return UPKG_EXPORTABLE_GET_INTERFACE(obj)->export(obj, f); + g_return_val_if_fail(U_OBJECT_IS_EXPORTABLE(obj), -1); + return U_OBJECT_EXPORTABLE_GET_INTERFACE(obj)->export(obj, f); } -int upkg_export_name(GObject *obj, char *buf, size_t n) +int u_object_export_name(GObject *obj, char *buf, size_t n) { - g_return_val_if_fail(UPKG_IS_EXPORTABLE(obj), -1); - return UPKG_EXPORTABLE_GET_INTERFACE(obj)->export_name(obj, buf, n); + g_return_val_if_fail(U_OBJECT_IS_EXPORTABLE(obj), -1); + return U_OBJECT_EXPORTABLE_GET_INTERFACE(obj)->export_name(obj, buf, n); } diff --git a/src/exportable.h b/src/exportable.h index 9fe07e9..623099e 100644 --- a/src/exportable.h +++ b/src/exportable.h @@ -4,26 +4,26 @@ #include #include -#define UPKG_TYPE_EXPORTABLE (upkg_exportable_get_type()) -#define UPKG_EXPORTABLE(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, \ - UPKG_TYPE_EXPORTABLE, UPkgExportable) -#define UPKG_IS_EXPORTABLE(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, \ - UPKG_TYPE_EXPORTABLE) -#define UPKG_EXPORTABLE_GET_INTERFACE(inst) G_TYPE_INSTANCE_GET_INTERFACE \ - (inst, UPKG_TYPE_EXPORTABLE, UPkgExportable) +#define U_OBJECT_TYPE_EXPORTABLE (u_object_exportable_get_type()) +#define U_OBJECT_EXPORTABLE(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, \ + U_OBJECT_TYPE_EXPORTABLE, UObjectExportable) +#define U_OBJECT_IS_EXPORTABLE(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, \ + U_OBJECT_TYPE_EXPORTABLE) +#define U_OBJECT_EXPORTABLE_GET_INTERFACE(inst) G_TYPE_INSTANCE_GET_INTERFACE \ + (inst, U_OBJECT_TYPE_EXPORTABLE, UObjectExportable) -typedef struct UPkgExportable UPkgExportable; +typedef struct UObjectExportable UObjectExportable; -struct UPkgExportable { +struct UObjectExportable { GTypeInterface parent; int (*export)(GObject *obj, FILE *f); int (*export_name)(GObject *obj, char *buf, size_t n); }; -GType upkg_exportable_get_type(void); +GType u_object_exportable_get_type(void); -int upkg_export(GObject *obj, FILE *f); -int upkg_export_name(GObject *obj, char *buf, size_t n); +int u_object_export(GObject *obj, FILE *f); +int u_object_export_name(GObject *obj, char *buf, size_t n); #endif diff --git a/src/module.c b/src/module.c index 025d600..24ef250 100644 --- a/src/module.c +++ b/src/module.c @@ -109,7 +109,7 @@ static int modcmp(const void *a, const void *b, void *_data) int module_init(void) { if (!initialized) { - package_tree = avl_create(modcmp, 0, &avl_allocator_default); + package_tree = avl_create(modcmp, NULL, NULL); if (!package_tree) { fprintf(stderr, "%s: failed to create package tree.\n", __func__); return -1; diff --git a/src/serializable.c b/src/serializable.c deleted file mode 100644 index fdc2cc5..0000000 --- a/src/serializable.c +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include "serializable.h" -#include "upkg.h" - -GType upkg_serializable_get_type(void) -{ - static GType type = 0; - if (type == 0) { - static const GTypeInfo info = { - sizeof (UPkgSerializable), - NULL, - NULL, - }; - - type = g_type_register_static(G_TYPE_INTERFACE, - "UPkgSerializable", &info, 0); - } - return type; -} - -int upkg_deserialize(GObject *obj, struct upkg_file *f) -{ - g_return_val_if_fail(UPKG_IS_SERIALIZABLE(obj), -1); - return UPKG_SERIALIZABLE_GET_INTERFACE(obj)->deserialize(obj, f); -} diff --git a/src/serializable.h b/src/serializable.h deleted file mode 100644 index 5d83d57..0000000 --- a/src/serializable.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef SERIALIZABLE_H_ -#define SERIALIZABLE_H_ - -#include -#include "upkg.h" - -#define UPKG_TYPE_SERIALIZABLE (upkg_serializable_get_type()) -#define UPKG_SERIALIZABLE(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, \ - UPKG_TYPE_SERIALIZABLE, UPkgSerializable) -#define UPKG_IS_SERIALIZABLE(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, \ - UPKG_TYPE_SERIALIZABLE) -#define UPKG_SERIALIZABLE_GET_INTERFACE(inst) G_TYPE_INSTANCE_GET_INTERFACE \ - (inst, UPKG_TYPE_SERIALIZABLE, UPkgSerializable) - -typedef struct UPkgSerializable UPkgSerializable; - -struct UPkgSerializable { - GTypeInterface parent; - - int (*deserialize)(GObject *obj, struct upkg_file *f); -}; - -GType upkg_serializable_get_type(void); -int upkg_deserialize(GObject *obj, struct upkg_file *f); - -#endif diff --git a/src/uobject.c b/src/uobject.c new file mode 100644 index 0000000..7dedc08 --- /dev/null +++ b/src/uobject.c @@ -0,0 +1,268 @@ +#include +#include +#include +#include +#include + +#include "uobject.h" +#include "upkg.h" +#include "pack.h" +#include "avl.h" + +#define U_OBJECT_GET_PRIV(o) \ + G_TYPE_INSTANCE_GET_PRIVATE(o, U_OBJECT_TYPE, struct uobject_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 uobject_property { + const char *name; + GValue val; +}; + +struct uobject_priv { + struct upkg_file *f; + size_t base, len; + + struct avl_table *properties; + + unsigned char buf[2048]; + unsigned long nbuf; +}; + +/* AVL tree functions. */ +static int propcmp(const void *_a, const void *_b, void *_data) +{ + const struct uobject_property *a = _a, *b = _b; + return strcmp(a->name, b->name); +} + +static void propfree(void *item, void *data) +{ + free(item); +} + +static unsigned long +get_real_size(unsigned long *real, unsigned size, unsigned char *buf, size_t 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; + case 5: + if (n < 1) return 0; + *real = *buf; + return 1; + case 6: + if (n < 2) return 0; + *real = unpack_16_le(buf); + return 2; + case 7: + if (n < 4) return 0; + *real = unpack_32_le(buf); + return 4; + } + + return 0; +} + +static unsigned long +decode_property(UObject *o, const char *name, struct upkg_file *f, unsigned long len) +{ + struct uobject_priv *priv = U_OBJECT_GET_PRIV(o); + unsigned long real_size, rc; + int type, size, top; + 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) + return 0; + len += rc; + + switch (type) { + 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]); + u_object_set_property(o, 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)); + u_object_set_property(o, name, &val); + break; + default: + fprintf(stderr, "Unhandled property type %x\n", (unsigned)type); + } + + 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) + return 0; + priv->nbuf = 0; + } + + return real_size; +} + +/* Deserialize properties from an Unreal package. */ +static int deserialize(UObject *o, struct upkg_file *f) +{ + struct uobject_priv *priv = U_OBJECT_GET_PRIV(o); + unsigned long rc, tot_len = 0; + + while (1) { + unsigned long len = 0; + const char *name; + long tmp; + + /* Read some data into buffer. */ + if (!f->eof) { + void *buf = priv->buf + priv->nbuf; + size_t amt = sizeof priv->buf - priv->nbuf; + rc = upkg_export_read(f, buf, amt); + if (rc == 0) + return -1; + priv->nbuf += rc; + } + + /* Get the property name. */ + rc = upkg_decode_index(&tmp, priv->buf+len, priv->nbuf-len); + if (rc == 0) + return -1; + len += rc; + + name = upkg_get_name(f->pkg, tmp); + if (!name) { + return -1; + } else if (strcmp(name, "None") == 0) { + tot_len += rc; + break; + } + + rc = decode_property(U_OBJECT(o), name, f, len); + if (rc == 0) + return -1; + len += rc; + + tot_len += len; + } + + f->base += tot_len; + f->len -= tot_len; + upkg_export_seek(f, 0, SEEK_SET); + + return 0; +} + +int u_object_deserialize(GObject *obj, struct upkg_file *f) +{ + g_return_val_if_fail(IS_U_OBJECT(obj), -1); + U_OBJECT_GET_CLASS(obj)->deserialize(obj, f); +} + +static void u_object_init(UObject *o) +{ + struct uobject_priv *priv = U_OBJECT_GET_PRIV(o); + priv->properties = NULL; +} + +void u_object_set_property(UObject *o, const char *name, const GValue *val) +{ + struct uobject_priv *priv = U_OBJECT_GET_PRIV(o); + struct uobject_property *prop, search = { .name = name }; + void **p; + + if (!priv->properties) { + priv->properties = avl_create(propcmp, NULL, NULL); + g_return_if_fail(priv->properties != NULL); + } + + prop = avl_find(priv->properties, &search); + if (prop) { + g_value_unset(&prop->val); + g_value_init(&prop->val, G_VALUE_TYPE(val)); + g_value_copy(val, &prop->val); + return; + } + + prop = malloc(sizeof *prop); + g_return_if_fail(prop != NULL); + + *prop = (struct uobject_property) { .name = name }; + g_value_init(&prop->val, G_VALUE_TYPE(val)); + g_value_copy(val, &prop->val); + + g_return_if_fail(avl_probe(priv->properties, &prop) != NULL); +} + +const GValue *u_object_get_property(UObject *o, const char *name) +{ + struct uobject_priv *priv = U_OBJECT_GET_PRIV(o); + struct uobject_property *prop, search = { .name = name }; + + if (!priv->properties) + return NULL; + + prop = avl_find(priv->properties, &search); + if (!prop) + return NULL; + return &prop->val; +} + +static void u_object_finalize(GObject *o) +{ + struct uobject_priv *priv = U_OBJECT_GET_PRIV(o); + + if (priv->properties) { + avl_destroy(priv->properties, propfree); + priv->properties = NULL; + } +} + +static void u_object_class_init(UObjectClass *class) +{ + GObjectClass *go = G_OBJECT_CLASS(class); + + class->deserialize = deserialize; + + g_type_class_add_private(class, sizeof (struct uobject_priv)); + go->finalize = u_object_finalize; +} + +G_DEFINE_TYPE(UObject, u_object, G_TYPE_OBJECT); diff --git a/src/uobject.h b/src/uobject.h new file mode 100644 index 0000000..63d8be2 --- /dev/null +++ b/src/uobject.h @@ -0,0 +1,40 @@ +#ifndef UOBJECT_H_ +#define UOBJECT_H_ + +#include +#include "upkg.h" +#include "avl.h" + +#define U_OBJECT_TYPE u_object_get_type() +#define U_OBJECT(obj) \ + G_TYPE_CHECK_INSTANCE_CAST(obj, U_OBJECT_TYPE, UObject) +#define U_OBJECT_CLASS(class) \ + G_TYPE_CHECK_CLASS_CAST(class, U_OBJECT_TYPE, UObjectClass) +#define IS_U_OBJECT(obj) \ + G_TYPE_CHECK_INSTANCE_TYPE(obj, U_OBJECT_TYPE) +#define IS_U_OBJECT_CLASS(class) \ + G_TYPE_CHECK_CLASS_TYPE(class, U_OBJECT_TYPE, UObjectClass) +#define U_OBJECT_GET_CLASS(obj) \ + G_TYPE_INSTANCE_GET_CLASS(obj, U_OBJECT_TYPE, UObjectClass) + +typedef struct UObject UObject; +typedef struct UObjectClass UObjectClass; + +struct UObject { + GObject parent; +}; + +struct UObjectClass { + GObjectClass parent; + + int (*deserialize)(UObject *obj, struct upkg_file *f); +}; + +GType u_object_get_type(void); + +const GValue *u_object_get_property(UObject *obj, const char *name); +void u_object_set_property(UObject *obj, const char *name, const GValue *val); + +int u_object_deserialize(GObject *obj, struct upkg_file *f); + +#endif diff --git a/src/upkg.c b/src/upkg.c index c40d6d1..8c26d27 100644 --- a/src/upkg.c +++ b/src/upkg.c @@ -23,7 +23,7 @@ #include "upkg.h" #include "module.h" -#include "serializable.h" +#include "uobject.h" #include "exportable.h" void print_upkg_flags(const char *prefix, unsigned long flags) @@ -91,13 +91,13 @@ int main(int argc, char **argv) if (!music) return EXIT_FAILURE; struct upkg_file *f = upkg_export_open(pkg, 0); - upkg_deserialize(music, f); + u_object_deserialize(music, f); char name[256]; - upkg_export_name(music, name, sizeof name); + u_object_export_name(music, name, sizeof name); FILE *fp = fopen(name, "wb"); if (!fp) return EXIT_FAILURE; - upkg_export(music, fp); + u_object_export(music, fp); fclose(fp); printf("Wrote %s\n", name);