Home | History | Annotate | Download | only in utility
      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