blob: f4bde7c7f6a3514f68cbc35561d12ced2c3c76f0 [file] [log] [blame]
Bill Richardsond55085d2011-02-04 15:01:37 -08001// Copyright (c) 2010 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// Utility for manipulating firmware screen block (BMPBLOCK) in GBB.
6//
7
8#include "bmpblk_utility.h"
9
10#include <assert.h>
11#include <errno.h>
12#include <getopt.h>
Tom Wai-Hong Tamee2bc912011-02-17 12:58:58 +080013#include <lzma.h>
Bill Richardsond55085d2011-02-04 15:01:37 -080014#include <stdarg.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <yaml.h>
19
Bill Richardson61362d62011-02-14 10:28:03 -080020extern "C" {
21#include "eficompress.h"
22}
23
24
Bill Richardsonfc05bb82011-02-08 15:03:36 -080025/* BMP header, used to validate image requirements
26 * See http://en.wikipedia.org/wiki/BMP_file_format
27 */
28typedef struct {
29 uint8_t CharB; // must be 'B'
30 uint8_t CharM; // must be 'M'
31 uint32_t Size;
32 uint16_t Reserved[2];
33 uint32_t ImageOffset;
34 uint32_t HeaderSize;
35 uint32_t PixelWidth;
36 uint32_t PixelHeight;
37 uint16_t Planes; // Must be 1 for x86
38 uint16_t BitPerPixel; // 1, 4, 8, or 24 for x86
Tom Wai-Hong Tama985ed42011-02-14 16:08:49 +080039 uint32_t CompressionType; // 0 (none) for x86, 1 (RLE) for arm
Bill Richardsonfc05bb82011-02-08 15:03:36 -080040 uint32_t ImageSize;
41 uint32_t XPixelsPerMeter;
42 uint32_t YPixelsPerMeter;
43 uint32_t NumberOfColors;
44 uint32_t ImportantColors;
45} __attribute__((packed)) BMP_IMAGE_HEADER;
46
Bill Richardsond55085d2011-02-04 15:01:37 -080047
48static void error(const char *format, ...) {
49 va_list ap;
50 va_start(ap, format);
51 fprintf(stderr, "ERROR: ");
52 vfprintf(stderr, format, ap);
53 va_end(ap);
54 exit(1);
55}
56
57///////////////////////////////////////////////////////////////////////
58// BmpBlock Utility implementation
59
60namespace vboot_reference {
61
Bill Richardson54e95822011-05-05 15:12:10 -070062 BmpBlockUtil::BmpBlockUtil(bool debug) {
63 major_version_ = BMPBLOCK_MAJOR_VERSION;
64 minor_version_ = BMPBLOCK_MINOR_VERSION;
65 config_.config_filename.clear();
66 memset(&config_.header, '\0', BMPBLOCK_SIGNATURE_SIZE);
67 config_.images_map.clear();
68 config_.screens_map.clear();
69 config_.localizations.clear();
70 bmpblock_.clear();
71 set_compression_ = false;
72 compression_ = COMPRESS_NONE;
73 debug_ = debug;
Bill Richardsond55085d2011-02-04 15:01:37 -080074 }
75
Bill Richardson54e95822011-05-05 15:12:10 -070076 BmpBlockUtil::~BmpBlockUtil() {
Bill Richardsond55085d2011-02-04 15:01:37 -080077 }
Bill Richardsond55085d2011-02-04 15:01:37 -080078
Bill Richardson54e95822011-05-05 15:12:10 -070079 void BmpBlockUtil::force_compression(uint32_t compression) {
80 compression_ = compression;
81 set_compression_ = true;
82 }
Bill Richardsond55085d2011-02-04 15:01:37 -080083
Bill Richardson54e95822011-05-05 15:12:10 -070084 void BmpBlockUtil::load_from_config(const char *filename) {
85 load_yaml_config(filename);
86 fill_bmpblock_header();
87 load_all_image_files();
88 }
89
90 void BmpBlockUtil::load_yaml_config(const char *filename) {
91 yaml_parser_t parser;
92
93 config_.config_filename = filename;
94 config_.images_map.clear();
95 config_.screens_map.clear();
96 config_.localizations.clear();
Bill Richardson8ba3d792011-05-18 18:25:31 -070097 config_.locale_names.clear();
Bill Richardson54e95822011-05-05 15:12:10 -070098
99 FILE *fp = fopen(filename, "rb");
100 if (!fp) {
101 perror(filename);
102 exit(errno);
103 }
104
105 yaml_parser_initialize(&parser);
106 yaml_parser_set_input_file(&parser, fp);
107 parse_config(&parser);
108 yaml_parser_delete(&parser);
109 fclose(fp);
110
111
Bill Richardson2e022632011-08-22 14:54:14 -0700112 // TODO: Check the yaml file for self-consistency. Warn on any problems.
Bill Richardson54e95822011-05-05 15:12:10 -0700113 // All images should be used somewhere in the screens.
114 // All images referenced in the screens should be defined.
115 // All screens should be used somewhere in the localizations.
116 // All screens referenced in the localizations should be defined.
Bill Richardson8ba3d792011-05-18 18:25:31 -0700117 // The number of localizations should match the number of locale_index
Bill Richardson54e95822011-05-05 15:12:10 -0700118
119 if (debug_) {
120 printf("%ld image_names\n", config_.image_names.size());
121 for (unsigned int i = 0; i < config_.image_names.size(); ++i) {
122 printf(" %d: \"%s\"\n", i, config_.image_names[i].c_str());
123 }
124 printf("%ld images_map\n", config_.images_map.size());
125 for (StrImageConfigMap::iterator it = config_.images_map.begin();
126 it != config_.images_map.end();
127 ++it) {
128 printf(" \"%s\": filename=\"%s\" offset=0x%x tag=%d\n",
129 it->first.c_str(),
130 it->second.filename.c_str(),
131 it->second.offset,
132 it->second.data.tag);
133 }
134 printf("%ld screens_map\n", config_.screens_map.size());
135 for (StrScreenConfigMap::iterator it = config_.screens_map.begin();
136 it != config_.screens_map.end();
137 ++it) {
138 printf(" \"%s\":\n", it->first.c_str());
139 for (int k=0; k<MAX_IMAGE_IN_LAYOUT; k++) {
140 printf(" %d: \"%s\" (%d,%d) ofs=0x%x\n",
141 k,
142 it->second.image_names[k].c_str(),
143 it->second.data.images[k].x,
144 it->second.data.images[k].y,
145 it->second.data.images[k].image_info_offset);
146 }
147 }
148 }
149 }
150
151 void BmpBlockUtil::expect_event(yaml_parser_t *parser,
152 const yaml_event_type_e type) {
153 yaml_event_t event;
Bill Richardsond55085d2011-02-04 15:01:37 -0800154 yaml_parser_parse(parser, &event);
Bill Richardson54e95822011-05-05 15:12:10 -0700155 if (event.type != type) {
156 error("Syntax error.\n");
157 }
158 yaml_event_delete(&event);
159 }
160
161 void BmpBlockUtil::parse_config(yaml_parser_t *parser) {
162 expect_event(parser, YAML_STREAM_START_EVENT);
163 expect_event(parser, YAML_DOCUMENT_START_EVENT);
164 parse_first_layer(parser);
165 expect_event(parser, YAML_DOCUMENT_END_EVENT);
166 expect_event(parser, YAML_STREAM_END_EVENT);
167 }
168
169 void BmpBlockUtil::parse_first_layer(yaml_parser_t *parser) {
170 yaml_event_t event;
171 string keyword;
172 expect_event(parser, YAML_MAPPING_START_EVENT);
173 for (;;) {
174 yaml_parser_parse(parser, &event);
175 switch (event.type) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800176 case YAML_SCALAR_EVENT:
177 keyword = (char*)event.data.scalar.value;
178 if (keyword == "bmpblock") {
179 parse_bmpblock(parser);
Bill Richardsona7209ee2011-02-17 14:30:14 -0800180 } else if (keyword == "compression") {
181 parse_compression(parser);
Bill Richardsond55085d2011-02-04 15:01:37 -0800182 } else if (keyword == "images") {
183 parse_images(parser);
184 } else if (keyword == "screens") {
185 parse_screens(parser);
186 } else if (keyword == "localizations") {
187 parse_localizations(parser);
Bill Richardson8ba3d792011-05-18 18:25:31 -0700188 } else if (keyword == "locale_index") {
189 parse_locale_index(parser);
Bill Richardsond55085d2011-02-04 15:01:37 -0800190 }
191 break;
192 case YAML_MAPPING_END_EVENT:
193 yaml_event_delete(&event);
194 return;
195 default:
196 error("Syntax error in parsing config file.\n");
Bill Richardson54e95822011-05-05 15:12:10 -0700197 }
198 yaml_event_delete(&event);
199 }
200 }
201
202 void BmpBlockUtil::parse_bmpblock(yaml_parser_t *parser) {
203 yaml_event_t event;
204 yaml_parser_parse(parser, &event);
205 if (event.type != YAML_SCALAR_EVENT) {
206 error("Syntax error in parsing bmpblock.\n");
207 }
208 string gotversion = (char*)event.data.scalar.value;
209 if (gotversion == "1.1") {
210 render_hwid_ = true;
211 } else if (gotversion == "1.0") {
212 minor_version_ = 0;
213 render_hwid_ = false;
214 } else {
215 error("Unsupported version specified in config file (%s)\n",
216 gotversion.c_str());
Bill Richardsond55085d2011-02-04 15:01:37 -0800217 }
218 yaml_event_delete(&event);
219 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800220
Bill Richardson54e95822011-05-05 15:12:10 -0700221 void BmpBlockUtil::parse_compression(yaml_parser_t *parser) {
222 yaml_event_t event;
Bill Richardsond55085d2011-02-04 15:01:37 -0800223 yaml_parser_parse(parser, &event);
Bill Richardson54e95822011-05-05 15:12:10 -0700224 if (event.type != YAML_SCALAR_EVENT) {
225 error("Syntax error in parsing bmpblock.\n");
226 }
227 char *comp_str = (char *)event.data.scalar.value;
228 char *e = 0;
229 uint32_t comp = (uint32_t)strtoul(comp_str, &e, 0);
230 if (!*comp_str || (e && *e) || comp >= MAX_COMPRESS) {
231 error("Invalid compression specified in config file (%d)\n", comp);
232 }
233 if (!set_compression_) {
234 compression_ = comp;
235 }
236 yaml_event_delete(&event);
237 }
238
239 void BmpBlockUtil::parse_images(yaml_parser_t *parser) {
240 yaml_event_t event;
241 string image_name, image_filename;
242 expect_event(parser, YAML_MAPPING_START_EVENT);
243 for (;;) {
244 yaml_parser_parse(parser, &event);
245 switch (event.type) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800246 case YAML_SCALAR_EVENT:
247 image_name = (char*)event.data.scalar.value;
248 yaml_event_delete(&event);
249 yaml_parser_parse(parser, &event);
250 if (event.type != YAML_SCALAR_EVENT) {
251 error("Syntax error in parsing images.\n");
252 }
253 image_filename = (char*)event.data.scalar.value;
Bill Richardsonf82f9412011-02-17 08:56:33 -0800254 config_.image_names.push_back(image_name);
Bill Richardsond55085d2011-02-04 15:01:37 -0800255 config_.images_map[image_name] = ImageConfig();
256 config_.images_map[image_name].filename = image_filename;
257 break;
258 case YAML_MAPPING_END_EVENT:
259 yaml_event_delete(&event);
260 return;
261 default:
262 error("Syntax error in parsing images.\n");
Bill Richardson54e95822011-05-05 15:12:10 -0700263 }
264 yaml_event_delete(&event);
Bill Richardsond55085d2011-02-04 15:01:37 -0800265 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800266 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800267
Bill Richardson54e95822011-05-05 15:12:10 -0700268 void BmpBlockUtil::parse_layout(yaml_parser_t *parser, ScreenConfig &screen) {
269 yaml_event_t event;
270 int depth = 0, index1 = 0, index2 = 0;
271 expect_event(parser, YAML_SEQUENCE_START_EVENT);
272 for (;;) {
273 yaml_parser_parse(parser, &event);
274 switch (event.type) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800275 case YAML_SEQUENCE_START_EVENT:
276 depth++;
277 break;
278 case YAML_SCALAR_EVENT:
279 switch (index2) {
Bill Richardson54e95822011-05-05 15:12:10 -0700280 case 0:
281 screen.data.images[index1].x = atoi((char*)event.data.scalar.value);
282 break;
283 case 1:
284 screen.data.images[index1].y = atoi((char*)event.data.scalar.value);
285 break;
286 case 2:
287 screen.image_names[index1] = (char*)event.data.scalar.value;
288 // Detect the special case where we're rendering the HWID string
289 // instead of displaying a bitmap. The image name shouldn't
290 // exist in the list of images, but we will still need an
291 // ImageInfo struct to remember where to draw the text.
292 // Note that if the image name DOES exist, we still will won't
293 // display it (yet). Future versions may use that image to hold the
294 // font glpyhs, which is why we pass it around now.
295 if (render_hwid_) {
296 if (screen.image_names[index1] == RENDER_HWID) {
297 config_.images_map[RENDER_HWID].data.tag = TAG_HWID;
298 } else if (screen.image_names[index1] == RENDER_HWID_RTOL) {
299 config_.images_map[RENDER_HWID_RTOL].data.tag = TAG_HWID_RTOL;
300 }
301 }
302 break;
303 default:
304 error("Syntax error in parsing layout\n");
Bill Richardsond55085d2011-02-04 15:01:37 -0800305 }
306 index2++;
307 break;
308 case YAML_SEQUENCE_END_EVENT:
309 if (depth == 1) {
310 index1++;
311 index2 = 0;
312 } else if (depth == 0) {
313 yaml_event_delete(&event);
314 return;
315 }
316 depth--;
317 break;
318 default:
319 error("Syntax error in paring layout.\n");
Bill Richardson54e95822011-05-05 15:12:10 -0700320 }
321 yaml_event_delete(&event);
Bill Richardsond55085d2011-02-04 15:01:37 -0800322 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800323 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800324
Bill Richardson54e95822011-05-05 15:12:10 -0700325 void BmpBlockUtil::parse_screens(yaml_parser_t *parser) {
326 yaml_event_t event;
327 string screen_name;
328 expect_event(parser, YAML_MAPPING_START_EVENT);
329 for (;;) {
330 yaml_parser_parse(parser, &event);
331 switch (event.type) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800332 case YAML_SCALAR_EVENT:
333 screen_name = (char*)event.data.scalar.value;
334 config_.screens_map[screen_name] = ScreenConfig();
335 parse_layout(parser, config_.screens_map[screen_name]);
336 break;
337 case YAML_MAPPING_END_EVENT:
338 yaml_event_delete(&event);
339 return;
340 default:
341 error("Syntax error in parsing screens.\n");
Bill Richardson54e95822011-05-05 15:12:10 -0700342 }
343 yaml_event_delete(&event);
Bill Richardsond55085d2011-02-04 15:01:37 -0800344 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800345 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800346
Bill Richardson54e95822011-05-05 15:12:10 -0700347 void BmpBlockUtil::parse_localizations(yaml_parser_t *parser) {
348 yaml_event_t event;
349 int depth = 0, index = 0;
350 expect_event(parser, YAML_SEQUENCE_START_EVENT);
351 for (;;) {
352 yaml_parser_parse(parser, &event);
353 switch (event.type) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800354 case YAML_SEQUENCE_START_EVENT:
355 config_.localizations.push_back(vector<string>());
356 depth++;
357 break;
358 case YAML_SCALAR_EVENT:
359 config_.localizations[index].push_back((char*)event.data.scalar.value);
360 break;
361 case YAML_SEQUENCE_END_EVENT:
362 if (depth == 1) {
363 index++;
364 } else if (depth == 0) {
365 yaml_event_delete(&event);
366 return;
367 }
368 depth--;
369 break;
370 default:
371 error("Syntax error in parsing localizations.\n");
Bill Richardson54e95822011-05-05 15:12:10 -0700372 }
373 yaml_event_delete(&event);
Bill Richardsond55085d2011-02-04 15:01:37 -0800374 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800375 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800376
Bill Richardson8ba3d792011-05-18 18:25:31 -0700377 void BmpBlockUtil::parse_locale_index(yaml_parser_t *parser) {
378 yaml_event_t event;
379 expect_event(parser, YAML_SEQUENCE_START_EVENT);
380 for (;;) {
381 yaml_parser_parse(parser, &event);
382 switch (event.type) {
383 case YAML_SCALAR_EVENT:
384 config_.locale_names.append((char*)event.data.scalar.value);
385 config_.locale_names.append(1, (char)'\0'); // '\0' to delimit
386 break;
387 case YAML_SEQUENCE_END_EVENT:
388 yaml_event_delete(&event);
389 config_.locale_names.append(1, (char)'\0'); // double '\0' to terminate
390 return;
391 default:
392 error("Syntax error in parsing localizations.\n");
393 }
394 }
395 }
396
Bill Richardson54e95822011-05-05 15:12:10 -0700397 void BmpBlockUtil::load_all_image_files() {
398 for (unsigned int i = 0; i < config_.image_names.size(); i++) {
399 StrImageConfigMap::iterator it =
400 config_.images_map.find(config_.image_names[i]);
401 if (debug_) {
402 printf("loading image \"%s\" from \"%s\"\n",
403 config_.image_names[i].c_str(),
404 it->second.filename.c_str());
405 }
406 const string &content = read_image_file(it->second.filename.c_str());
407 it->second.raw_content = content;
408 it->second.data.original_size = content.size();
409 it->second.data.format = get_image_format(content);
410 switch (it->second.data.format) {
411 case FORMAT_BMP:
412 it->second.data.width = get_bmp_image_width(it->second.raw_content);
413 it->second.data.height = get_bmp_image_height(it->second.raw_content);
414 break;
415 default:
416 error("Unsupported image format in %s\n", it->second.filename.c_str());
417 }
418 switch(compression_) {
419 case COMPRESS_NONE:
420 it->second.data.compression = compression_;
421 it->second.compressed_content = content;
422 it->second.data.compressed_size = content.size();
423 break;
424 case COMPRESS_EFIv1:
Bill Richardson61362d62011-02-14 10:28:03 -0800425 {
426 // The content will always compress smaller (so sez the docs).
427 uint32_t tmpsize = content.size();
428 uint8_t *tmpbuf = (uint8_t *)malloc(tmpsize);
429 // The size of the compressed content is also returned.
430 if (EFI_SUCCESS != EfiCompress((uint8_t *)content.c_str(), tmpsize,
431 tmpbuf, &tmpsize)) {
432 error("Unable to compress!\n");
433 }
434 it->second.data.compression = compression_;
435 it->second.compressed_content.assign((const char *)tmpbuf, tmpsize);
436 it->second.data.compressed_size = tmpsize;
437 free(tmpbuf);
438 }
439 break;
Bill Richardson54e95822011-05-05 15:12:10 -0700440 case COMPRESS_LZMA1:
Tom Wai-Hong Tamee2bc912011-02-17 12:58:58 +0800441 {
442 // Calculate the worst case of buffer size.
443 uint32_t tmpsize = lzma_stream_buffer_bound(content.size());
444 uint8_t *tmpbuf = (uint8_t *)malloc(tmpsize);
445 lzma_stream stream = LZMA_STREAM_INIT;
446 lzma_options_lzma options;
447 lzma_ret result;
448
449 lzma_lzma_preset(&options, 9);
450 result = lzma_alone_encoder(&stream, &options);
451 if (result != LZMA_OK) {
452 error("Unable to initialize easy encoder (error: %d)!\n", result);
453 }
454
455 stream.next_in = (uint8_t *)content.data();
456 stream.avail_in = content.size();
457 stream.next_out = tmpbuf;
458 stream.avail_out = tmpsize;
459 result = lzma_code(&stream, LZMA_FINISH);
460 if (result != LZMA_STREAM_END) {
461 error("Unable to encode data (error: %d)!\n", result);
462 }
463
464 it->second.data.compression = compression_;
465 it->second.compressed_content.assign((const char *)tmpbuf,
466 tmpsize - stream.avail_out);
467 it->second.data.compressed_size = tmpsize - stream.avail_out;
468 lzma_end(&stream);
469 free(tmpbuf);
470 }
471 break;
Bill Richardsond55085d2011-02-04 15:01:37 -0800472 default:
Bill Richardson54e95822011-05-05 15:12:10 -0700473 error("Unsupported compression method attempted.\n");
Bill Richardsond55085d2011-02-04 15:01:37 -0800474 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800475 }
476 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800477
Bill Richardson54e95822011-05-05 15:12:10 -0700478 const string BmpBlockUtil::read_image_file(const char *filename) {
479 string content;
480 vector<char> buffer;
Bill Richardsond55085d2011-02-04 15:01:37 -0800481
Bill Richardson54e95822011-05-05 15:12:10 -0700482 FILE *fp = fopen(filename, "rb");
483 if (!fp) {
484 perror(filename);
485 exit(errno);
486 }
487
488 if (fseek(fp, 0, SEEK_END) == 0) {
489 buffer.resize(ftell(fp));
490 rewind(fp);
491 }
492
493 if (!buffer.empty()) {
494 if(fread(&buffer[0], buffer.size(), 1, fp) != 1) {
495 perror(filename);
496 buffer.clear();
497 } else {
498 content.assign(buffer.begin(), buffer.end());
499 }
500 }
501
502 fclose(fp);
503 return content;
504 }
505
506 ImageFormat BmpBlockUtil::get_image_format(const string content) {
507 if (content.size() < sizeof(BMP_IMAGE_HEADER))
508 return FORMAT_INVALID;
509 const BMP_IMAGE_HEADER *hdr = (const BMP_IMAGE_HEADER *)content.c_str();
510
511 if (hdr->CharB != 'B' || hdr->CharM != 'M' ||
512 hdr->Planes != 1 ||
513 (hdr->CompressionType != 0 && hdr->CompressionType != 1) ||
514 (hdr->BitPerPixel != 1 && hdr->BitPerPixel != 4 &&
515 hdr->BitPerPixel != 8 && hdr->BitPerPixel != 24))
516 return FORMAT_INVALID;
517
518 return FORMAT_BMP;
519 }
520
521 uint32_t BmpBlockUtil::get_bmp_image_width(const string content) {
522 const BMP_IMAGE_HEADER *hdr = (const BMP_IMAGE_HEADER *)content.c_str();
523 return hdr->PixelWidth;
524 }
525
526 uint32_t BmpBlockUtil::get_bmp_image_height(const string content) {
527 const BMP_IMAGE_HEADER *hdr = (const BMP_IMAGE_HEADER *)content.c_str();
528 return hdr->PixelHeight;
529 }
530
531 void BmpBlockUtil::fill_bmpblock_header() {
532 memset(&config_.header, '\0', sizeof(config_.header));
533 memcpy(&config_.header.signature, BMPBLOCK_SIGNATURE,
534 BMPBLOCK_SIGNATURE_SIZE);
535 config_.header.major_version = major_version_;
536 config_.header.minor_version = minor_version_;
537 config_.header.number_of_localizations = config_.localizations.size();
538 config_.header.number_of_screenlayouts = config_.localizations[0].size();
539 // HEY: this is part of the yaml consistency check
540 for (unsigned int i = 1; i < config_.localizations.size(); ++i) {
541 assert(config_.header.number_of_screenlayouts ==
542 config_.localizations[i].size());
543 }
544 config_.header.number_of_imageinfos = config_.images_map.size();
Bill Richardson8ba3d792011-05-18 18:25:31 -0700545 config_.header.locale_string_offset = 0; // Filled by pack_bmpblock()
Bill Richardson54e95822011-05-05 15:12:10 -0700546 }
547
548 void BmpBlockUtil::pack_bmpblock() {
549 bmpblock_.clear();
550
551 /* Compute the ImageInfo offsets from start of BMPBLOCK. */
552 uint32_t current_offset = sizeof(BmpBlockHeader) +
553 sizeof(ScreenLayout) * (config_.header.number_of_localizations *
554 config_.header.number_of_screenlayouts);
555 for (StrImageConfigMap::iterator it = config_.images_map.begin();
556 it != config_.images_map.end();
557 ++it) {
558 it->second.offset = current_offset;
559 if (debug_)
560 printf(" \"%s\": filename=\"%s\" offset=0x%x tag=%d\n",
561 it->first.c_str(),
562 it->second.filename.c_str(),
563 it->second.offset,
564 it->second.data.tag);
565 current_offset += sizeof(ImageInfo) +
566 it->second.data.compressed_size;
567 /* Make it 4-byte aligned. */
568 if ((current_offset & 3) > 0) {
569 current_offset = (current_offset & ~3) + 4;
570 }
571 }
Bill Richardson8ba3d792011-05-18 18:25:31 -0700572 /* And leave room for the locale_index string */
573 if (config_.locale_names.size()) {
574 config_.header.locale_string_offset = current_offset;
575 current_offset += config_.locale_names.size();
576 }
577
Bill Richardson54e95822011-05-05 15:12:10 -0700578 bmpblock_.resize(current_offset);
579
580 /* Fill BmpBlockHeader struct. */
581 string::iterator current_filled = bmpblock_.begin();
582 std::copy(reinterpret_cast<char*>(&config_.header),
583 reinterpret_cast<char*>(&config_.header + 1),
584 current_filled);
585 current_filled += sizeof(config_.header);
586 current_offset = sizeof(config_.header);
587
588 /* Fill all ScreenLayout structs. */
589 for (unsigned int i = 0; i < config_.localizations.size(); ++i) {
590 for (unsigned int j = 0; j < config_.localizations[i].size(); ++j) {
591 ScreenConfig &screen = config_.screens_map[config_.localizations[i][j]];
592 for (unsigned int k = 0;
593 k < MAX_IMAGE_IN_LAYOUT && !screen.image_names[k].empty();
594 ++k) {
595 if (config_.images_map.find(screen.image_names[k]) ==
596 config_.images_map.end()) {
597 error("Invalid image name \"%s\"\n", screen.image_names[k].c_str());
598 }
599 if (debug_)
600 printf("i=%d j=%d k=%d=\"%s\" (%d,%d) ofs=%x\n", i,j,k,
601 screen.image_names[k].c_str(),
602 screen.data.images[k].x, screen.data.images[k].y,
603 config_.images_map[screen.image_names[k]].offset
604 );
605 screen.data.images[k].image_info_offset =
Bill Richardsond55085d2011-02-04 15:01:37 -0800606 config_.images_map[screen.image_names[k]].offset;
Bill Richardson54e95822011-05-05 15:12:10 -0700607 }
608 std::copy(reinterpret_cast<char*>(&screen.data),
609 reinterpret_cast<char*>(&screen.data + 1),
610 current_filled);
611 current_filled += sizeof(screen.data);
612 if (debug_)
613 printf("S: current offset is 0x%08x\n", current_offset);
614 current_offset += sizeof(screen.data);
Bill Richardsond55085d2011-02-04 15:01:37 -0800615 }
Bill Richardson54e95822011-05-05 15:12:10 -0700616 }
617
618 /* Fill all ImageInfo structs and image contents. */
619 for (StrImageConfigMap::iterator it = config_.images_map.begin();
620 it != config_.images_map.end();
621 ++it) {
622 current_filled = bmpblock_.begin() + it->second.offset;
623 current_offset = it->second.offset;
624 if (debug_)
625 printf("I0: current offset is 0x%08x\n", current_offset);
626 std::copy(reinterpret_cast<char*>(&it->second.data),
627 reinterpret_cast<char*>(&it->second.data + 1),
Bill Richardsond55085d2011-02-04 15:01:37 -0800628 current_filled);
Bill Richardson54e95822011-05-05 15:12:10 -0700629 current_filled += sizeof(it->second.data);
630 current_offset += sizeof(it->second.data);
631 if (debug_)
632 printf("I1: current offset is 0x%08x (len %ld)\n",
633 current_offset, it->second.compressed_content.length());
634 std::copy(it->second.compressed_content.begin(),
635 it->second.compressed_content.end(),
636 current_filled);
Bill Richardsond55085d2011-02-04 15:01:37 -0800637 }
Bill Richardson8ba3d792011-05-18 18:25:31 -0700638
639 /* Fill in locale_names. */
640 if (config_.header.locale_string_offset) {
641 current_offset = config_.header.locale_string_offset;
642 current_filled = bmpblock_.begin() + current_offset;
643 if (debug_)
644 printf("locale_names: offset 0x%08x (len %ld)\n",
645 current_offset, config_.locale_names.size());
646 std::copy(config_.locale_names.begin(),
647 config_.locale_names.end(),
648 current_filled);
649 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800650 }
651
Bill Richardson54e95822011-05-05 15:12:10 -0700652 void BmpBlockUtil::write_to_bmpblock(const char *filename) {
653 assert(!bmpblock_.empty());
Bill Richardsond55085d2011-02-04 15:01:37 -0800654
Bill Richardson54e95822011-05-05 15:12:10 -0700655 FILE *fp = fopen(filename, "wb");
656 if (!fp) {
657 perror(filename);
658 exit(errno);
659 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800660
Bill Richardson54e95822011-05-05 15:12:10 -0700661 int r = fwrite(bmpblock_.c_str(), bmpblock_.size(), 1, fp);
662 fclose(fp);
663 if (r != 1) {
664 perror(filename);
665 exit(errno);
666 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800667 }
668
Bill Richardsond55085d2011-02-04 15:01:37 -0800669} // namespace vboot_reference
670
671#ifdef WITH_UTIL_MAIN
672
Bill Richardson54e95822011-05-05 15:12:10 -0700673 //////////////////////////////////////////////////////////////////////////////
674 // Command line utilities.
Bill Richardson794d4d42011-02-10 19:13:10 -0800675
Bill Richardson54e95822011-05-05 15:12:10 -0700676 extern "C" {
Bill Richardson794d4d42011-02-10 19:13:10 -0800677#include "bmpblk_util.h"
Bill Richardson54e95822011-05-05 15:12:10 -0700678 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800679
Bill Richardson54e95822011-05-05 15:12:10 -0700680 using vboot_reference::BmpBlockUtil;
Bill Richardsond55085d2011-02-04 15:01:37 -0800681
Bill Richardson54e95822011-05-05 15:12:10 -0700682 // utility function: provide usage of this utility and exit.
683 static void usagehelp_exit(const char *prog_name) {
684 printf(
685 "\n"
686 "To create a new BMPBLOCK file using config from YAML file:\n"
687 "\n"
688 " %s [-z NUM] -c YAML BMPBLOCK\n"
689 "\n"
690 " -z NUM = compression algorithm to use\n"
691 " 0 = none\n"
692 " 1 = EFIv1\n"
693 " 2 = LZMA1\n"
694 "\n", prog_name);
695 printf(
696 "To display the contents of a BMPBLOCK:\n"
697 "\n"
698 " %s [-y] BMPBLOCK\n"
699 "\n"
700 " -y = display as yaml\n"
701 "\n", prog_name);
702 printf(
703 "To unpack a BMPBLOCK file:\n"
704 "\n"
705 " %s -x [-d DIR] [-f] BMPBLOCK\n"
706 "\n"
707 " -d DIR = directory to use (default '.')\n"
708 " -f = force overwriting existing files\n"
709 "\n", prog_name);
710 exit(1);
711 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800712
Bill Richardson54e95822011-05-05 15:12:10 -0700713 ///////////////////////////////////////////////////////////////////////
714 // main
Bill Richardsond55085d2011-02-04 15:01:37 -0800715
Bill Richardson54e95822011-05-05 15:12:10 -0700716 int main(int argc, char *argv[]) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800717
Bill Richardson54e95822011-05-05 15:12:10 -0700718 const char *prog_name = strrchr(argv[0], '/');
719 if (prog_name)
720 prog_name++;
721 else
722 prog_name = argv[0];
Bill Richardsond55085d2011-02-04 15:01:37 -0800723
Bill Richardson54e95822011-05-05 15:12:10 -0700724 int overwrite = 0, extract_mode = 0;
725 int compression = 0;
726 int set_compression = 0;
727 const char *config_fn = 0, *bmpblock_fn = 0, *extract_dir = ".";
728 int show_as_yaml = 0;
729 bool debug = false;
Bill Richardsond55085d2011-02-04 15:01:37 -0800730
Bill Richardson54e95822011-05-05 15:12:10 -0700731 int opt;
732 opterr = 0; // quiet
733 int errorcnt = 0;
734 char *e = 0;
735 while ((opt = getopt(argc, argv, ":c:xz:fd:yD")) != -1) {
736 switch (opt) {
737 case 'c':
738 config_fn = optarg;
739 break;
740 case 'x':
741 extract_mode = 1;
742 break;
743 case 'y':
744 show_as_yaml = 1;
745 break;
746 case 'z':
747 compression = (int)strtoul(optarg, &e, 0);
748 if (!*optarg || (e && *e)) {
749 fprintf(stderr, "%s: invalid argument to -%c: \"%s\"\n",
750 prog_name, opt, optarg);
751 errorcnt++;
752 }
753 if (compression >= MAX_COMPRESS) {
754 fprintf(stderr, "%s: compression type must be less than %d\n",
755 prog_name, MAX_COMPRESS);
756 errorcnt++;
757 }
758 set_compression = 1;
759 break;
760 case 'f':
761 overwrite = 1;
762 break;
763 case 'd':
764 extract_dir= optarg;
765 break;
766 case 'D':
767 debug = true;
768 break;
769 case ':':
770 fprintf(stderr, "%s: missing argument to -%c\n",
771 prog_name, optopt);
Bill Richardson794d4d42011-02-10 19:13:10 -0800772 errorcnt++;
Bill Richardson54e95822011-05-05 15:12:10 -0700773 break;
774 default:
775 fprintf(stderr, "%s: unrecognized switch: -%c\n",
776 prog_name, optopt);
Bill Richardson794d4d42011-02-10 19:13:10 -0800777 errorcnt++;
Bill Richardson54e95822011-05-05 15:12:10 -0700778 break;
Bill Richardson794d4d42011-02-10 19:13:10 -0800779 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800780 }
Bill Richardson54e95822011-05-05 15:12:10 -0700781 argc -= optind;
782 argv += optind;
783
784 if (argc >= 1) {
785 bmpblock_fn = argv[0];
786 } else {
787 fprintf(stderr, "%s: missing BMPBLOCK name\n", prog_name);
788 errorcnt++;
789 }
790
791 if (errorcnt)
792 usagehelp_exit(prog_name);
793
794 BmpBlockUtil util(debug);
795
796 if (config_fn) {
797 if (set_compression)
798 util.force_compression(compression);
799 util.load_from_config(config_fn);
800 util.pack_bmpblock();
801 util.write_to_bmpblock(bmpblock_fn);
802 }
803
804 else if (extract_mode) {
805 return dump_bmpblock(bmpblock_fn, 1, extract_dir, overwrite);
806 } else {
807 return dump_bmpblock(bmpblock_fn, show_as_yaml, 0, 0);
808 }
809
810 return 0;
Bill Richardsond55085d2011-02-04 15:01:37 -0800811 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800812
813#endif // WITH_UTIL_MAIN