Home | History | Annotate | Download | only in utility
      1 // Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <errno.h>
      6 #include <fcntl.h>
      7 #include <getopt.h>
      8 #include <limits.h>
      9 #include <stdarg.h>
     10 #include <stdint.h>
     11 #include <stdio.h>
     12 #include <stdlib.h>
     13 #include <string.h>
     14 #include <sys/mman.h>
     15 #include <sys/stat.h>
     16 #include <sys/types.h>
     17 #include <unistd.h>
     18 
     19 #include "bmpblk_font.h"
     20 #include "image_types.h"
     21 #include "vboot_api.h"
     22 
     23 static char *progname;
     24 
     25 static void error(const char *fmt, ...)
     26 {
     27     va_list args;
     28     va_start( args, fmt );
     29     fprintf(stderr, "%s: ", progname);
     30     vfprintf( stderr, fmt, args );
     31     va_end( args );
     32 }
     33 #define fatal(args...) do { error(args); exit(1); } while(0)
     34 
     35 
     36 /* Command line options */
     37 enum {
     38   OPT_OUTFILE = 1000,
     39 };
     40 
     41 #define DEFAULT_OUTFILE "font.bin"
     42 
     43 
     44 static struct option long_opts[] = {
     45   {"outfile", 1, 0,                   OPT_OUTFILE             },
     46   {NULL, 0, 0, 0}
     47 };
     48 
     49 
     50 /* Print help and return error */
     51 static void HelpAndDie(void) {
     52   fprintf(stderr,
     53           "\n"
     54           "%s - Create a vboot fontfile from a set of BMP files.\n"
     55           "\n"
     56           "Usage:  %s [OPTIONS] BMPFILE [BMPFILE...]\n"
     57           "\n"
     58           "Each BMP file must match *_HEX.bmp, where HEX is the hexadecimal\n"
     59           "representation of the character that the file displays. The images\n"
     60           "will be encoded in the given order. Typically the first image is\n"
     61           "reused to represent any missing characters.\n"
     62           "\n"
     63           "OPTIONS are:\n"
     64           "  --outfile <filename>      Output file (default is %s)\n"
     65           "\n", progname, progname, DEFAULT_OUTFILE);
     66   exit(1);
     67 }
     68 
     69 //////////////////////////////////////////////////////////////////////////////
     70 
     71 // Returns pointer to buffer containing entire file, sets length.
     72 static void *read_entire_file(const char *filename, size_t *length) {
     73   int fd;
     74   struct stat sbuf;
     75   void *ptr;
     76 
     77   *length = 0;                          // just in case
     78 
     79   if (0 != stat(filename, &sbuf)) {
     80     error("Unable to stat %s: %s\n", filename, strerror(errno));
     81     return 0;
     82   }
     83 
     84   if (!sbuf.st_size) {
     85     error("File %s is empty\n", filename);
     86     return 0;
     87   }
     88 
     89   fd = open(filename, O_RDONLY);
     90   if (fd < 0) {
     91     error("Unable to open %s: %s\n", filename, strerror(errno));
     92     return 0;
     93   }
     94 
     95   ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
     96   if (MAP_FAILED == ptr) {
     97     error("Unable to mmap %s: %s\n", filename, strerror(errno));
     98     close(fd);
     99     return 0;
    100   }
    101 
    102   *length = sbuf.st_size;
    103 
    104   close(fd);
    105 
    106   return ptr;
    107 }
    108 
    109 
    110 // Reclaims buffer from read_entire_file().
    111 static void discard_file(void *ptr, size_t length) {
    112   munmap(ptr, length);
    113 }
    114 
    115 //////////////////////////////////////////////////////////////////////////////
    116 
    117 
    118 
    119 int main(int argc, char* argv[]) {
    120   char* outfile = DEFAULT_OUTFILE;
    121   int numimages = 0;
    122   int parse_error = 0;
    123   int i;
    124   FILE *ofp;
    125   FontArrayHeader header;
    126   FontArrayEntryHeader entry;
    127 
    128   progname = strrchr(argv[0], '/');
    129   if (progname)
    130     progname++;
    131   else
    132     progname = argv[0];
    133 
    134   while ((i = getopt_long(argc, argv, "", long_opts, NULL)) != -1) {
    135     switch (i) {
    136       case OPT_OUTFILE:
    137         outfile = optarg;
    138         break;
    139 
    140     default:
    141         /* Unhandled option */
    142         printf("Unknown option\n");
    143         parse_error = 1;
    144         break;
    145     }
    146   }
    147 
    148   numimages = argc - optind;
    149 
    150   if (parse_error || numimages < 1)
    151     HelpAndDie();
    152 
    153   printf("outfile is %s\n", outfile);
    154   printf("numimages is %d\n", numimages);
    155 
    156   ofp = fopen(outfile, "wb");
    157   if (!ofp)
    158     fatal("Unable to open %s: %s\n", outfile, strerror(errno));
    159 
    160   memcpy(&header.signature, FONT_SIGNATURE, FONT_SIGNATURE_SIZE);
    161   header.num_entries = numimages;
    162   if (1 != fwrite(&header, sizeof(header), 1, ofp)) {
    163     error("Can't write header to %s: %s\n", outfile, strerror(errno));
    164     goto bad1;
    165   }
    166 
    167   for(i=0; i<numimages; i++) {
    168     char *imgfile = argv[optind+i];
    169     char *s;
    170     uint32_t ascii;
    171     void *imgdata = 0;
    172     size_t imgsize, filesize, diff;
    173 
    174     s = strrchr(imgfile, '_');
    175     if (!s || 1 != sscanf(s, "_%x.bmp", &ascii)) { // This is not foolproof.
    176       error("Unable to parse the character from filename %s\n", imgfile);
    177       goto bad1;
    178     }
    179 
    180     imgdata = read_entire_file(imgfile, &imgsize);
    181     if (!imgdata)
    182       goto bad1;
    183 
    184     if (FORMAT_BMP != identify_image_type(imgdata, imgsize, &entry.info)) {
    185       error("%s does not contain a valid BMP image\n", imgfile);
    186       goto bad1;
    187     }
    188 
    189     // Pad the image to align it on a 4-byte boundary.
    190     filesize = imgsize;
    191     if (imgsize % 4)
    192       filesize = ((imgsize + 4) / 4) * 4;
    193     diff = filesize - imgsize;
    194 
    195     entry.ascii = ascii;
    196     entry.info.tag = TAG_NONE;
    197     entry.info.compression = COMPRESS_NONE; // we'll compress it all later
    198     entry.info.original_size = filesize;
    199     entry.info.compressed_size = filesize;
    200 
    201     printf("%s => 0x%x %dx%d\n", imgfile, entry.ascii,
    202            entry.info.width, entry.info.height);
    203 
    204     if (1 != fwrite(&entry, sizeof(entry), 1, ofp)) {
    205       error("Can't write entry to %s: %s\n", outfile, strerror(errno));
    206       goto bad1;
    207     }
    208     if (1 != fwrite(imgdata, imgsize, 1, ofp)) {
    209       error("Can't write image to %s: %s\n", outfile, strerror(errno));
    210       goto bad1;
    211     }
    212     if (diff && 1 != fwrite("\0\0\0\0\0\0\0\0", diff, 1, ofp)) {
    213       error("Can't write padding to %s: %s\n", outfile, strerror(errno));
    214       goto bad1;
    215     }
    216 
    217 
    218     discard_file(imgdata, imgsize);
    219   }
    220 
    221   fclose(ofp);
    222   return 0;
    223 
    224 bad1:
    225   fclose(ofp);
    226   error("Aborting\n");
    227   (void) unlink(outfile);
    228   exit(1);
    229 }
    230