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 <assert.h> 9 #include <errno.h> 10 #include <getopt.h> 11 #include <lzma.h> 12 #include <stdarg.h> 13 #include <stdint.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 #include <yaml.h> 18 19 #include "bmpblk_utility.h" 20 #include "image_types.h" 21 #include "vboot_api.h" 22 23 extern "C" { 24 #include "eficompress.h" 25 } 26 27 28 static void error(const char *format, ...) { 29 va_list ap; 30 va_start(ap, format); 31 fprintf(stderr, "ERROR: "); 32 vfprintf(stderr, format, ap); 33 va_end(ap); 34 exit(1); 35 } 36 37 /////////////////////////////////////////////////////////////////////// 38 // BmpBlock Utility implementation 39 40 namespace vboot_reference { 41 42 BmpBlockUtil::BmpBlockUtil(bool debug) { 43 major_version_ = BMPBLOCK_MAJOR_VERSION; 44 minor_version_ = BMPBLOCK_MINOR_VERSION; 45 config_.config_filename.clear(); 46 memset(&config_.header, '\0', BMPBLOCK_SIGNATURE_SIZE); 47 config_.images_map.clear(); 48 config_.screens_map.clear(); 49 config_.localizations.clear(); 50 bmpblock_.clear(); 51 set_compression_ = false; 52 compression_ = COMPRESS_NONE; 53 debug_ = debug; 54 render_hwid_ = true; 55 support_font_ = true; 56 got_font_ = false; 57 got_rtol_font_ = false; 58 } 59 60 BmpBlockUtil::~BmpBlockUtil() { 61 } 62 63 void BmpBlockUtil::force_compression(uint32_t compression) { 64 compression_ = compression; 65 set_compression_ = true; 66 } 67 68 void BmpBlockUtil::load_from_config(const char *filename) { 69 load_yaml_config(filename); 70 fill_bmpblock_header(); 71 load_all_image_files(); 72 } 73 74 void BmpBlockUtil::load_yaml_config(const char *filename) { 75 yaml_parser_t parser; 76 77 config_.config_filename = filename; 78 config_.images_map.clear(); 79 config_.screens_map.clear(); 80 config_.localizations.clear(); 81 config_.locale_names.clear(); 82 83 FILE *fp = fopen(filename, "rb"); 84 if (!fp) { 85 perror(filename); 86 exit(errno); 87 } 88 89 yaml_parser_initialize(&parser); 90 yaml_parser_set_input_file(&parser, fp); 91 parse_config(&parser); 92 yaml_parser_delete(&parser); 93 fclose(fp); 94 95 96 // TODO: Check the yaml file for self-consistency. Warn on any problems. 97 // All images should be used somewhere in the screens. 98 // All images referenced in the screens should be defined. 99 // All screens should be used somewhere in the localizations. 100 // All screens referenced in the localizations should be defined. 101 // The number of localizations should match the number of locale_index 102 103 if (debug_) { 104 printf("%ld image_names\n", config_.image_names.size()); 105 for (unsigned int i = 0; i < config_.image_names.size(); ++i) { 106 printf(" %d: \"%s\"\n", i, config_.image_names[i].c_str()); 107 } 108 printf("%ld images_map\n", config_.images_map.size()); 109 for (StrImageConfigMap::iterator it = config_.images_map.begin(); 110 it != config_.images_map.end(); 111 ++it) { 112 printf(" \"%s\": filename=\"%s\" offset=0x%x tag=%d fmt=%d\n", 113 it->first.c_str(), 114 it->second.filename.c_str(), 115 it->second.offset, 116 it->second.data.tag, 117 it->second.data.format); 118 } 119 printf("%ld screens_map\n", config_.screens_map.size()); 120 for (StrScreenConfigMap::iterator it = config_.screens_map.begin(); 121 it != config_.screens_map.end(); 122 ++it) { 123 printf(" \"%s\":\n", it->first.c_str()); 124 for (int k=0; k<MAX_IMAGE_IN_LAYOUT; k++) { 125 printf(" %d: \"%s\" (%d,%d) ofs=0x%x\n", 126 k, 127 it->second.image_names[k].c_str(), 128 it->second.data.images[k].x, 129 it->second.data.images[k].y, 130 it->second.data.images[k].image_info_offset); 131 } 132 } 133 } 134 } 135 136 void BmpBlockUtil::expect_event(yaml_parser_t *parser, 137 const yaml_event_type_e type) { 138 yaml_event_t event; 139 yaml_parser_parse(parser, &event); 140 if (event.type != type) { 141 error("Syntax error.\n"); 142 } 143 yaml_event_delete(&event); 144 } 145 146 void BmpBlockUtil::parse_config(yaml_parser_t *parser) { 147 expect_event(parser, YAML_STREAM_START_EVENT); 148 expect_event(parser, YAML_DOCUMENT_START_EVENT); 149 parse_first_layer(parser); 150 expect_event(parser, YAML_DOCUMENT_END_EVENT); 151 expect_event(parser, YAML_STREAM_END_EVENT); 152 } 153 154 void BmpBlockUtil::parse_first_layer(yaml_parser_t *parser) { 155 yaml_event_t event; 156 string keyword; 157 expect_event(parser, YAML_MAPPING_START_EVENT); 158 for (;;) { 159 yaml_parser_parse(parser, &event); 160 switch (event.type) { 161 case YAML_SCALAR_EVENT: 162 keyword = (char*)event.data.scalar.value; 163 if (keyword == "bmpblock") { 164 parse_bmpblock(parser); 165 } else if (keyword == "compression") { 166 parse_compression(parser); 167 } else if (keyword == "images") { 168 parse_images(parser); 169 } else if (keyword == "screens") { 170 parse_screens(parser); 171 } else if (keyword == "localizations") { 172 parse_localizations(parser); 173 } else if (keyword == "locale_index") { 174 parse_locale_index(parser); 175 } 176 break; 177 case YAML_MAPPING_END_EVENT: 178 yaml_event_delete(&event); 179 return; 180 default: 181 error("Syntax error in parsing config file.\n"); 182 } 183 yaml_event_delete(&event); 184 } 185 } 186 187 void BmpBlockUtil::parse_bmpblock(yaml_parser_t *parser) { 188 yaml_event_t event; 189 yaml_parser_parse(parser, &event); 190 if (event.type != YAML_SCALAR_EVENT) { 191 error("Syntax error in parsing bmpblock.\n"); 192 } 193 string gotversion = (char*)event.data.scalar.value; 194 if (gotversion != "2.0") { 195 error("Unsupported version specified in config file (%s)\n", 196 gotversion.c_str()); 197 } 198 yaml_event_delete(&event); 199 } 200 201 void BmpBlockUtil::parse_compression(yaml_parser_t *parser) { 202 yaml_event_t event; 203 yaml_parser_parse(parser, &event); 204 if (event.type != YAML_SCALAR_EVENT) { 205 error("Syntax error in parsing bmpblock.\n"); 206 } 207 char *comp_str = (char *)event.data.scalar.value; 208 char *e = 0; 209 uint32_t comp = (uint32_t)strtoul(comp_str, &e, 0); 210 if (!*comp_str || (e && *e) || comp >= MAX_COMPRESS) { 211 error("Invalid compression specified in config file (%d)\n", comp); 212 } 213 if (!set_compression_) { 214 compression_ = comp; 215 } 216 yaml_event_delete(&event); 217 } 218 219 void BmpBlockUtil::parse_images(yaml_parser_t *parser) { 220 yaml_event_t event; 221 string image_name, image_filename; 222 expect_event(parser, YAML_MAPPING_START_EVENT); 223 for (;;) { 224 yaml_parser_parse(parser, &event); 225 switch (event.type) { 226 case YAML_SCALAR_EVENT: 227 image_name = (char*)event.data.scalar.value; 228 yaml_event_delete(&event); 229 yaml_parser_parse(parser, &event); 230 if (event.type != YAML_SCALAR_EVENT) { 231 error("Syntax error in parsing images.\n"); 232 } 233 image_filename = (char*)event.data.scalar.value; 234 config_.image_names.push_back(image_name); 235 config_.images_map[image_name] = ImageConfig(); 236 config_.images_map[image_name].filename = image_filename; 237 if (image_name == RENDER_HWID) { 238 got_font_ = true; 239 } 240 if (image_name == RENDER_HWID_RTOL) { 241 got_rtol_font_ = true; 242 } 243 break; 244 case YAML_MAPPING_END_EVENT: 245 yaml_event_delete(&event); 246 return; 247 default: 248 error("Syntax error in parsing images.\n"); 249 } 250 yaml_event_delete(&event); 251 } 252 } 253 254 void BmpBlockUtil::parse_layout(yaml_parser_t *parser, ScreenConfig &screen) { 255 yaml_event_t event; 256 int depth = 0, index1 = 0, index2 = 0; 257 expect_event(parser, YAML_SEQUENCE_START_EVENT); 258 for (;;) { 259 yaml_parser_parse(parser, &event); 260 switch (event.type) { 261 case YAML_SEQUENCE_START_EVENT: 262 depth++; 263 break; 264 case YAML_SCALAR_EVENT: 265 switch (index2) { 266 case 0: 267 screen.data.images[index1].x = atoi((char*)event.data.scalar.value); 268 break; 269 case 1: 270 screen.data.images[index1].y = atoi((char*)event.data.scalar.value); 271 break; 272 case 2: 273 screen.image_names[index1] = (char*)event.data.scalar.value; 274 // Detect the special case where we're rendering the HWID string 275 // instead of displaying a bitmap. The image name may not 276 // exist in the list of images (v1.1), but we will still need an 277 // ImageInfo struct to remember where to draw the text. 278 // Note that v1.2 requires that the image name DOES exist, because 279 // the corresponding file is used to hold the font glpyhs. 280 if (render_hwid_) { 281 if (screen.image_names[index1] == RENDER_HWID) { 282 config_.images_map[RENDER_HWID].data.tag = TAG_HWID; 283 if (support_font_ && !got_font_) 284 error("Font required in 'image:' section for %s\n", 285 RENDER_HWID); 286 } else if (screen.image_names[index1] == RENDER_HWID_RTOL) { 287 config_.images_map[RENDER_HWID_RTOL].data.tag = TAG_HWID_RTOL; 288 if (support_font_ && !got_rtol_font_) 289 error("Font required in 'image:' section for %s\n", 290 RENDER_HWID_RTOL); 291 } 292 } 293 break; 294 default: 295 error("Syntax error in parsing layout\n"); 296 } 297 index2++; 298 break; 299 case YAML_SEQUENCE_END_EVENT: 300 if (depth == 1) { 301 index1++; 302 index2 = 0; 303 } else if (depth == 0) { 304 yaml_event_delete(&event); 305 return; 306 } 307 depth--; 308 break; 309 default: 310 error("Syntax error in paring layout.\n"); 311 } 312 yaml_event_delete(&event); 313 } 314 } 315 316 void BmpBlockUtil::parse_screens(yaml_parser_t *parser) { 317 yaml_event_t event; 318 string screen_name; 319 expect_event(parser, YAML_MAPPING_START_EVENT); 320 for (;;) { 321 yaml_parser_parse(parser, &event); 322 switch (event.type) { 323 case YAML_SCALAR_EVENT: 324 screen_name = (char*)event.data.scalar.value; 325 config_.screens_map[screen_name] = ScreenConfig(); 326 parse_layout(parser, config_.screens_map[screen_name]); 327 break; 328 case YAML_MAPPING_END_EVENT: 329 yaml_event_delete(&event); 330 return; 331 default: 332 error("Syntax error in parsing screens.\n"); 333 } 334 yaml_event_delete(&event); 335 } 336 } 337 338 void BmpBlockUtil::parse_localizations(yaml_parser_t *parser) { 339 yaml_event_t event; 340 int depth = 0, index = 0; 341 expect_event(parser, YAML_SEQUENCE_START_EVENT); 342 for (;;) { 343 yaml_parser_parse(parser, &event); 344 switch (event.type) { 345 case YAML_SEQUENCE_START_EVENT: 346 config_.localizations.push_back(vector<string>()); 347 depth++; 348 break; 349 case YAML_SCALAR_EVENT: 350 config_.localizations[index].push_back((char*)event.data.scalar.value); 351 break; 352 case YAML_SEQUENCE_END_EVENT: 353 if (depth == 1) { 354 index++; 355 } else if (depth == 0) { 356 yaml_event_delete(&event); 357 return; 358 } 359 depth--; 360 break; 361 default: 362 error("Syntax error in parsing localizations.\n"); 363 } 364 yaml_event_delete(&event); 365 } 366 } 367 368 void BmpBlockUtil::parse_locale_index(yaml_parser_t *parser) { 369 yaml_event_t event; 370 expect_event(parser, YAML_SEQUENCE_START_EVENT); 371 for (;;) { 372 yaml_parser_parse(parser, &event); 373 switch (event.type) { 374 case YAML_SCALAR_EVENT: 375 config_.locale_names.append((char*)event.data.scalar.value); 376 config_.locale_names.append(1, (char)'\0'); // '\0' to delimit 377 break; 378 case YAML_SEQUENCE_END_EVENT: 379 yaml_event_delete(&event); 380 config_.locale_names.append(1, (char)'\0'); // double '\0' to terminate 381 return; 382 default: 383 error("Syntax error in parsing localizations.\n"); 384 } 385 } 386 } 387 388 void BmpBlockUtil::load_all_image_files() { 389 for (unsigned int i = 0; i < config_.image_names.size(); i++) { 390 StrImageConfigMap::iterator it = 391 config_.images_map.find(config_.image_names[i]); 392 if (debug_) { 393 printf("loading image \"%s\" from \"%s\"\n", 394 config_.image_names[i].c_str(), 395 it->second.filename.c_str()); 396 } 397 const string &content = read_image_file(it->second.filename.c_str()); 398 it->second.raw_content = content; 399 it->second.data.original_size = content.size(); 400 it->second.data.format = 401 identify_image_type(content.c_str(), 402 (uint32_t)content.size(), &it->second.data); 403 if (FORMAT_INVALID == it->second.data.format) { 404 error("Unsupported image format in %s\n", it->second.filename.c_str()); 405 } 406 switch(compression_) { 407 case COMPRESS_NONE: 408 it->second.data.compression = compression_; 409 it->second.compressed_content = content; 410 it->second.data.compressed_size = content.size(); 411 break; 412 case COMPRESS_EFIv1: 413 { 414 // The content will always compress smaller (so sez the docs). 415 uint32_t tmpsize = content.size(); 416 uint8_t *tmpbuf = (uint8_t *)malloc(tmpsize); 417 // The size of the compressed content is also returned. 418 if (EFI_SUCCESS != EfiCompress((uint8_t *)content.c_str(), tmpsize, 419 tmpbuf, &tmpsize)) { 420 error("Unable to compress!\n"); 421 } 422 it->second.data.compression = compression_; 423 it->second.compressed_content.assign((const char *)tmpbuf, tmpsize); 424 it->second.data.compressed_size = tmpsize; 425 free(tmpbuf); 426 } 427 break; 428 case COMPRESS_LZMA1: 429 { 430 // Calculate the worst case of buffer size. 431 uint32_t tmpsize = lzma_stream_buffer_bound(content.size()); 432 uint8_t *tmpbuf = (uint8_t *)malloc(tmpsize); 433 lzma_stream stream = LZMA_STREAM_INIT; 434 lzma_options_lzma options; 435 lzma_ret result; 436 437 lzma_lzma_preset(&options, 9); 438 result = lzma_alone_encoder(&stream, &options); 439 if (result != LZMA_OK) { 440 error("Unable to initialize easy encoder (error: %d)!\n", result); 441 } 442 443 stream.next_in = (uint8_t *)content.data(); 444 stream.avail_in = content.size(); 445 stream.next_out = tmpbuf; 446 stream.avail_out = tmpsize; 447 result = lzma_code(&stream, LZMA_FINISH); 448 if (result != LZMA_STREAM_END) { 449 error("Unable to encode data (error: %d)!\n", result); 450 } 451 452 it->second.data.compression = compression_; 453 it->second.compressed_content.assign((const char *)tmpbuf, 454 tmpsize - stream.avail_out); 455 it->second.data.compressed_size = tmpsize - stream.avail_out; 456 lzma_end(&stream); 457 free(tmpbuf); 458 } 459 break; 460 default: 461 error("Unsupported compression method attempted.\n"); 462 } 463 } 464 } 465 466 const string BmpBlockUtil::read_image_file(const char *filename) { 467 string content; 468 vector<char> buffer; 469 470 FILE *fp = fopen(filename, "rb"); 471 if (!fp) { 472 perror(filename); 473 exit(errno); 474 } 475 476 if (fseek(fp, 0, SEEK_END) == 0) { 477 buffer.resize(ftell(fp)); 478 rewind(fp); 479 } 480 481 if (!buffer.empty()) { 482 if(fread(&buffer[0], buffer.size(), 1, fp) != 1) { 483 perror(filename); 484 buffer.clear(); 485 } else { 486 content.assign(buffer.begin(), buffer.end()); 487 } 488 } 489 490 fclose(fp); 491 return content; 492 } 493 494 void BmpBlockUtil::fill_bmpblock_header() { 495 memset(&config_.header, '\0', sizeof(config_.header)); 496 memcpy(&config_.header.signature, BMPBLOCK_SIGNATURE, 497 BMPBLOCK_SIGNATURE_SIZE); 498 config_.header.major_version = major_version_; 499 config_.header.minor_version = minor_version_; 500 config_.header.number_of_localizations = config_.localizations.size(); 501 config_.header.number_of_screenlayouts = config_.localizations[0].size(); 502 // NOTE: this is part of the yaml consistency check 503 for (unsigned int i = 1; i < config_.localizations.size(); ++i) { 504 assert(config_.header.number_of_screenlayouts == 505 config_.localizations[i].size()); 506 } 507 config_.header.number_of_imageinfos = config_.images_map.size(); 508 config_.header.locale_string_offset = 0; // Filled by pack_bmpblock() 509 } 510 511 void BmpBlockUtil::pack_bmpblock() { 512 bmpblock_.clear(); 513 514 /* Compute the ImageInfo offsets from start of BMPBLOCK. */ 515 uint32_t current_offset = sizeof(BmpBlockHeader) + 516 sizeof(ScreenLayout) * (config_.header.number_of_localizations * 517 config_.header.number_of_screenlayouts); 518 for (StrImageConfigMap::iterator it = config_.images_map.begin(); 519 it != config_.images_map.end(); 520 ++it) { 521 it->second.offset = current_offset; 522 if (debug_) 523 printf(" \"%s\": filename=\"%s\" offset=0x%x tag=%d fmt=%d\n", 524 it->first.c_str(), 525 it->second.filename.c_str(), 526 it->second.offset, 527 it->second.data.tag, 528 it->second.data.format); 529 current_offset += sizeof(ImageInfo) + 530 it->second.data.compressed_size; 531 /* Make it 4-byte aligned. */ 532 if ((current_offset & 3) > 0) { 533 current_offset = (current_offset & ~3) + 4; 534 } 535 } 536 /* And leave room for the locale_index string */ 537 if (config_.locale_names.size()) { 538 config_.header.locale_string_offset = current_offset; 539 current_offset += config_.locale_names.size(); 540 } 541 542 bmpblock_.resize(current_offset); 543 544 /* Fill BmpBlockHeader struct. */ 545 string::iterator current_filled = bmpblock_.begin(); 546 std::copy(reinterpret_cast<char*>(&config_.header), 547 reinterpret_cast<char*>(&config_.header + 1), 548 current_filled); 549 current_filled += sizeof(config_.header); 550 current_offset = sizeof(config_.header); 551 552 /* Fill all ScreenLayout structs. */ 553 for (unsigned int i = 0; i < config_.localizations.size(); ++i) { 554 for (unsigned int j = 0; j < config_.localizations[i].size(); ++j) { 555 ScreenConfig &screen = config_.screens_map[config_.localizations[i][j]]; 556 for (unsigned int k = 0; 557 k < MAX_IMAGE_IN_LAYOUT && !screen.image_names[k].empty(); 558 ++k) { 559 if (config_.images_map.find(screen.image_names[k]) == 560 config_.images_map.end()) { 561 error("Invalid image name \"%s\"\n", screen.image_names[k].c_str()); 562 } 563 if (debug_) 564 printf("i=%d j=%d k=%d=\"%s\" (%d,%d) ofs=%x\n", i,j,k, 565 screen.image_names[k].c_str(), 566 screen.data.images[k].x, screen.data.images[k].y, 567 config_.images_map[screen.image_names[k]].offset 568 ); 569 screen.data.images[k].image_info_offset = 570 config_.images_map[screen.image_names[k]].offset; 571 } 572 std::copy(reinterpret_cast<char*>(&screen.data), 573 reinterpret_cast<char*>(&screen.data + 1), 574 current_filled); 575 current_filled += sizeof(screen.data); 576 if (debug_) 577 printf("S: current offset is 0x%08x\n", current_offset); 578 current_offset += sizeof(screen.data); 579 } 580 } 581 582 /* Fill all ImageInfo structs and image contents. */ 583 for (StrImageConfigMap::iterator it = config_.images_map.begin(); 584 it != config_.images_map.end(); 585 ++it) { 586 current_filled = bmpblock_.begin() + it->second.offset; 587 current_offset = it->second.offset; 588 if (debug_) 589 printf("I0: current offset is 0x%08x\n", current_offset); 590 std::copy(reinterpret_cast<char*>(&it->second.data), 591 reinterpret_cast<char*>(&it->second.data + 1), 592 current_filled); 593 current_filled += sizeof(it->second.data); 594 current_offset += sizeof(it->second.data); 595 if (debug_) 596 printf("I1: current offset is 0x%08x (len %ld)\n", 597 current_offset, it->second.compressed_content.length()); 598 std::copy(it->second.compressed_content.begin(), 599 it->second.compressed_content.end(), 600 current_filled); 601 } 602 603 /* Fill in locale_names. */ 604 if (config_.header.locale_string_offset) { 605 current_offset = config_.header.locale_string_offset; 606 current_filled = bmpblock_.begin() + current_offset; 607 if (debug_) 608 printf("locale_names: offset 0x%08x (len %ld)\n", 609 current_offset, config_.locale_names.size()); 610 std::copy(config_.locale_names.begin(), 611 config_.locale_names.end(), 612 current_filled); 613 } 614 } 615 616 void BmpBlockUtil::write_to_bmpblock(const char *filename) { 617 assert(!bmpblock_.empty()); 618 619 FILE *fp = fopen(filename, "wb"); 620 if (!fp) { 621 perror(filename); 622 exit(errno); 623 } 624 625 int r = fwrite(bmpblock_.c_str(), bmpblock_.size(), 1, fp); 626 fclose(fp); 627 if (r != 1) { 628 perror(filename); 629 exit(errno); 630 } 631 } 632 633 } // namespace vboot_reference 634 635 #ifndef FOR_LIBRARY 636 637 ////////////////////////////////////////////////////////////////////////////// 638 // Command line utilities. 639 640 extern "C" { 641 #include "bmpblk_util.h" 642 } 643 644 using vboot_reference::BmpBlockUtil; 645 646 // utility function: provide usage of this utility and exit. 647 static void usagehelp_exit(const char *prog_name) { 648 printf( 649 "\n" 650 "To create a new BMPBLOCK file using config from YAML file:\n" 651 "\n" 652 " %s [-z NUM] -c YAML BMPBLOCK\n" 653 "\n" 654 " -z NUM = compression algorithm to use\n" 655 " 0 = none\n" 656 " 1 = EFIv1\n" 657 " 2 = LZMA1\n" 658 "\n", prog_name); 659 printf( 660 "To display the contents of a BMPBLOCK:\n" 661 "\n" 662 " %s [-y] BMPBLOCK\n" 663 "\n" 664 " -y = display as yaml\n" 665 "\n", prog_name); 666 printf( 667 "To unpack a BMPBLOCK file:\n" 668 "\n" 669 " %s -x [-d DIR] [-f] BMPBLOCK\n" 670 "\n" 671 " -d DIR = directory to use (default '.')\n" 672 " -f = force overwriting existing files\n" 673 "\n", prog_name); 674 exit(1); 675 } 676 677 /////////////////////////////////////////////////////////////////////// 678 // main 679 680 int main(int argc, char *argv[]) { 681 682 const char *prog_name = strrchr(argv[0], '/'); 683 if (prog_name) 684 prog_name++; 685 else 686 prog_name = argv[0]; 687 688 int overwrite = 0, extract_mode = 0; 689 int compression = 0; 690 int set_compression = 0; 691 const char *config_fn = 0, *bmpblock_fn = 0, *extract_dir = "."; 692 int show_as_yaml = 0; 693 bool debug = false; 694 695 int opt; 696 opterr = 0; // quiet 697 int errorcnt = 0; 698 char *e = 0; 699 while ((opt = getopt(argc, argv, ":c:xz:fd:yD")) != -1) { 700 switch (opt) { 701 case 'c': 702 config_fn = optarg; 703 break; 704 case 'x': 705 extract_mode = 1; 706 break; 707 case 'y': 708 show_as_yaml = 1; 709 break; 710 case 'z': 711 compression = (int)strtoul(optarg, &e, 0); 712 if (!*optarg || (e && *e)) { 713 fprintf(stderr, "%s: invalid argument to -%c: \"%s\"\n", 714 prog_name, opt, optarg); 715 errorcnt++; 716 } 717 if (compression >= MAX_COMPRESS) { 718 fprintf(stderr, "%s: compression type must be less than %d\n", 719 prog_name, MAX_COMPRESS); 720 errorcnt++; 721 } 722 set_compression = 1; 723 break; 724 case 'f': 725 overwrite = 1; 726 break; 727 case 'd': 728 extract_dir= optarg; 729 break; 730 case 'D': 731 debug = true; 732 break; 733 case ':': 734 fprintf(stderr, "%s: missing argument to -%c\n", 735 prog_name, optopt); 736 errorcnt++; 737 break; 738 default: 739 fprintf(stderr, "%s: unrecognized switch: -%c\n", 740 prog_name, optopt); 741 errorcnt++; 742 break; 743 } 744 } 745 argc -= optind; 746 argv += optind; 747 748 if (argc >= 1) { 749 bmpblock_fn = argv[0]; 750 } else { 751 fprintf(stderr, "%s: missing BMPBLOCK name\n", prog_name); 752 errorcnt++; 753 } 754 755 if (errorcnt) 756 usagehelp_exit(prog_name); 757 758 BmpBlockUtil util(debug); 759 760 if (config_fn) { 761 if (set_compression) 762 util.force_compression(compression); 763 util.load_from_config(config_fn); 764 util.pack_bmpblock(); 765 util.write_to_bmpblock(bmpblock_fn); 766 } 767 768 else if (extract_mode) { 769 return dump_bmpblock(bmpblock_fn, 1, extract_dir, overwrite); 770 } else { 771 return dump_bmpblock(bmpblock_fn, show_as_yaml, 0, 0); 772 } 773 774 return 0; 775 } 776 777 #endif // FOR_LIBRARY 778