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 
error(const char * fmt,...)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 */
HelpAndDie(void)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.
read_entire_file(const char * filename,size_t * 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().
discard_file(void * ptr,size_t length)111 static void discard_file(void *ptr, size_t length) {
112   munmap(ptr, length);
113 }
114 
115 //////////////////////////////////////////////////////////////////////////////
116 
117 
118 
main(int argc,char * argv[])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