summaryrefslogtreecommitdiffstats
path: root/src/libr-icons.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libr-icons.c')
-rw-r--r--src/libr-icons.c643
1 files changed, 643 insertions, 0 deletions
diff --git a/src/libr-icons.c b/src/libr-icons.c
new file mode 100644
index 0000000..18dc536
--- /dev/null
+++ b/src/libr-icons.c
@@ -0,0 +1,643 @@
+/*
+ *
+ * Copyright (c) 2008-2011 Erich Hoover
+ *
+ * libr icons - Add icon resources into ELF binaries
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * To provide feedback, report bugs, or otherwise contact me:
+ * ehoover at mines dot edu
+ *
+ */
+
+/* Include compile-time parameters */
+#include "config.h"
+
+#include "libr-icons.h"
+
+/* For "one canvas" SVG documents */
+#include "onecanvas.h"
+
+/* For string manipulation */
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+/* For handling files */
+#include <sys/stat.h>
+
+/* For C99 number types */
+#include <stdint.h>
+
+#define ICON_SECTION ".icon"
+#define TERM_LEN 1
+
+#define OFFSET_ENTRIES 0
+#define OFFSET_GUID OFFSET_ENTRIES+sizeof(uint32_t)
+
+#if defined(__i386)
+ #define ID12FORMAT "%012llx"
+#elif defined(__x86_64)
+ #define ID12FORMAT "%012lx"
+#else
+ #define ID12FORMAT "%012lx"
+ #warning "string formatting may be incorrect on this architecture."
+#endif
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+typedef uint32_t ID8;
+typedef uint16_t ID4;
+typedef struct {uint64_t p:48;} __attribute__((__packed__)) ID12;
+
+typedef struct {
+ ID8 g1;
+ ID4 g2;
+ ID4 g3;
+ ID4 g4;
+ ID12 g5;
+} __attribute__((__packed__)) UUID;
+
+typedef struct {
+ char *name;
+ size_t offset;
+ size_t entry_size;
+ libr_icontype_t type;
+ unsigned int icon_size;
+} iconentry;
+
+typedef struct{
+ size_t size;
+ char *buffer;
+ iconentry entry;
+} iconlist;
+
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
+
+/*
+ * Decode a UUID to its binary representation
+ *
+ * NOTE: The last 12-bit parameter cannot be obtained using (uint64_t *) with
+ * some versions of GCC using some optimization levels. This problem is very
+ * frustrating to debug, so I do not recommend playing with it yourself.
+ */
+UUID guid_decode(char *guid)
+{
+ UUID id = {0x00000000, 0x0000, 0x0000, 0x0000, {0x000000000000} };
+ uint64_t tmp12;
+
+ sscanf(guid, "%08x-%04hx-%04hx-%04hx-" ID12FORMAT, &id.g1, &id.g2, &id.g3, &id.g4, &tmp12);
+ id.g5.p = tmp12;
+ return id;
+}
+
+/*
+ * Return the size of the file represented by the file stream
+ */
+off_t fsize(FILE *handle)
+{
+ struct stat file_stat;
+
+ if(fstat(fileno(handle), &file_stat) == ERROR)
+ return ERROR;
+ return file_stat.st_size;
+}
+
+/*
+ * Create a new icon handle
+ */
+libr_icon *new_icon_handle(libr_icontype_t type, unsigned int icon_size, char *buffer, size_t buffer_size)
+{
+ libr_icon *icon_handle = (libr_icon *) malloc(sizeof(libr_icon));
+
+ icon_handle->type = type;
+ icon_handle->buffer = buffer;
+ icon_handle->icon_size = icon_size;
+ icon_handle->buffer_size = buffer_size;
+ return icon_handle;
+}
+
+/*
+ * Obtain an existing icon resource list
+ */
+int get_iconlist(libr_file *file_handle, iconlist *icons)
+{
+ if(icons == NULL)
+ {
+ /* Need to be able to return SOMETHING */
+ return false;
+ }
+ /* Obtain the icon resource list */
+ icons->buffer = libr_malloc(file_handle, ICON_SECTION, &(icons->size));
+ if(icons->buffer == NULL)
+ return false;
+ return true;
+}
+
+/*
+ * Get the next entry in an icon resource list
+ */
+iconentry *get_nexticon(iconlist *icons, iconentry *last_entry)
+{
+ size_t i;
+
+ /* The icon list is needed both for the data buffer and for a call-specific iconentry instance */
+ if(icons == NULL)
+ return NULL;
+ /* If this is the first call (last_entry == NULL) then return the first entry */
+ if(last_entry == NULL)
+ icons->entry.offset = sizeof(uint32_t)+sizeof(UUID);
+ else
+ icons->entry.offset += icons->entry.entry_size;
+ /* Check to see if we've run out of entries */
+ if(icons->entry.offset >= icons->size)
+ return NULL;
+ i = icons->entry.offset;
+ memcpy(&(icons->entry.entry_size), &(icons->buffer[i]), sizeof(uint32_t));
+ i += sizeof(uint32_t);
+ icons->entry.type = icons->buffer[i];
+ i += sizeof(unsigned char);
+ switch(icons->entry.type)
+ {
+ case LIBR_SVG:
+ icons->entry.icon_size = 0;
+ icons->entry.name = &(icons->buffer[i]);
+ break;
+ case LIBR_PNG:
+ memcpy(&(icons->entry.icon_size), &(icons->buffer[i]), sizeof(uint32_t));
+ i += sizeof(uint32_t);
+ icons->entry.name = &(icons->buffer[i]);
+ break;
+ default:
+ /* Invalid entry type */
+ return NULL;
+ }
+ return &(icons->entry);
+}
+
+/*
+ * Free an icon handle
+ */
+EXPORT_FN int libr_icon_close(libr_icon *icon)
+{
+ if(icon == NULL)
+ return false;
+ if(icon->buffer == NULL)
+ return false;
+ free(icon->buffer);
+ free(icon);
+ return true;
+}
+
+/*
+ * Read an icon resource from an ELF file by name
+ */
+EXPORT_FN libr_icon *libr_icon_geticon_byname(libr_file *handle, char *icon_name)
+{
+ iconentry *entry = NULL;
+ libr_icon *icon = NULL;
+ size_t buffer_size = 0;
+ unsigned int icon_size;
+ libr_icontype_t type;
+ char *buffer = NULL;
+ int inlist = false;
+ iconlist icons;
+
+ if(!get_iconlist(handle, &icons))
+ {
+ /* Failed to obtain a list of ELF icons */
+ return NULL;
+ }
+ /* Look for the icon name in the entry list */
+ while((entry = get_nexticon(&icons, entry)) != NULL)
+ {
+ if(!strcmp(entry->name, icon_name))
+ {
+ type = entry->type;
+ icon_size = entry->icon_size;
+ inlist = true;
+ break;
+ }
+ }
+ if(!inlist)
+ {
+ /* Could not find icon name in the list of icons */
+ return false;
+ }
+ /* Get the icon from the ELF binary */
+ if(!libr_size(handle, icon_name, &buffer_size))
+ {
+ /* Failed to obtain ELF icon size */
+ return NULL;
+ }
+ /* Allocate memory for the icon */
+ buffer = (char *) malloc(buffer_size);
+ if(buffer == NULL)
+ {
+ /* Failed to allocate memory for icon */
+ return NULL;
+ }
+ /* Get the compressed icon from the ELF file */
+ if(!libr_read(handle, icon_name, buffer))
+ {
+ /* Failed to obtain ELF icon */
+ goto geticon_byname_complete;
+ }
+ icon = new_icon_handle(type, icon_size, buffer, buffer_size);
+
+geticon_byname_complete:
+ if(icon == NULL)
+ free(buffer);
+ return icon;
+}
+
+/*
+ * Read an icon resource from an ELF file by the square icon size
+ */
+EXPORT_FN libr_icon *libr_icon_geticon_bysize(libr_file *handle, unsigned int iconsize)
+{
+ unsigned int closest_id = 0, i = 0, j = 0;
+ int found_png = false, found_svg = false;
+ unsigned long closest_size = 0;
+ iconentry *entry = NULL;
+ iconlist icons;
+
+ if(!get_iconlist(handle, &icons))
+ {
+ /* Failed to obtain a list of ELF icons */
+ return NULL;
+ }
+ /* Look for the closest size match, ignore SVG in case there are multiple icons */
+ while((entry = get_nexticon(&icons, entry)) != NULL)
+ {
+ if(entry->type == LIBR_SVG)
+ found_svg = true;
+ if(entry->type == LIBR_PNG)
+ {
+ if(j == 0)
+ {
+ closest_size = entry->icon_size;
+ found_png = true;
+ }
+ if(abs(iconsize-entry->icon_size) < closest_size)
+ {
+ closest_size = entry->icon_size;
+ closest_id = i;
+ }
+ j++;
+ }
+ i++;
+ }
+ /* If any PNG files were found then use the file if:
+ * 1) There are no SVG files <OR>
+ * 2) The PNG is an EXACT size match
+ */
+ if(found_png)
+ {
+ i=0;
+ entry = NULL;
+ while((entry = get_nexticon(&icons, entry)) != NULL)
+ {
+ if(i == closest_id)
+ {
+ if(entry->icon_size == iconsize || !found_svg)
+ return libr_icon_geticon_byname(handle, entry->name);
+ break;
+ }
+ i++;
+ }
+ }
+ /* Otherwise use the SVG (provided that there is one) */
+ if(found_svg)
+ {
+ entry = NULL;
+ while((entry = get_nexticon(&icons, entry)) != NULL)
+ {
+ if(entry->type == LIBR_SVG)
+ {
+ libr_icon *icon = libr_icon_geticon_byname(handle, entry->name);
+ if (icon) {
+ libr_icon *icon_onecanvas;
+ char *buffer;
+
+ /* should we report the requested size for SVG? */
+ icon->icon_size = iconsize;
+
+ /* if the SVG is a "one canvas" document then extract the correctly sized icon */
+ if((buffer = onecanvas_geticon_bysize(icon->buffer, iconsize)) != NULL)
+ {
+ libr_icon_close(icon);
+ icon_onecanvas = new_icon_handle(LIBR_SVG, iconsize, buffer, strlen(buffer));
+ return icon_onecanvas;
+ }
+ }
+ return icon;
+ }
+ }
+ }
+ /* Give up */
+ return NULL;
+}
+
+/*
+ * Obtains the icon UUID for the ELF file
+ */
+EXPORT_FN int libr_icon_getuuid(libr_file *handle, char *uuid)
+{
+ UUID id = {0x00000000, 0x0000, 0x0000, 0x0000, {0x000000000000} };
+ iconlist icons;
+
+ if(!get_iconlist(handle, &icons))
+ {
+ /* Failed to obtain the list of ELF icons */
+ return false;
+ }
+ /* Now store the GUID to the return string */
+ memcpy(&id, &(icons.buffer[OFFSET_GUID]), sizeof(UUID));
+ snprintf(uuid, GUIDSTR_LENGTH, "%08x-%04hx-%04hx-%04hx-" ID12FORMAT "\n", id.g1, id.g2, id.g3, id.g4, (uint64_t) id.g5.p);
+ free(icons.buffer);
+ return true;
+}
+EXPORT_FN int libr_icon_getguid(libr_file *handle, char *uuid) ALIAS_FN(libr_icon_getuuid);
+
+/*
+ * Allocate a buffer containing the data of an icon
+ */
+EXPORT_FN char *libr_icon_malloc(libr_icon *icon, size_t *size)
+{
+ char *iconfile = NULL;
+
+ if(size == NULL)
+ {
+ /* No return size passed */
+ return NULL;
+ }
+ if(!libr_icon_size(icon, size))
+ {
+ /* Failed to obtain embedded icon file size */
+ return NULL;
+ }
+ iconfile = (char *) malloc(*size);
+ if(!libr_icon_read(icon, iconfile))
+ {
+ /* Failed to obtain embedded icon file */
+ free(iconfile);
+ return NULL;
+ }
+ return iconfile;
+}
+
+/*
+ * Create an icon resource to represent a file on the hard disk
+ */
+EXPORT_FN libr_icon *libr_icon_newicon_byfile(libr_icontype_t type, unsigned int icon_size, char *icon_file)
+{
+ libr_icon *icon_handle = NULL;
+ size_t len, buffer_size = 0;
+ char *buffer = NULL;
+ FILE *handle = NULL;
+
+ /* Open a handle to the icon file */
+ if((handle = fopen(icon_file, "r")) == NULL)
+ {
+ /* Failed to open icon file */
+ return NULL;
+ }
+ /* Get the size of the icon file */
+ if((buffer_size = fsize(handle)) == ERROR)
+ {
+ /* Failed to obtain the icon's file size */
+ return NULL;
+ }
+ /* Allocate a buffer for the uncompressed icon */
+ buffer = (char *) malloc(buffer_size);
+ if(buffer == NULL)
+ {
+ /* Failed to allocate a buffer for the icon data */
+ return NULL;
+ }
+ /* Read the uncompressed image from the disk */
+ if((len = fread(buffer, 1, buffer_size, handle)) <= 0)
+ {
+ /* Failed to read icon from disk */
+ goto newicon_complete;
+ }
+ fclose(handle);
+ if(len != buffer_size)
+ {
+ /* Failed to read the entire icon */
+ goto newicon_complete;
+ }
+ /* Allocate the icon handle */
+ icon_handle = new_icon_handle(type, icon_size, buffer, buffer_size);
+
+newicon_complete:
+ if(icon_handle == NULL)
+ free(buffer);
+ return icon_handle;
+}
+
+/*
+ * Copy the icon resource into a buffer
+ */
+EXPORT_FN int libr_icon_read(libr_icon *icon, char *buffer)
+{
+ if(icon == NULL)
+ return false;
+ memcpy(buffer, icon->buffer, icon->buffer_size);
+ return true;
+}
+
+/*
+ * Get the memory size of an icon resource
+ */
+EXPORT_FN int libr_icon_size(libr_icon *icon, size_t *size)
+{
+ if(icon == NULL)
+ return false;
+ *size = icon->buffer_size;
+ return true;
+}
+
+/*
+ * Save the icon resource to a file
+ */
+EXPORT_FN int libr_icon_save(libr_icon *icon, char *filename)
+{
+ FILE *file = NULL;
+ int ret = false;
+ size_t len;
+
+ if(icon == NULL)
+ return false;
+ /* Open the file to store the image */
+ if((file = fopen(filename, "w")) == NULL)
+ {
+ /* Failed to open file to write the icon */
+ return false;
+ }
+ /* Store the uncompressed icon to disk */
+ if((len = fwrite(icon->buffer, 1, icon->buffer_size, file)) <= 0)
+ {
+ /* Failed to write output file */
+ goto saveicon_complete;
+ }
+ if(len != icon->buffer_size)
+ {
+ /* Did not write the entire file */
+ goto saveicon_complete;
+ }
+ ret = true;
+
+saveicon_complete:
+ /* Close remaining resources */
+ fclose(file);
+ return ret;
+}
+
+/*
+ * Sets the icon GUID for the ELF file
+ */
+EXPORT_FN int libr_icon_setuuid(libr_file *handle, char *guid)
+{
+ int ret = false;
+ iconlist icons;
+ UUID id;
+ int i;
+
+ /* First check the GUID string */
+ for(i=0;i<strlen(guid);i++)
+ {
+ if(!isxdigit(guid[i]))
+ {
+ if(guid[i] == '-' && (i == 8 || i == 13 || i == 18 || i == 23))
+ continue;
+ /* not a valid GUID string */
+ return false;
+ }
+ }
+ id = guid_decode(guid);
+ /* Now check existing resources */
+ if(!get_iconlist(handle, &icons))
+ {
+ /* No icons exist in the file, create a new icon section with the GUID */
+ uint32_t entries = 0;
+
+ icons.size = sizeof(uint32_t)+sizeof(UUID);
+ icons.buffer = (char *) malloc(icons.size);
+ memcpy(&(icons.buffer[OFFSET_ENTRIES]), &entries, sizeof(uint32_t));
+ }
+ /* Set the GUID and write the resource */
+ if(!libr_write(handle, ICON_SECTION, icons.buffer, icons.size, LIBR_UNCOMPRESSED, LIBR_OVERWRITE))
+ {
+ /* failed to write icon resource */
+ goto setguid_complete;
+ }
+ ret = true;
+
+setguid_complete:
+ free(icons.buffer);
+ return ret;
+}
+EXPORT_FN int libr_icon_setguid(libr_file *handle, char *uuid) ALIAS_FN(libr_icon_setuuid);
+
+/*
+ * Add an icon resource to an ELF file
+ */
+EXPORT_FN int libr_icon_write(libr_file *handle, libr_icon *icon, char *icon_name, libr_overwrite_t overwrite)
+{
+ size_t entry_size, i;
+ iconentry *entry = NULL;
+ iconlist icons;
+ int ret = false;
+
+ /* Check to make sure the user did not make a poor name choice */
+ if(!strcmp(icon_name, ICON_SECTION))
+ {
+ /* ".icon" is a reserved section name */
+ return false;
+
+ }
+ /* Check to make sure the file supports icon resources */
+ if(!get_iconlist(handle, &icons))
+ {
+ /* A GUID must be set first */
+ return false;
+ }
+ /* First add the icon as a new named section */
+ if(!libr_write(handle, icon_name, icon->buffer, icon->buffer_size, LIBR_COMPRESSED, overwrite))
+ {
+ /* Failed to add the icon as a resource */
+ goto writeicon_complete;
+ }
+ /* Look to see if the icon already has an entry */
+ while((entry = get_nexticon(&icons, entry)) != NULL)
+ {
+ if(!strcmp(entry->name, icon_name))
+ {
+ ret = true;
+ goto writeicon_complete;
+ }
+ }
+ /* Now add the icon to the list of icon resources in the ".icon" section */
+ switch(icon->type)
+ {
+ case LIBR_SVG:
+ entry_size = sizeof(uint32_t)+sizeof(unsigned char)+strlen(icon_name)+TERM_LEN;
+ break;
+ case LIBR_PNG:
+ entry_size = sizeof(uint32_t)+sizeof(unsigned char)+sizeof(uint32_t)+strlen(icon_name)+TERM_LEN;
+ break;
+ default:
+ /* Unhandled icon type */
+ goto writeicon_complete;
+ }
+ icons.buffer = (char *) realloc(icons.buffer, icons.size+entry_size);
+ if(icons.buffer == NULL)
+ {
+ /* Failed to expand memory size */
+ goto writeicon_complete;
+ }
+ i = icons.size;
+ memcpy(&(icons.buffer[i]), &entry_size, sizeof(uint32_t));
+ i+=sizeof(uint32_t);
+ icons.buffer[i] = icon->type;
+ i+=sizeof(unsigned char);
+ if(icon->type == LIBR_PNG)
+ {
+ memcpy(&(icons.buffer[i]), &icon->icon_size, sizeof(uint32_t));
+ i+=sizeof(uint32_t);
+ }
+ memcpy(&(icons.buffer[i]), icon_name, strlen(icon_name));
+ i+=strlen(icon_name);
+ icons.buffer[i] = '\0';
+ icons.size += entry_size;
+ if(i != (icons.size-1))
+ printf("Really dangerous, buffer size mismatch!\n");
+ /* Write the updated icon table */
+ if(!libr_write(handle, ICON_SECTION, icons.buffer, icons.size, LIBR_UNCOMPRESSED, LIBR_OVERWRITE))
+ {
+ /* failed to write icon resource */
+ goto writeicon_complete;
+ }
+ ret = true;
+
+writeicon_complete:
+ if(icons.buffer)
+ free(icons.buffer);
+ return ret;
+}