Contrary to past belief, _every_ object serialization has a common header.
This removes the need for a specific serializable interface.
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
#include <string.h>
#include <glib-object.h>
-#include "serializable.h"
#include "exportable.h"
+#include "uobject.h"
#include "music.h"
#include "upkg.h"
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);
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);
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 */
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)
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);
}
#define MUSIC_H_
#include <glib-object.h>
+#include "uobject.h"
#define ENGINE_MUSIC_TYPE (engine_music_get_type())
#define ENGINE_MUSIC(obj) \
typedef struct EngineMusicClass EngineMusicClass;
struct EngineMusic {
- GObject parent;
+ UObject parent;
};
struct EngineMusicClass {
- GObjectClass parent;
+ UObjectClass parent;
};
GType engine_music_get_type(void);
#include <glib-object.h>
#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);
}
#include <stdio.h>
#include <glib-object.h>
-#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
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;
+++ /dev/null
-#include <glib-object.h>
-#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);
-}
+++ /dev/null
-#ifndef SERIALIZABLE_H_
-#define SERIALIZABLE_H_
-
-#include <glib-object.h>
-#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
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <glib-object.h>
+
+#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);
--- /dev/null
+#ifndef UOBJECT_H_
+#define UOBJECT_H_
+
+#include <glib-object.h>
+#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
#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)
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);