#include #include #include #include #include #include #include "icon_tools.h" #include "plist.h" #include "values.h" #include #include enum { R_NONE = 0, R_RESOURCES, R_SECTION, R_ICON, R_PLIST, R_STRING, }; typedef struct { gchar *tag; gint id; } tag_map_t; static tag_map_t tag_map[] = { {"resources", R_RESOURCES}, {"section", R_SECTION}, {"icon", R_ICON}, {"plist", R_PLIST}, {"string", R_STRING}, }; #define TAG_MAP_SZ (sizeof(tag_map)/sizeof(tag_map_t)) typedef struct { gchar *key; gchar *value; GValue *plist; GQueue *stack; GQueue *tag_stack; gboolean closed_top; } parse_data_t; GList *inc_list = NULL; static gchar* find_file(GList *list, const gchar *name) { gchar *str; GList *link = list; while (link != NULL) { gchar *inc; inc = (gchar*)link->data; str = g_strdup_printf("%s/%s", inc, name); if (g_file_test(str, G_FILE_TEST_IS_REGULAR)) { return str; } g_free(str); link = g_list_next(link); } if (g_file_test(name, G_FILE_TEST_IS_REGULAR)) { return g_strdup(name); } return NULL; } static const gchar* lookup_attr_value( const gchar *name, const gchar **attr_names, const gchar **attr_values) { gint ii; if (attr_names == NULL) return NULL; for (ii = 0; attr_names[ii] != NULL; ii++) { if (strcmp(name, attr_names[ii]) == 0) return attr_values[ii]; } return NULL; } static GValue* read_string_from_file(const gchar *filename) { gchar *buffer; size_t size; GValue *gval; FILE *fd; fd = g_fopen(filename, "r"); if (fd == NULL) return NULL; fseek(fd, 0, SEEK_END); size = ftell(fd); fseek(fd, 0, SEEK_SET); buffer = g_malloc(size+1); size = fread(buffer, 1, size, fd); buffer[size] = 0; gval = ghb_value_new(G_TYPE_STRING); g_value_take_string(gval, buffer); fclose(fd); return gval; } static void start_element( GMarkupParseContext *ctx, const gchar *tag, const gchar **attr_names, const gchar **attr_values, gpointer ud, GError **error) { parse_data_t *pd = (parse_data_t*)ud; union { gint id; gpointer pid; } id; gint ii; // Check to see if the first element found has been closed // If so, ignore any junk following it. if (pd->closed_top) return; for (ii = 0; ii < TAG_MAP_SZ; ii++) { if (strcmp(tag, tag_map[ii].tag) == 0) { id.id = tag_map[ii].id; break; } } if (ii == TAG_MAP_SZ) { g_warning("Unrecognized start tag (%s)", tag); return; } g_queue_push_head(pd->tag_stack, id.pid); GType gtype = 0; GValue *gval = NULL; GValue *current = g_queue_peek_head(pd->stack); switch (id.id) { case R_SECTION: { const gchar *name; name = lookup_attr_value("name", attr_names, attr_values); if (name && strcmp(name, "icons") == 0) { gval = ghb_dict_value_new(); if (pd->key) g_free(pd->key); pd->key = g_strdup(name); g_queue_push_head(pd->stack, gval); } } break; case R_ICON: { gchar *filename; const gchar *name; name = lookup_attr_value("file", attr_names, attr_values); filename = find_file(inc_list, name); name = lookup_attr_value("name", attr_names, attr_values); if (filename && name) { ghb_rawdata_t *rd; GdkPixbuf *pb; GError *err = NULL; pb = gdk_pixbuf_new_from_file(filename, &err); if (pb == NULL) { g_warning("Failed to open icon file %s: %s", filename, err->message); break; } gval = ghb_dict_value_new(); int colorspace = gdk_pixbuf_get_colorspace(pb); gboolean alpha = gdk_pixbuf_get_has_alpha(pb); int width = gdk_pixbuf_get_width(pb); int height = gdk_pixbuf_get_height(pb); int bps = gdk_pixbuf_get_bits_per_sample(pb); int rowstride = gdk_pixbuf_get_rowstride(pb); ghb_dict_insert(gval, g_strdup("colorspace"), ghb_int_value_new(colorspace)); ghb_dict_insert(gval, g_strdup("alpha"), ghb_boolean_value_new(alpha)); ghb_dict_insert(gval, g_strdup("width"), ghb_int_value_new(width)); ghb_dict_insert(gval, g_strdup("height"), ghb_int_value_new(height)); ghb_dict_insert(gval, g_strdup("bps"), ghb_int_value_new(bps)); ghb_dict_insert(gval, g_strdup("rowstride"), ghb_int_value_new(rowstride)); rd = g_malloc(sizeof(ghb_rawdata_t)); rd->data = gdk_pixbuf_get_pixels(pb); rd->size = height * rowstride * bps / 8; GValue *data = ghb_rawdata_value_new(rd); ghb_dict_insert(gval, g_strdup("data"), data); if (pd->key) g_free(pd->key); pd->key = g_strdup(name); g_free(filename); } else { g_warning("%s:missing a requried attribute", name); exit(EXIT_FAILURE); } } break; case R_PLIST: { gchar *filename; const gchar *name; name = lookup_attr_value("file", attr_names, attr_values); filename = find_file(inc_list, name); name = lookup_attr_value("name", attr_names, attr_values); if (filename && name) { gval = ghb_plist_parse_file(filename); if (pd->key) g_free(pd->key); pd->key = g_strdup(name); g_free(filename); } else { g_warning("%s:missing a requried attribute", name); exit(EXIT_FAILURE); } } break; case R_STRING: { gchar *filename; const gchar *name; name = lookup_attr_value("file", attr_names, attr_values); filename = find_file(inc_list, name); name = lookup_attr_value("name", attr_names, attr_values); if (filename && name) { gval = read_string_from_file(filename); if (pd->key) g_free(pd->key); pd->key = g_strdup(name); g_free(filename); } else { g_warning("%s:missing a requried attribute", name); exit(EXIT_FAILURE); } } break; } // Add the element to the current container if (gval) { // There's an element to add if (current == NULL) { pd->plist = gval; return; } gtype = G_VALUE_TYPE(current); if (gtype == ghb_array_get_type()) { ghb_array_append(current, gval); } else if (gtype == ghb_dict_get_type()) { if (pd->key == NULL) { g_warning("No key for dictionary item"); ghb_value_free(gval); } else { ghb_dict_insert(current, g_strdup(pd->key), gval); } } else { g_error("Invalid container type. This shouldn't happen"); } } } static void end_element( GMarkupParseContext *ctx, const gchar *name, gpointer ud, GError **error) { parse_data_t *pd = (parse_data_t*)ud; gint id; union { gint id; gpointer pid; } start_id; gint ii; // Check to see if the first element found has been closed // If so, ignore any junk following it. if (pd->closed_top) return; for (ii = 0; ii < TAG_MAP_SZ; ii++) { if (strcmp(name, tag_map[ii].tag) == 0) { id = tag_map[ii].id; break; } } if (ii == TAG_MAP_SZ) { g_warning("Unrecognized start tag (%s)", name); return; } start_id.pid = g_queue_pop_head(pd->tag_stack); if (start_id.id != id) g_warning("start tag != end tag: (%s %d) %d", name, id, id); GValue *gval = NULL; GValue *current = g_queue_peek_head(pd->stack); GType gtype = 0; switch (id) { case R_SECTION: { g_queue_pop_head(pd->stack); } break; } if (gval) { // Get the top of the data structure stack and if it's an array // or dict, add the current element if (current == NULL) { pd->plist = gval; pd->closed_top = TRUE; return; } gtype = G_VALUE_TYPE(current); if (gtype == ghb_array_get_type()) { ghb_array_append(current, gval); } else if (gtype == ghb_dict_get_type()) { if (pd->key == NULL) { g_warning("No key for dictionary item"); ghb_value_free(gval); } else { ghb_dict_insert(current, g_strdup(pd->key), gval); } } else { g_error("Invalid container type. This shouldn't happen"); } } if (g_queue_is_empty(pd->tag_stack)) pd->closed_top = TRUE; } static void text_data( GMarkupParseContext *ctx, const gchar *text, gsize len, gpointer ud, GError **error) { parse_data_t *pd = (parse_data_t*)ud; if (pd->value) g_free(pd->value); pd->value = g_strdup(text); } static void passthrough( GMarkupParseContext *ctx, const gchar *text, gsize len, gpointer ud, GError **error) { //parse_data_t *pd = (parse_data_t*)ud; //g_debug("passthrough %s", text); } static void parse_error(GMarkupParseContext *ctx, GError *error, gpointer ud) { g_warning("Resource parse error: %s", error->message); } // This is required or the parser crashes static void destroy_notify(gpointer data) { // Do nothing //g_debug("destroy parser"); } GValue* ghb_resource_parse(const gchar *buf, gssize len) { GMarkupParseContext *ctx; GMarkupParser parser; parse_data_t pd; GError *err = NULL; pd.stack = g_queue_new(); pd.tag_stack = g_queue_new(); pd.key = NULL; pd.value = NULL; pd.plist = ghb_dict_value_new(); g_queue_push_head(pd.stack, pd.plist); pd.closed_top = FALSE; parser.start_element = start_element; parser.end_element = end_element; parser.text = text_data; parser.passthrough = passthrough; parser.error = parse_error; ctx = g_markup_parse_context_new(&parser, 0, &pd, destroy_notify); g_markup_parse_context_parse(ctx, buf, len, &err); g_markup_parse_context_end_parse(ctx, &err); g_markup_parse_context_free(ctx); g_queue_free(pd.stack); g_queue_free(pd.tag_stack); return pd.plist; } GValue* ghb_resource_parse_file(FILE *fd) { gchar *buffer; size_t size; GValue *gval; if (fd == NULL) return NULL; fseek(fd, 0, SEEK_END); size = ftell(fd); fseek(fd, 0, SEEK_SET); buffer = g_malloc(size+1); size = fread(buffer, 1, size, fd); buffer[size] = 0; gval = ghb_resource_parse(buffer, (gssize)size); g_free(buffer); return gval; } static void usage(char *cmd) { fprintf(stderr, "Usage: %s [-I ] \n" "Summary:\n" " Creates a resource plist from a resource list\n" "Options:\n" " I - Include path to search for files\n" " Input resources file\n" " Output resources plist file\n" , cmd); exit(EXIT_FAILURE); } #define OPTS "I:" gint main(gint argc, gchar *argv[]) { FILE *file; GValue *gval; int opt; const gchar *src, *dst; do { opt = getopt(argc, argv, OPTS); switch (opt) { case -1: break; case 'I': inc_list = g_list_prepend(inc_list, g_strdup(optarg)); break; } } while (opt != -1); if (optind != argc - 2) { usage(argv[0]); return EXIT_FAILURE; } src = argv[optind++]; dst = argv[optind++]; #if !GLIB_CHECK_VERSION(2, 36, 0) g_type_init(); #endif file = g_fopen(src, "r"); if (file == NULL) { fprintf(stderr, "Error: failed to open %s\n", src); return EXIT_FAILURE; } gval = ghb_resource_parse_file(file); ghb_plist_write_file(dst, gval); fclose(file); return 0; }