Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // 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> | |
| 13 #include <stdarg.h> | |
| 14 #include <stdio.h> | |
| 15 #include <stdlib.h> | |
| 16 #include <string.h> | |
| 17 #include <yaml.h> | |
| 18 | |
| 19 static void error(const char *format, ...) { | |
| 20 va_list ap; | |
| 21 va_start(ap, format); | |
| 22 fprintf(stderr, "ERROR: "); | |
| 23 vfprintf(stderr, format, ap); | |
| 24 va_end(ap); | |
| 25 exit(1); | |
| 26 } | |
| 27 | |
| 28 /////////////////////////////////////////////////////////////////////// | |
| 29 // BmpBlock Utility implementation | |
| 30 | |
| 31 namespace vboot_reference { | |
| 32 | |
| 33 BmpBlockUtil::BmpBlockUtil() { | |
| 34 initialize(); | |
| 35 } | |
| 36 | |
| 37 BmpBlockUtil::~BmpBlockUtil() { | |
| 38 } | |
| 39 | |
| 40 void BmpBlockUtil::initialize() { | |
| 41 config_.config_filename.clear(); | |
| 42 memset(&config_.header, '\0', BMPBLOCK_SIGNATURE_SIZE); | |
| 43 config_.images_map.clear(); | |
| 44 config_.screens_map.clear(); | |
| 45 config_.localizations.clear(); | |
| 46 bmpblock_.clear(); | |
| 47 } | |
| 48 | |
| 49 void BmpBlockUtil::load_from_config(const char *filename) { | |
| 50 load_yaml_config(filename); | |
| 51 fill_bmpblock_header(); | |
| 52 load_all_image_files(); | |
| 53 fill_all_image_infos(); | |
| 54 } | |
| 55 | |
| 56 void BmpBlockUtil::load_yaml_config(const char *filename) { | |
| 57 yaml_parser_t parser; | |
| 58 | |
| 59 config_.config_filename = filename; | |
| 60 config_.images_map.clear(); | |
| 61 config_.screens_map.clear(); | |
| 62 config_.localizations.clear(); | |
| 63 | |
| 64 FILE *fp = fopen(filename, "rb"); | |
| 65 if (!fp) { | |
| 66 perror(filename); | |
| 67 exit(errno); | |
| 68 } | |
| 69 | |
| 70 yaml_parser_initialize(&parser); | |
| 71 yaml_parser_set_input_file(&parser, fp); | |
| 72 parse_config(&parser); | |
| 73 yaml_parser_delete(&parser); | |
| 74 fclose(fp); | |
| 75 } | |
| 76 | |
| 77 void BmpBlockUtil::expect_event(yaml_parser_t *parser, | |
| 78 const yaml_event_type_e type) { | |
| 79 yaml_event_t event; | |
| 80 yaml_parser_parse(parser, &event); | |
| 81 if (event.type != type) { | |
| 82 error("Syntax error.\n"); | |
| 83 } | |
| 84 yaml_event_delete(&event); | |
| 85 } | |
| 86 | |
| 87 void BmpBlockUtil::parse_config(yaml_parser_t *parser) { | |
| 88 expect_event(parser, YAML_STREAM_START_EVENT); | |
| 89 expect_event(parser, YAML_DOCUMENT_START_EVENT); | |
| 90 parse_first_layer(parser); | |
| 91 expect_event(parser, YAML_DOCUMENT_END_EVENT); | |
| 92 expect_event(parser, YAML_STREAM_END_EVENT); | |
| 93 } | |
| 94 | |
| 95 void BmpBlockUtil::parse_first_layer(yaml_parser_t *parser) { | |
| 96 yaml_event_t event; | |
| 97 string keyword; | |
| 98 expect_event(parser, YAML_MAPPING_START_EVENT); | |
| 99 for (;;) { | |
| 100 yaml_parser_parse(parser, &event); | |
| 101 switch (event.type) { | |
| 102 case YAML_SCALAR_EVENT: | |
| 103 keyword = (char*)event.data.scalar.value; | |
| 104 if (keyword == "bmpblock") { | |
| 105 parse_bmpblock(parser); | |
| 106 } else if (keyword == "images") { | |
| 107 parse_images(parser); | |
| 108 } else if (keyword == "screens") { | |
| 109 parse_screens(parser); | |
| 110 } else if (keyword == "localizations") { | |
| 111 parse_localizations(parser); | |
| 112 } | |
| 113 break; | |
| 114 case YAML_MAPPING_END_EVENT: | |
| 115 yaml_event_delete(&event); | |
| 116 return; | |
| 117 default: | |
| 118 error("Syntax error in parsing config file.\n"); | |
| 119 } | |
| 120 yaml_event_delete(&event); | |
| 121 } | |
| 122 } | |
| 123 | |
| 124 void BmpBlockUtil::parse_bmpblock(yaml_parser_t *parser) { | |
| 125 yaml_event_t event; | |
| 126 yaml_parser_parse(parser, &event); | |
| 127 if (event.type != YAML_SCALAR_EVENT) { | |
| 128 error("Syntax error in parsing bmpblock.\n"); | |
| 129 } | |
| 130 config_.header.major_version = atoi((char*)event.data.scalar.value); | |
| 131 config_.header.minor_version = atoi( | |
| 132 strchr((char*)event.data.scalar.value, '.') + 1); | |
| 133 yaml_event_delete(&event); | |
| 134 } | |
| 135 | |
| 136 void BmpBlockUtil::parse_images(yaml_parser_t *parser) { | |
| 137 yaml_event_t event; | |
| 138 string image_name, image_filename; | |
| 139 expect_event(parser, YAML_MAPPING_START_EVENT); | |
| 140 for (;;) { | |
| 141 yaml_parser_parse(parser, &event); | |
| 142 switch (event.type) { | |
| 143 case YAML_SCALAR_EVENT: | |
| 144 image_name = (char*)event.data.scalar.value; | |
| 145 yaml_event_delete(&event); | |
| 146 yaml_parser_parse(parser, &event); | |
| 147 if (event.type != YAML_SCALAR_EVENT) { | |
| 148 error("Syntax error in parsing images.\n"); | |
| 149 } | |
| 150 image_filename = (char*)event.data.scalar.value; | |
| 151 config_.images_map[image_name] = ImageConfig(); | |
| 152 config_.images_map[image_name].filename = image_filename; | |
| 153 break; | |
| 154 case YAML_MAPPING_END_EVENT: | |
| 155 yaml_event_delete(&event); | |
| 156 return; | |
| 157 default: | |
| 158 error("Syntax error in parsing images.\n"); | |
| 159 } | |
| 160 yaml_event_delete(&event); | |
| 161 } | |
| 162 } | |
| 163 | |
| 164 void BmpBlockUtil::parse_layout(yaml_parser_t *parser, ScreenConfig &screen) { | |
| 165 yaml_event_t event; | |
| 166 string screen_name; | |
| 167 int depth = 0, index1 = 0, index2 = 0; | |
| 168 expect_event(parser, YAML_SEQUENCE_START_EVENT); | |
| 169 for (;;) { | |
| 170 yaml_parser_parse(parser, &event); | |
| 171 switch (event.type) { | |
| 172 case YAML_SEQUENCE_START_EVENT: | |
| 173 depth++; | |
| 174 break; | |
| 175 case YAML_SCALAR_EVENT: | |
| 176 switch (index2) { | |
| 177 case 0: | |
| 178 screen.data.images[index1].x = atoi((char*)event.data.scalar.value); | |
| 179 break; | |
| 180 case 1: | |
| 181 screen.data.images[index1].y = atoi((char*)event.data.scalar.value); | |
| 182 break; | |
| 183 case 2: | |
| 184 screen.image_names[index1] = (char*)event.data.scalar.value; | |
| 185 break; | |
| 186 default: | |
| 187 error("Syntax error in parsing layout.\n"); | |
| 188 } | |
| 189 index2++; | |
| 190 break; | |
| 191 case YAML_SEQUENCE_END_EVENT: | |
| 192 if (depth == 1) { | |
| 193 index1++; | |
| 194 index2 = 0; | |
| 195 } else if (depth == 0) { | |
| 196 yaml_event_delete(&event); | |
| 197 return; | |
| 198 } | |
| 199 depth--; | |
| 200 break; | |
| 201 default: | |
| 202 error("Syntax error in paring layout.\n"); | |
| 203 } | |
| 204 yaml_event_delete(&event); | |
| 205 } | |
| 206 } | |
| 207 | |
| 208 void BmpBlockUtil::parse_screens(yaml_parser_t *parser) { | |
| 209 yaml_event_t event; | |
| 210 string screen_name; | |
| 211 expect_event(parser, YAML_MAPPING_START_EVENT); | |
| 212 for (;;) { | |
| 213 yaml_parser_parse(parser, &event); | |
| 214 switch (event.type) { | |
| 215 case YAML_SCALAR_EVENT: | |
| 216 screen_name = (char*)event.data.scalar.value; | |
| 217 config_.screens_map[screen_name] = ScreenConfig(); | |
| 218 parse_layout(parser, config_.screens_map[screen_name]); | |
| 219 break; | |
| 220 case YAML_MAPPING_END_EVENT: | |
| 221 yaml_event_delete(&event); | |
| 222 return; | |
| 223 default: | |
| 224 error("Syntax error in parsing screens.\n"); | |
| 225 } | |
| 226 yaml_event_delete(&event); | |
| 227 } | |
| 228 } | |
| 229 | |
| 230 void BmpBlockUtil::parse_localizations(yaml_parser_t *parser) { | |
| 231 yaml_event_t event; | |
| 232 int depth = 0, index = 0; | |
| 233 expect_event(parser, YAML_SEQUENCE_START_EVENT); | |
| 234 for (;;) { | |
| 235 yaml_parser_parse(parser, &event); | |
| 236 switch (event.type) { | |
| 237 case YAML_SEQUENCE_START_EVENT: | |
| 238 config_.localizations.push_back(vector<string>()); | |
| 239 depth++; | |
| 240 break; | |
| 241 case YAML_SCALAR_EVENT: | |
| 242 config_.localizations[index].push_back((char*)event.data.scalar.value); | |
| 243 break; | |
| 244 case YAML_SEQUENCE_END_EVENT: | |
| 245 if (depth == 1) { | |
| 246 index++; | |
| 247 } else if (depth == 0) { | |
| 248 yaml_event_delete(&event); | |
| 249 return; | |
| 250 } | |
| 251 depth--; | |
| 252 break; | |
| 253 default: | |
| 254 error("Syntax error in parsing localizations.\n"); | |
| 255 } | |
| 256 yaml_event_delete(&event); | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 void BmpBlockUtil::load_all_image_files() { | |
| 261 for (StrImageConfigMap::iterator it = config_.images_map.begin(); | |
| 262 it != config_.images_map.end(); | |
| 263 ++it) { | |
| 264 const string &content = read_image_file(it->second.filename.c_str()); | |
| 265 it->second.raw_content = content; | |
| 266 it->second.data.original_size = content.size(); | |
| 267 /* Use no compression as default */ | |
| 268 it->second.data.compression = COMPRESS_NONE; | |
| 269 it->second.compressed_content = content; | |
| 270 it->second.data.compressed_size = content.size(); | |
| 271 } | |
| 272 } | |
| 273 | |
| 274 const string BmpBlockUtil::read_image_file(const char *filename) { | |
| 275 string content; | |
| 276 vector<char> buffer; | |
| 277 | |
| 278 FILE *fp = fopen(filename, "rb"); | |
| 279 if (!fp) { | |
| 280 perror(filename); | |
| 281 exit(errno); | |
| 282 } | |
| 283 | |
| 284 if (fseek(fp, 0, SEEK_END) == 0) { | |
| 285 buffer.resize(ftell(fp)); | |
| 286 rewind(fp); | |
| 287 } | |
| 288 | |
| 289 if (!buffer.empty()) { | |
| 290 if(fread(&buffer[0], buffer.size(), 1, fp) != 1) { | |
| 291 perror(filename); | |
| 292 buffer.clear(); | |
| 293 } else { | |
| 294 content.assign(buffer.begin(), buffer.end()); | |
| 295 } | |
| 296 } | |
| 297 | |
| 298 fclose(fp); | |
| 299 return content; | |
| 300 } | |
| 301 | |
| 302 ImageFormat BmpBlockUtil::get_image_format(const string content) { | |
| 303 if (content[0] == 'B' && content[1] == 'M') | |
| 304 return FORMAT_BMP; | |
| 305 else | |
| 306 return FORMAT_INVALID; | |
| 307 } | |
| 308 | |
| 309 uint32_t BmpBlockUtil::get_bmp_image_width(const string content) { | |
| 310 #define BMP_WIDTH_OFFSET 18 | |
|
Hung-Te
2011/01/21 07:45:41
please move this to file beginning, and comment ho
Tom Wai-Hong Tam
2011/01/21 08:32:09
Done.
| |
| 311 const char *start = content.c_str(); | |
| 312 return *(uint32_t*)(start + BMP_WIDTH_OFFSET); | |
| 313 } | |
| 314 | |
| 315 uint32_t BmpBlockUtil::get_bmp_image_height(const string content) { | |
| 316 #define BMP_HEIGHT_OFFSET 22 | |
|
Hung-Te
2011/01/21 07:45:41
Please check comments for BMP_WIDTH_OFFSET
Tom Wai-Hong Tam
2011/01/21 08:32:09
Done.
| |
| 317 const char *start = content.c_str(); | |
| 318 return *(uint32_t*)(start + BMP_HEIGHT_OFFSET); | |
| 319 } | |
| 320 | |
| 321 void BmpBlockUtil::fill_all_image_infos() { | |
| 322 for (StrImageConfigMap::iterator it = config_.images_map.begin(); | |
| 323 it != config_.images_map.end(); | |
| 324 ++it) { | |
| 325 it->second.data.format = (uint32_t)get_image_format(it->second.raw_content); | |
| 326 switch (it->second.data.format) { | |
| 327 case FORMAT_BMP: | |
| 328 it->second.data.width = get_bmp_image_width(it->second.raw_content); | |
| 329 it->second.data.height = get_bmp_image_height(it->second.raw_content); | |
| 330 break; | |
| 331 default: | |
| 332 error("Unsupported image format."); | |
| 333 } | |
| 334 } | |
| 335 } | |
| 336 | |
| 337 void BmpBlockUtil::compress_all_images(const Compression compress) { | |
| 338 switch (compress) { | |
| 339 case COMPRESS_NONE: | |
| 340 for (StrImageConfigMap::iterator it = config_.images_map.begin(); | |
| 341 it != config_.images_map.end(); | |
| 342 ++it) { | |
| 343 it->second.data.compression = compress; | |
| 344 it->second.compressed_content = it->second.raw_content; | |
| 345 it->second.data.compressed_size = it->second.compressed_content.size(); | |
| 346 } | |
| 347 break; | |
| 348 case COMPRESS_LZMA: | |
| 349 /* TODO(waihong): Implement the support of LZMA. */ | |
| 350 break; | |
| 351 default: | |
| 352 error("Unsupported data compression."); | |
| 353 } | |
| 354 } | |
| 355 | |
| 356 void BmpBlockUtil::fill_bmpblock_header() { | |
| 357 memset(&config_.header, '\0', BMPBLOCK_SIGNATURE_SIZE); | |
|
Hung-Te
2011/01/21 07:45:41
Is this SIGNATURE SIZE, or header size?
Tom Wai-Hong Tam
2011/01/21 08:32:09
It's header size. Fixed it.
| |
| 358 memcpy(&config_.header.signature, BMPBLOCK_SIGNATURE, | |
| 359 BMPBLOCK_SIGNATURE_SIZE); | |
| 360 config_.header.number_of_localizations = config_.localizations.size(); | |
| 361 config_.header.number_of_screenlayouts = config_.localizations[0].size(); | |
|
Hung-Te
2011/01/21 07:45:41
should we make some assert to check that each _loc
Tom Wai-Hong Tam
2011/01/21 08:32:09
Done.
| |
| 362 config_.header.number_of_imageinfos = config_.images_map.size(); | |
| 363 } | |
| 364 | |
| 365 void BmpBlockUtil::pack_bmpblock() { | |
| 366 bmpblock_.clear(); | |
| 367 | |
| 368 /* Compute the ImageInfo offsets from start of BMPBLOCK. */ | |
| 369 uint32_t current_offset = sizeof(BmpBlockHeader) + | |
| 370 sizeof(ScreenLayout) * config_.images_map.size(); | |
| 371 for (StrImageConfigMap::iterator it = config_.images_map.begin(); | |
| 372 it != config_.images_map.end(); | |
| 373 ++it) { | |
| 374 it->second.offset = current_offset; | |
| 375 current_offset += sizeof(ImageInfo) + it->second.data.compressed_size; | |
| 376 /* Make it 4-byte aligned. */ | |
| 377 if ((current_offset & 3) > 0) { | |
| 378 current_offset = (current_offset & ~3) + 4; | |
| 379 } | |
| 380 } | |
| 381 bmpblock_.resize(current_offset); | |
| 382 | |
| 383 /* Fill BmpBlockHeader struct. */ | |
| 384 string::iterator current_filled = bmpblock_.begin(); | |
| 385 std::copy(reinterpret_cast<char*>(&config_.header), | |
| 386 reinterpret_cast<char*>(&config_.header + 1), | |
| 387 current_filled); | |
| 388 current_filled += sizeof(config_.header); | |
| 389 | |
| 390 /* Fill all ScreenLayout structs. */ | |
| 391 for (unsigned int i = 0; i < config_.localizations.size(); ++i) { | |
| 392 for (unsigned int j = 0; j < config_.localizations[i].size(); ++j) { | |
| 393 ScreenConfig &screen = config_.screens_map[config_.localizations[i][j]]; | |
| 394 for (unsigned int k = 0; | |
| 395 k < MAX_IMAGE_IN_LAYOUT && !screen.image_names[k].empty(); | |
| 396 ++k) { | |
| 397 screen.data.images[k].image_info_offset = | |
| 398 config_.images_map[screen.image_names[k]].offset; | |
| 399 } | |
| 400 std::copy(reinterpret_cast<char*>(&screen.data), | |
| 401 reinterpret_cast<char*>(&screen.data + 1), | |
| 402 current_filled); | |
| 403 current_filled += sizeof(screen.data); | |
| 404 } | |
| 405 } | |
| 406 | |
| 407 /* Fill all ImageInfo structs and image contents. */ | |
| 408 for (StrImageConfigMap::iterator it = config_.images_map.begin(); | |
| 409 it != config_.images_map.end(); | |
| 410 ++it) { | |
| 411 current_filled = bmpblock_.begin() + it->second.offset; | |
| 412 std::copy(reinterpret_cast<char*>(&it->second.data), | |
| 413 reinterpret_cast<char*>(&it->second.data + 1), | |
| 414 current_filled); | |
| 415 current_filled += sizeof(it->second.data); | |
| 416 std::copy(it->second.compressed_content.begin(), | |
| 417 it->second.compressed_content.end(), | |
| 418 current_filled); | |
| 419 } | |
| 420 } | |
| 421 | |
| 422 void BmpBlockUtil::write_to_bmpblock(const char *filename) { | |
| 423 assert(!bmpblock_.empty()); | |
| 424 | |
| 425 FILE *fp = fopen(filename, "wb"); | |
| 426 if (!fp) { | |
| 427 perror(filename); | |
| 428 exit(errno); | |
| 429 } | |
| 430 | |
| 431 int r = fwrite(bmpblock_.c_str(), bmpblock_.size(), 1, fp); | |
| 432 fclose(fp); | |
| 433 if (r != 1) { | |
| 434 perror(filename); | |
| 435 exit(errno); | |
| 436 } | |
| 437 } | |
| 438 | |
| 439 } // namespace vboot_reference | |
| 440 | |
| 441 #ifdef WITH_UTIL_MAIN | |
| 442 | |
| 443 /////////////////////////////////////////////////////////////////////// | |
| 444 // command line utilities | |
| 445 | |
| 446 using vboot_reference::BmpBlockUtil; | |
| 447 | |
| 448 // utility function: provide usage of this utility and exit. | |
| 449 static void usagehelp_exit(const char *prog_name) { | |
| 450 printf( | |
| 451 "Utility to manage firmware screen block (BMPBLOCK)\n" | |
| 452 "Usage: %s -c|-l|-x [options] BMPBLOCK_FILE\n" | |
| 453 "\n" | |
| 454 "Main Operation Mode:\n" | |
| 455 " -c, --create Create a new BMPBLOCK file. Should specify --config.\n" | |
| 456 " -l, --list List the contents of a BMPBLOCK file.\n" | |
| 457 " -x, --extract Extract embedded images and config file from a BMPBLOCK\n" | |
| 458 " file.\n" | |
| 459 "\n" | |
| 460 "Other Options:\n" | |
| 461 " -C, --config=CONFIG_FILE Config file describing screen layouts and\n" | |
| 462 " embedded images. (default: bmpblk.cfg)\n" | |
| 463 "\n" | |
| 464 "Example:\n" | |
| 465 " %s --create --config=screens.cfg bmpblk.bin\n" | |
| 466 , prog_name, prog_name); | |
| 467 exit(1); | |
| 468 } | |
| 469 | |
| 470 /////////////////////////////////////////////////////////////////////// | |
| 471 // main | |
| 472 | |
| 473 int main(int argc, char *argv[]) { | |
| 474 const char *prog_name = argv[0]; | |
| 475 BmpBlockUtil util; | |
| 476 | |
| 477 struct BmpBlockUtilOptions { | |
| 478 bool create_mode, list_mode, extract_mode; | |
| 479 string config_fn, bmpblock_fn; | |
| 480 } options; | |
| 481 | |
| 482 int longindex, opt; | |
| 483 static struct option longopts[] = { | |
| 484 {"create", 0, NULL, 'c'}, | |
| 485 {"list", 0, NULL, 'l'}, | |
| 486 {"extract", 0, NULL, 'x'}, | |
| 487 {"config", 1, NULL, 'C'}, | |
| 488 { NULL, 0, NULL, 0 }, | |
| 489 }; | |
| 490 | |
| 491 while ((opt = getopt_long(argc, argv, "clx:C:", longopts, &longindex)) >= 0) { | |
|
Hung-Te
2011/01/21 07:45:41
'x:' or 'x'? looks like you don't have arg for -x.
Tom Wai-Hong Tam
2011/01/21 08:32:09
Yes, fixed it.
| |
| 492 switch (opt) { | |
| 493 case 'c': | |
| 494 options.create_mode = true; | |
| 495 break; | |
| 496 case 'l': | |
| 497 options.list_mode = true; | |
| 498 break; | |
| 499 case 'x': | |
| 500 options.extract_mode = true; | |
| 501 break; | |
| 502 case 'C': | |
| 503 options.config_fn = optarg; | |
| 504 break; | |
| 505 default: | |
| 506 case '?': | |
| 507 usagehelp_exit(prog_name); | |
| 508 break; | |
| 509 } | |
| 510 } | |
| 511 argc -= optind; | |
| 512 argv += optind; | |
| 513 | |
| 514 if (argc == 1) { | |
| 515 options.bmpblock_fn = argv[0]; | |
| 516 } else { | |
| 517 usagehelp_exit(prog_name); | |
| 518 } | |
| 519 | |
| 520 if (options.create_mode) { | |
| 521 util.load_from_config(options.config_fn.c_str()); | |
| 522 util.pack_bmpblock(); | |
| 523 util.write_to_bmpblock(options.bmpblock_fn.c_str()); | |
| 524 printf("The BMPBLOCK is sucessfully created in: %s.\n", | |
| 525 options.bmpblock_fn.c_str()); | |
| 526 } | |
| 527 | |
| 528 if (options.list_mode) { | |
| 529 /* TODO(waihong): Implement the list mode. */ | |
| 530 error("List mode hasn't been implemented yet.\n"); | |
| 531 } | |
| 532 | |
| 533 if (options.extract_mode) { | |
| 534 /* TODO(waihong): Implement the extract mode. */ | |
| 535 error("Extract mode hasn't been implemented yet.\n"); | |
| 536 } | |
| 537 | |
| 538 return 0; | |
| 539 } | |
| 540 | |
| 541 #endif // WITH_UTIL_MAIN | |
| OLD | NEW |