/* * * 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 #include #include #include /* For handling files */ #include /* For C99 number types */ #include #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 * 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;ibuffer, 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; }