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
23static char *progname;
24
25static 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 */
37enum {
38  OPT_OUTFILE = 1000,
39};
40
41#define DEFAULT_OUTFILE "font.bin"
42
43
44static struct option long_opts[] = {
45  {"outfile", 1, 0,                   OPT_OUTFILE             },
46  {NULL, 0, 0, 0}
47};
48
49
50/* Print help and return error */
51static 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.
72static 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().
111static void discard_file(void *ptr, size_t length) {
112  munmap(ptr, length);
113}
114
115//////////////////////////////////////////////////////////////////////////////
116
117
118
119int 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
224bad1:
225  fclose(ofp);
226  error("Aborting\n");
227  (void) unlink(outfile);
228  exit(1);
229}
230