From 0b66ccda847802bdc7ba4755743031c323df4b7e Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Mon, 8 Jun 2009 15:46:50 -0400 Subject: [PATCH] Initial UObject implementation plus a dumb music extractor. --- configure.ac | 14 ++++++ src/Makefile.am | 8 +++- src/exportable.c | 31 ++++++++++++ src/exportable.h | 29 +++++++++++ src/music.c | 117 +++++++++++++++++++++++++++++++++++++++++++++ src/music.h | 41 ++++++++++++++++ src/serializable.c | 25 ++++++++++ src/serializable.h | 26 ++++++++++ src/upkg.c | 19 ++++++++ 9 files changed, 308 insertions(+), 2 deletions(-) create mode 100644 src/exportable.c create mode 100644 src/exportable.h create mode 100644 src/music.c create mode 100644 src/music.h create mode 100644 src/serializable.c create mode 100644 src/serializable.h diff --git a/configure.ac b/configure.ac index debf262..5b8dbf7 100644 --- a/configure.ac +++ b/configure.ac @@ -8,6 +8,20 @@ AM_INIT_AUTOMAKE([-Wall -Werror foreign]) AC_PROG_CC_C99 +AC_ARG_WITH([gobject], + [AS_HELP_STRING([--with-gobject], + [build tools requiring libgobject [default=yes]] + )], + [], + [with_gobject=yes]) + +have_gobject=no +if test x$with_gobject = xyes; then +AM_PATH_GLIB_2_0(, [have_gobject=yes], [have_gobject=no], [gobject]) +fi + +AM_CONDITIONAL([BUILD_UPKG], [test x$have_gobject = xyes]) + LT_INIT AC_CONFIG_FILES([ diff --git a/src/Makefile.am b/src/Makefile.am index 2002c43..a631216 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,9 +1,13 @@ lib_LTLIBRARIES = libupkg.la -bin_PROGRAMS = upkg libupkg_la_SOURCES = libupkg.c pack.c include_HEADERS = upkg.h noinst_HEADERS = pack.h -upkg_SOURCES = upkg.c +if BUILD_UPKG +bin_PROGRAMS = upkg +upkg_SOURCES = upkg.c exportable.c serializable.c music.c +upkg_CFLAGS = $(GLIB_CFLAGS) +upkg_LDFLAGS = $(GLIB_LIBS) upkg_LDADD = libupkg.la +endif diff --git a/src/exportable.c b/src/exportable.c new file mode 100644 index 0000000..8947fcc --- /dev/null +++ b/src/exportable.c @@ -0,0 +1,31 @@ +#include +#include +#include "exportable.h" + +GType upkg_exportable_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (UPkgExportable), + NULL, + NULL, + }; + + type = g_type_register_static(G_TYPE_INTERFACE, + "UPkgExportable", &info, 0); + } + return type; +} + +int upkg_export(GObject *obj, FILE *f) +{ + g_return_val_if_fail(UPKG_IS_EXPORTABLE(obj), -1); + return UPKG_EXPORTABLE_GET_INTERFACE(obj)->export(obj, f); +} + +int upkg_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); +} diff --git a/src/exportable.h b/src/exportable.h new file mode 100644 index 0000000..9fe07e9 --- /dev/null +++ b/src/exportable.h @@ -0,0 +1,29 @@ +#ifndef EXPORTABLE_H_ +#define EXPORTABLE_H_ + +#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) + +typedef struct UPkgExportable UPkgExportable; + +struct UPkgExportable { + GTypeInterface parent; + + int (*export)(GObject *obj, FILE *f); + int (*export_name)(GObject *obj, char *buf, size_t n); +}; + +GType upkg_exportable_get_type(void); + +int upkg_export(GObject *obj, FILE *f); +int upkg_export_name(GObject *obj, char *buf, size_t n); + +#endif diff --git a/src/music.c b/src/music.c new file mode 100644 index 0000000..4c64a7b --- /dev/null +++ b/src/music.c @@ -0,0 +1,117 @@ +#include +#include +#include + +#include "serializable.h" +#include "exportable.h" +#include "music.h" +#include "upkg.h" + +#define U_MUSIC_GET_PRIV(o) \ + G_TYPE_INSTANCE_GET_PRIVATE(o, U_MUSIC_TYPE, struct music_priv) + +struct music_priv { + unsigned type; + + struct upkg_file *f; + size_t base, len; + + unsigned char buf[2048]; + unsigned long nbuf; +}; + +static void serializable_init(UPkgSerializable *s); +static void exportable_init(UPkgExportable *e); + +G_DEFINE_TYPE_WITH_CODE(UMusic, u_music, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(UPKG_TYPE_SERIALIZABLE, serializable_init) + G_IMPLEMENT_INTERFACE(UPKG_TYPE_EXPORTABLE, exportable_init) +); + +static int export(GObject *o, FILE *f) +{ + struct music_priv *priv = U_MUSIC_GET_PRIV(o); + size_t left = priv->len, rc; + + unsigned char buf[1024]; + + if (upkg_export_seek(priv->f, priv->base, SEEK_SET) != 0) + return -1; + + while (left > 0) { + rc = upkg_export_read(priv->f, buf, MIN(left, sizeof buf)); + if (rc == 0) + return -1; + if (fwrite(buf, 1, rc, f) != rc) + return -1; + left -= rc; + } + + return 0; +} + +static int export_name(GObject *o, char *buf, size_t n) +{ + struct music_priv *priv = U_MUSIC_GET_PRIV(o); + + return snprintf(buf, n, "%s", priv->f->name); +} + +static void exportable_init(UPkgExportable *e) +{ + e->export = export; + e->export_name = export_name; +} + +static int deserialize(GObject *o, struct upkg_file *f) +{ + struct music_priv *priv = U_MUSIC_GET_PRIV(o); + UMusic *m = U_MUSIC(o); + long size; + + size_t rc; + + priv->nbuf = upkg_export_read(f, priv->buf, sizeof priv->buf); + priv->base = 0; + + /* Random field #1 */ + if (priv->nbuf - priv->base < 2) + return -1; + // unpack_16_le(priv->buf+0); + priv->base += 2; + + if (f->pkg->version > 61) { + /* Random field #2 */ + if (priv->nbuf - priv->base < 4) + return -1; + // unpack_32_le(priv->buf+2); + priv->base += 4; + } + + rc = upkg_decode_index(&size, priv->buf+priv->base, priv->nbuf-priv->base); + if (rc == 0 || size < 0) + return -1; + + priv->base += rc; + priv->len = size; + + priv->nbuf -= priv->base; + memmove(priv->buf, priv->buf+priv->base, priv->nbuf); + + priv->f = f; + return 0; +} + +static void serializable_init(UPkgSerializable *s) +{ + s->deserialize = deserialize; +} + +static void u_music_init(UMusic *o) +{ +} + +static void u_music_class_init(UMusicClass *class) +{ + g_type_class_add_private(class, sizeof (struct music_priv)); +} diff --git a/src/music.h b/src/music.h new file mode 100644 index 0000000..d3f685c --- /dev/null +++ b/src/music.h @@ -0,0 +1,41 @@ +#ifndef MUSIC_H_ +#define MUSIC_H_ + +#include + +#define U_MUSIC_TYPE (u_music_get_type()) +#define U_MUSIC(obj) \ + G_TYPE_CHECK_INSTANCE_CAST(obj, U_MUSIC_TYPE, UMusic) +#define U_MUSIC_CLASS(class) \ + G_TYPE_CHECK_CLASS_CAST(class, U_MUSIC_TYPE, UMusicClass) +#define IS_U_MUSIC(obj) \ + G_TYPE_CHECK_INSTANCE_TYPE(obj, U_MUSIC_TYPE) +#define IS_U_MUSIC_CLASS(class) \ + G_TYPE_CHECK_CLASS_TYPE(class, U_MUSIC_TYPE, UMusicClass) + +typedef struct UMusic UMusic; +typedef struct UMusicClass UMusicClass; + +enum { + U_MUSIC_UNKNOWN, + U_MUSIC_IT, + U_MUSIC_S3M, + U_MUSIC_XM, +}; + +struct UMusic { + GObject parent; +}; + +struct UMusicClass { + GObjectClass parent; +}; + +GType u_music_get_type(void); + +static inline GObject *u_music_new(void) +{ + return g_object_new(u_music_get_type(), NULL); +} + +#endif diff --git a/src/serializable.c b/src/serializable.c new file mode 100644 index 0000000..fdc2cc5 --- /dev/null +++ b/src/serializable.c @@ -0,0 +1,25 @@ +#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 new file mode 100644 index 0000000..5d83d57 --- /dev/null +++ b/src/serializable.h @@ -0,0 +1,26 @@ +#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/upkg.c b/src/upkg.c index 20b7336..bcbee2f 100644 --- a/src/upkg.c +++ b/src/upkg.c @@ -19,7 +19,12 @@ #include #include +#include + #include "upkg.h" +#include "serializable.h" +#include "exportable.h" +#include "music.h" void print_upkg_flags(const char *prefix, unsigned long flags) { @@ -52,6 +57,8 @@ int main(int argc, char **argv) { struct upkg *pkg; + g_type_init(); + if (argc < 2) { fprintf(stderr, "usage: upkg file\n"); return EXIT_FAILURE; @@ -79,6 +86,18 @@ int main(int argc, char **argv) printf("Exports: %lu\n", pkg->export_count); printf("Imports: %lu\n", pkg->import_count); + GObject *music = u_music_new(); + struct upkg_file *f = upkg_export_open(pkg, 0); + upkg_deserialize(music, f); + + char name[256]; + upkg_export_name(music, name, sizeof name); + FILE *fp = fopen(name, "wb"); + if (!fp) return EXIT_FAILURE; + upkg_export(music, fp); + fclose(fp); + printf("Wrote %s\n", name); + upkg_close(pkg); return 0; } -- 2.43.0