You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
777 lines
24 KiB
777 lines
24 KiB
// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
|
// Use of this source code is governed by a BSD-style license that can be |
|
// found in the LICENSE file. |
|
// |
|
// Utility for manipulating firmware screen block (BMPBLOCK) in GBB. |
|
// |
|
|
|
#include <assert.h> |
|
#include <errno.h> |
|
#include <getopt.h> |
|
#include <lzma.h> |
|
#include <stdarg.h> |
|
#include <stdint.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <yaml.h> |
|
|
|
#include "bmpblk_utility.h" |
|
#include "image_types.h" |
|
#include "vboot_api.h" |
|
|
|
extern "C" { |
|
#include "eficompress.h" |
|
} |
|
|
|
|
|
static void error(const char *format, ...) { |
|
va_list ap; |
|
va_start(ap, format); |
|
fprintf(stderr, "ERROR: "); |
|
vfprintf(stderr, format, ap); |
|
va_end(ap); |
|
exit(1); |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////// |
|
// BmpBlock Utility implementation |
|
|
|
namespace vboot_reference { |
|
|
|
BmpBlockUtil::BmpBlockUtil(bool debug) { |
|
major_version_ = BMPBLOCK_MAJOR_VERSION; |
|
minor_version_ = BMPBLOCK_MINOR_VERSION; |
|
config_.config_filename.clear(); |
|
memset(&config_.header, '\0', BMPBLOCK_SIGNATURE_SIZE); |
|
config_.images_map.clear(); |
|
config_.screens_map.clear(); |
|
config_.localizations.clear(); |
|
bmpblock_.clear(); |
|
set_compression_ = false; |
|
compression_ = COMPRESS_NONE; |
|
debug_ = debug; |
|
render_hwid_ = true; |
|
support_font_ = true; |
|
got_font_ = false; |
|
got_rtol_font_ = false; |
|
} |
|
|
|
BmpBlockUtil::~BmpBlockUtil() { |
|
} |
|
|
|
void BmpBlockUtil::force_compression(uint32_t compression) { |
|
compression_ = compression; |
|
set_compression_ = true; |
|
} |
|
|
|
void BmpBlockUtil::load_from_config(const char *filename) { |
|
load_yaml_config(filename); |
|
fill_bmpblock_header(); |
|
load_all_image_files(); |
|
} |
|
|
|
void BmpBlockUtil::load_yaml_config(const char *filename) { |
|
yaml_parser_t parser; |
|
|
|
config_.config_filename = filename; |
|
config_.images_map.clear(); |
|
config_.screens_map.clear(); |
|
config_.localizations.clear(); |
|
config_.locale_names.clear(); |
|
|
|
FILE *fp = fopen(filename, "rb"); |
|
if (!fp) { |
|
perror(filename); |
|
exit(errno); |
|
} |
|
|
|
yaml_parser_initialize(&parser); |
|
yaml_parser_set_input_file(&parser, fp); |
|
parse_config(&parser); |
|
yaml_parser_delete(&parser); |
|
fclose(fp); |
|
|
|
|
|
// TODO: Check the yaml file for self-consistency. Warn on any problems. |
|
// All images should be used somewhere in the screens. |
|
// All images referenced in the screens should be defined. |
|
// All screens should be used somewhere in the localizations. |
|
// All screens referenced in the localizations should be defined. |
|
// The number of localizations should match the number of locale_index |
|
|
|
if (debug_) { |
|
printf("%ld image_names\n", config_.image_names.size()); |
|
for (unsigned int i = 0; i < config_.image_names.size(); ++i) { |
|
printf(" %d: \"%s\"\n", i, config_.image_names[i].c_str()); |
|
} |
|
printf("%ld images_map\n", config_.images_map.size()); |
|
for (StrImageConfigMap::iterator it = config_.images_map.begin(); |
|
it != config_.images_map.end(); |
|
++it) { |
|
printf(" \"%s\": filename=\"%s\" offset=0x%x tag=%d fmt=%d\n", |
|
it->first.c_str(), |
|
it->second.filename.c_str(), |
|
it->second.offset, |
|
it->second.data.tag, |
|
it->second.data.format); |
|
} |
|
printf("%ld screens_map\n", config_.screens_map.size()); |
|
for (StrScreenConfigMap::iterator it = config_.screens_map.begin(); |
|
it != config_.screens_map.end(); |
|
++it) { |
|
printf(" \"%s\":\n", it->first.c_str()); |
|
for (int k=0; k<MAX_IMAGE_IN_LAYOUT; k++) { |
|
printf(" %d: \"%s\" (%d,%d) ofs=0x%x\n", |
|
k, |
|
it->second.image_names[k].c_str(), |
|
it->second.data.images[k].x, |
|
it->second.data.images[k].y, |
|
it->second.data.images[k].image_info_offset); |
|
} |
|
} |
|
} |
|
} |
|
|
|
void BmpBlockUtil::expect_event(yaml_parser_t *parser, |
|
const yaml_event_type_e type) { |
|
yaml_event_t event; |
|
yaml_parser_parse(parser, &event); |
|
if (event.type != type) { |
|
error("Syntax error.\n"); |
|
} |
|
yaml_event_delete(&event); |
|
} |
|
|
|
void BmpBlockUtil::parse_config(yaml_parser_t *parser) { |
|
expect_event(parser, YAML_STREAM_START_EVENT); |
|
expect_event(parser, YAML_DOCUMENT_START_EVENT); |
|
parse_first_layer(parser); |
|
expect_event(parser, YAML_DOCUMENT_END_EVENT); |
|
expect_event(parser, YAML_STREAM_END_EVENT); |
|
} |
|
|
|
void BmpBlockUtil::parse_first_layer(yaml_parser_t *parser) { |
|
yaml_event_t event; |
|
string keyword; |
|
expect_event(parser, YAML_MAPPING_START_EVENT); |
|
for (;;) { |
|
yaml_parser_parse(parser, &event); |
|
switch (event.type) { |
|
case YAML_SCALAR_EVENT: |
|
keyword = (char*)event.data.scalar.value; |
|
if (keyword == "bmpblock") { |
|
parse_bmpblock(parser); |
|
} else if (keyword == "compression") { |
|
parse_compression(parser); |
|
} else if (keyword == "images") { |
|
parse_images(parser); |
|
} else if (keyword == "screens") { |
|
parse_screens(parser); |
|
} else if (keyword == "localizations") { |
|
parse_localizations(parser); |
|
} else if (keyword == "locale_index") { |
|
parse_locale_index(parser); |
|
} |
|
break; |
|
case YAML_MAPPING_END_EVENT: |
|
yaml_event_delete(&event); |
|
return; |
|
default: |
|
error("Syntax error in parsing config file.\n"); |
|
} |
|
yaml_event_delete(&event); |
|
} |
|
} |
|
|
|
void BmpBlockUtil::parse_bmpblock(yaml_parser_t *parser) { |
|
yaml_event_t event; |
|
yaml_parser_parse(parser, &event); |
|
if (event.type != YAML_SCALAR_EVENT) { |
|
error("Syntax error in parsing bmpblock.\n"); |
|
} |
|
string gotversion = (char*)event.data.scalar.value; |
|
if (gotversion != "2.0") { |
|
error("Unsupported version specified in config file (%s)\n", |
|
gotversion.c_str()); |
|
} |
|
yaml_event_delete(&event); |
|
} |
|
|
|
void BmpBlockUtil::parse_compression(yaml_parser_t *parser) { |
|
yaml_event_t event; |
|
yaml_parser_parse(parser, &event); |
|
if (event.type != YAML_SCALAR_EVENT) { |
|
error("Syntax error in parsing bmpblock.\n"); |
|
} |
|
char *comp_str = (char *)event.data.scalar.value; |
|
char *e = 0; |
|
uint32_t comp = (uint32_t)strtoul(comp_str, &e, 0); |
|
if (!*comp_str || (e && *e) || comp >= MAX_COMPRESS) { |
|
error("Invalid compression specified in config file (%d)\n", comp); |
|
} |
|
if (!set_compression_) { |
|
compression_ = comp; |
|
} |
|
yaml_event_delete(&event); |
|
} |
|
|
|
void BmpBlockUtil::parse_images(yaml_parser_t *parser) { |
|
yaml_event_t event; |
|
string image_name, image_filename; |
|
expect_event(parser, YAML_MAPPING_START_EVENT); |
|
for (;;) { |
|
yaml_parser_parse(parser, &event); |
|
switch (event.type) { |
|
case YAML_SCALAR_EVENT: |
|
image_name = (char*)event.data.scalar.value; |
|
yaml_event_delete(&event); |
|
yaml_parser_parse(parser, &event); |
|
if (event.type != YAML_SCALAR_EVENT) { |
|
error("Syntax error in parsing images.\n"); |
|
} |
|
image_filename = (char*)event.data.scalar.value; |
|
config_.image_names.push_back(image_name); |
|
config_.images_map[image_name] = ImageConfig(); |
|
config_.images_map[image_name].filename = image_filename; |
|
if (image_name == RENDER_HWID) { |
|
got_font_ = true; |
|
} |
|
if (image_name == RENDER_HWID_RTOL) { |
|
got_rtol_font_ = true; |
|
} |
|
break; |
|
case YAML_MAPPING_END_EVENT: |
|
yaml_event_delete(&event); |
|
return; |
|
default: |
|
error("Syntax error in parsing images.\n"); |
|
} |
|
yaml_event_delete(&event); |
|
} |
|
} |
|
|
|
void BmpBlockUtil::parse_layout(yaml_parser_t *parser, ScreenConfig &screen) { |
|
yaml_event_t event; |
|
int depth = 0, index1 = 0, index2 = 0; |
|
expect_event(parser, YAML_SEQUENCE_START_EVENT); |
|
for (;;) { |
|
yaml_parser_parse(parser, &event); |
|
switch (event.type) { |
|
case YAML_SEQUENCE_START_EVENT: |
|
depth++; |
|
break; |
|
case YAML_SCALAR_EVENT: |
|
switch (index2) { |
|
case 0: |
|
screen.data.images[index1].x = atoi((char*)event.data.scalar.value); |
|
break; |
|
case 1: |
|
screen.data.images[index1].y = atoi((char*)event.data.scalar.value); |
|
break; |
|
case 2: |
|
screen.image_names[index1] = (char*)event.data.scalar.value; |
|
// Detect the special case where we're rendering the HWID string |
|
// instead of displaying a bitmap. The image name may not |
|
// exist in the list of images (v1.1), but we will still need an |
|
// ImageInfo struct to remember where to draw the text. |
|
// Note that v1.2 requires that the image name DOES exist, because |
|
// the corresponding file is used to hold the font glpyhs. |
|
if (render_hwid_) { |
|
if (screen.image_names[index1] == RENDER_HWID) { |
|
config_.images_map[RENDER_HWID].data.tag = TAG_HWID; |
|
if (support_font_ && !got_font_) |
|
error("Font required in 'image:' section for %s\n", |
|
RENDER_HWID); |
|
} else if (screen.image_names[index1] == RENDER_HWID_RTOL) { |
|
config_.images_map[RENDER_HWID_RTOL].data.tag = TAG_HWID_RTOL; |
|
if (support_font_ && !got_rtol_font_) |
|
error("Font required in 'image:' section for %s\n", |
|
RENDER_HWID_RTOL); |
|
} |
|
} |
|
break; |
|
default: |
|
error("Syntax error in parsing layout\n"); |
|
} |
|
index2++; |
|
break; |
|
case YAML_SEQUENCE_END_EVENT: |
|
if (depth == 1) { |
|
index1++; |
|
index2 = 0; |
|
} else if (depth == 0) { |
|
yaml_event_delete(&event); |
|
return; |
|
} |
|
depth--; |
|
break; |
|
default: |
|
error("Syntax error in paring layout.\n"); |
|
} |
|
yaml_event_delete(&event); |
|
} |
|
} |
|
|
|
void BmpBlockUtil::parse_screens(yaml_parser_t *parser) { |
|
yaml_event_t event; |
|
string screen_name; |
|
expect_event(parser, YAML_MAPPING_START_EVENT); |
|
for (;;) { |
|
yaml_parser_parse(parser, &event); |
|
switch (event.type) { |
|
case YAML_SCALAR_EVENT: |
|
screen_name = (char*)event.data.scalar.value; |
|
config_.screens_map[screen_name] = ScreenConfig(); |
|
parse_layout(parser, config_.screens_map[screen_name]); |
|
break; |
|
case YAML_MAPPING_END_EVENT: |
|
yaml_event_delete(&event); |
|
return; |
|
default: |
|
error("Syntax error in parsing screens.\n"); |
|
} |
|
yaml_event_delete(&event); |
|
} |
|
} |
|
|
|
void BmpBlockUtil::parse_localizations(yaml_parser_t *parser) { |
|
yaml_event_t event; |
|
int depth = 0, index = 0; |
|
expect_event(parser, YAML_SEQUENCE_START_EVENT); |
|
for (;;) { |
|
yaml_parser_parse(parser, &event); |
|
switch (event.type) { |
|
case YAML_SEQUENCE_START_EVENT: |
|
config_.localizations.push_back(vector<string>()); |
|
depth++; |
|
break; |
|
case YAML_SCALAR_EVENT: |
|
config_.localizations[index].push_back((char*)event.data.scalar.value); |
|
break; |
|
case YAML_SEQUENCE_END_EVENT: |
|
if (depth == 1) { |
|
index++; |
|
} else if (depth == 0) { |
|
yaml_event_delete(&event); |
|
return; |
|
} |
|
depth--; |
|
break; |
|
default: |
|
error("Syntax error in parsing localizations.\n"); |
|
} |
|
yaml_event_delete(&event); |
|
} |
|
} |
|
|
|
void BmpBlockUtil::parse_locale_index(yaml_parser_t *parser) { |
|
yaml_event_t event; |
|
expect_event(parser, YAML_SEQUENCE_START_EVENT); |
|
for (;;) { |
|
yaml_parser_parse(parser, &event); |
|
switch (event.type) { |
|
case YAML_SCALAR_EVENT: |
|
config_.locale_names.append((char*)event.data.scalar.value); |
|
config_.locale_names.append(1, (char)'\0'); // '\0' to delimit |
|
break; |
|
case YAML_SEQUENCE_END_EVENT: |
|
yaml_event_delete(&event); |
|
config_.locale_names.append(1, (char)'\0'); // double '\0' to terminate |
|
return; |
|
default: |
|
error("Syntax error in parsing localizations.\n"); |
|
} |
|
} |
|
} |
|
|
|
void BmpBlockUtil::load_all_image_files() { |
|
for (unsigned int i = 0; i < config_.image_names.size(); i++) { |
|
StrImageConfigMap::iterator it = |
|
config_.images_map.find(config_.image_names[i]); |
|
if (debug_) { |
|
printf("loading image \"%s\" from \"%s\"\n", |
|
config_.image_names[i].c_str(), |
|
it->second.filename.c_str()); |
|
} |
|
const string &content = read_image_file(it->second.filename.c_str()); |
|
it->second.raw_content = content; |
|
it->second.data.original_size = content.size(); |
|
it->second.data.format = |
|
identify_image_type(content.c_str(), |
|
(uint32_t)content.size(), &it->second.data); |
|
if (FORMAT_INVALID == it->second.data.format) { |
|
error("Unsupported image format in %s\n", it->second.filename.c_str()); |
|
} |
|
switch(compression_) { |
|
case COMPRESS_NONE: |
|
it->second.data.compression = compression_; |
|
it->second.compressed_content = content; |
|
it->second.data.compressed_size = content.size(); |
|
break; |
|
case COMPRESS_EFIv1: |
|
{ |
|
// The content will always compress smaller (so sez the docs). |
|
uint32_t tmpsize = content.size(); |
|
uint8_t *tmpbuf = (uint8_t *)malloc(tmpsize); |
|
// The size of the compressed content is also returned. |
|
if (EFI_SUCCESS != EfiCompress((uint8_t *)content.c_str(), tmpsize, |
|
tmpbuf, &tmpsize)) { |
|
error("Unable to compress!\n"); |
|
} |
|
it->second.data.compression = compression_; |
|
it->second.compressed_content.assign((const char *)tmpbuf, tmpsize); |
|
it->second.data.compressed_size = tmpsize; |
|
free(tmpbuf); |
|
} |
|
break; |
|
case COMPRESS_LZMA1: |
|
{ |
|
// Calculate the worst case of buffer size. |
|
uint32_t tmpsize = lzma_stream_buffer_bound(content.size()); |
|
uint8_t *tmpbuf = (uint8_t *)malloc(tmpsize); |
|
lzma_stream stream = LZMA_STREAM_INIT; |
|
lzma_options_lzma options; |
|
lzma_ret result; |
|
|
|
lzma_lzma_preset(&options, 9); |
|
result = lzma_alone_encoder(&stream, &options); |
|
if (result != LZMA_OK) { |
|
error("Unable to initialize easy encoder (error: %d)!\n", result); |
|
} |
|
|
|
stream.next_in = (uint8_t *)content.data(); |
|
stream.avail_in = content.size(); |
|
stream.next_out = tmpbuf; |
|
stream.avail_out = tmpsize; |
|
result = lzma_code(&stream, LZMA_FINISH); |
|
if (result != LZMA_STREAM_END) { |
|
error("Unable to encode data (error: %d)!\n", result); |
|
} |
|
|
|
it->second.data.compression = compression_; |
|
it->second.compressed_content.assign((const char *)tmpbuf, |
|
tmpsize - stream.avail_out); |
|
it->second.data.compressed_size = tmpsize - stream.avail_out; |
|
lzma_end(&stream); |
|
free(tmpbuf); |
|
} |
|
break; |
|
default: |
|
error("Unsupported compression method attempted.\n"); |
|
} |
|
} |
|
} |
|
|
|
const string BmpBlockUtil::read_image_file(const char *filename) { |
|
string content; |
|
vector<char> buffer; |
|
|
|
FILE *fp = fopen(filename, "rb"); |
|
if (!fp) { |
|
perror(filename); |
|
exit(errno); |
|
} |
|
|
|
if (fseek(fp, 0, SEEK_END) == 0) { |
|
buffer.resize(ftell(fp)); |
|
rewind(fp); |
|
} |
|
|
|
if (!buffer.empty()) { |
|
if(fread(&buffer[0], buffer.size(), 1, fp) != 1) { |
|
perror(filename); |
|
buffer.clear(); |
|
} else { |
|
content.assign(buffer.begin(), buffer.end()); |
|
} |
|
} |
|
|
|
fclose(fp); |
|
return content; |
|
} |
|
|
|
void BmpBlockUtil::fill_bmpblock_header() { |
|
memset(&config_.header, '\0', sizeof(config_.header)); |
|
memcpy(&config_.header.signature, BMPBLOCK_SIGNATURE, |
|
BMPBLOCK_SIGNATURE_SIZE); |
|
config_.header.major_version = major_version_; |
|
config_.header.minor_version = minor_version_; |
|
config_.header.number_of_localizations = config_.localizations.size(); |
|
config_.header.number_of_screenlayouts = config_.localizations[0].size(); |
|
// NOTE: this is part of the yaml consistency check |
|
for (unsigned int i = 1; i < config_.localizations.size(); ++i) { |
|
assert(config_.header.number_of_screenlayouts == |
|
config_.localizations[i].size()); |
|
} |
|
config_.header.number_of_imageinfos = config_.images_map.size(); |
|
config_.header.locale_string_offset = 0; // Filled by pack_bmpblock() |
|
} |
|
|
|
void BmpBlockUtil::pack_bmpblock() { |
|
bmpblock_.clear(); |
|
|
|
/* Compute the ImageInfo offsets from start of BMPBLOCK. */ |
|
uint32_t current_offset = sizeof(BmpBlockHeader) + |
|
sizeof(ScreenLayout) * (config_.header.number_of_localizations * |
|
config_.header.number_of_screenlayouts); |
|
for (StrImageConfigMap::iterator it = config_.images_map.begin(); |
|
it != config_.images_map.end(); |
|
++it) { |
|
it->second.offset = current_offset; |
|
if (debug_) |
|
printf(" \"%s\": filename=\"%s\" offset=0x%x tag=%d fmt=%d\n", |
|
it->first.c_str(), |
|
it->second.filename.c_str(), |
|
it->second.offset, |
|
it->second.data.tag, |
|
it->second.data.format); |
|
current_offset += sizeof(ImageInfo) + |
|
it->second.data.compressed_size; |
|
/* Make it 4-byte aligned. */ |
|
if ((current_offset & 3) > 0) { |
|
current_offset = (current_offset & ~3) + 4; |
|
} |
|
} |
|
/* And leave room for the locale_index string */ |
|
if (config_.locale_names.size()) { |
|
config_.header.locale_string_offset = current_offset; |
|
current_offset += config_.locale_names.size(); |
|
} |
|
|
|
bmpblock_.resize(current_offset); |
|
|
|
/* Fill BmpBlockHeader struct. */ |
|
string::iterator current_filled = bmpblock_.begin(); |
|
std::copy(reinterpret_cast<char*>(&config_.header), |
|
reinterpret_cast<char*>(&config_.header + 1), |
|
current_filled); |
|
current_filled += sizeof(config_.header); |
|
current_offset = sizeof(config_.header); |
|
|
|
/* Fill all ScreenLayout structs. */ |
|
for (unsigned int i = 0; i < config_.localizations.size(); ++i) { |
|
for (unsigned int j = 0; j < config_.localizations[i].size(); ++j) { |
|
ScreenConfig &screen = config_.screens_map[config_.localizations[i][j]]; |
|
for (unsigned int k = 0; |
|
k < MAX_IMAGE_IN_LAYOUT && !screen.image_names[k].empty(); |
|
++k) { |
|
if (config_.images_map.find(screen.image_names[k]) == |
|
config_.images_map.end()) { |
|
error("Invalid image name \"%s\"\n", screen.image_names[k].c_str()); |
|
} |
|
if (debug_) |
|
printf("i=%d j=%d k=%d=\"%s\" (%d,%d) ofs=%x\n", i,j,k, |
|
screen.image_names[k].c_str(), |
|
screen.data.images[k].x, screen.data.images[k].y, |
|
config_.images_map[screen.image_names[k]].offset |
|
); |
|
screen.data.images[k].image_info_offset = |
|
config_.images_map[screen.image_names[k]].offset; |
|
} |
|
std::copy(reinterpret_cast<char*>(&screen.data), |
|
reinterpret_cast<char*>(&screen.data + 1), |
|
current_filled); |
|
current_filled += sizeof(screen.data); |
|
if (debug_) |
|
printf("S: current offset is 0x%08x\n", current_offset); |
|
current_offset += sizeof(screen.data); |
|
} |
|
} |
|
|
|
/* Fill all ImageInfo structs and image contents. */ |
|
for (StrImageConfigMap::iterator it = config_.images_map.begin(); |
|
it != config_.images_map.end(); |
|
++it) { |
|
current_filled = bmpblock_.begin() + it->second.offset; |
|
current_offset = it->second.offset; |
|
if (debug_) |
|
printf("I0: current offset is 0x%08x\n", current_offset); |
|
std::copy(reinterpret_cast<char*>(&it->second.data), |
|
reinterpret_cast<char*>(&it->second.data + 1), |
|
current_filled); |
|
current_filled += sizeof(it->second.data); |
|
current_offset += sizeof(it->second.data); |
|
if (debug_) |
|
printf("I1: current offset is 0x%08x (len %ld)\n", |
|
current_offset, it->second.compressed_content.length()); |
|
std::copy(it->second.compressed_content.begin(), |
|
it->second.compressed_content.end(), |
|
current_filled); |
|
} |
|
|
|
/* Fill in locale_names. */ |
|
if (config_.header.locale_string_offset) { |
|
current_offset = config_.header.locale_string_offset; |
|
current_filled = bmpblock_.begin() + current_offset; |
|
if (debug_) |
|
printf("locale_names: offset 0x%08x (len %ld)\n", |
|
current_offset, config_.locale_names.size()); |
|
std::copy(config_.locale_names.begin(), |
|
config_.locale_names.end(), |
|
current_filled); |
|
} |
|
} |
|
|
|
void BmpBlockUtil::write_to_bmpblock(const char *filename) { |
|
assert(!bmpblock_.empty()); |
|
|
|
FILE *fp = fopen(filename, "wb"); |
|
if (!fp) { |
|
perror(filename); |
|
exit(errno); |
|
} |
|
|
|
int r = fwrite(bmpblock_.c_str(), bmpblock_.size(), 1, fp); |
|
fclose(fp); |
|
if (r != 1) { |
|
perror(filename); |
|
exit(errno); |
|
} |
|
} |
|
|
|
} // namespace vboot_reference |
|
|
|
#ifndef FOR_LIBRARY |
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
// Command line utilities. |
|
|
|
extern "C" { |
|
#include "bmpblk_util.h" |
|
} |
|
|
|
using vboot_reference::BmpBlockUtil; |
|
|
|
// utility function: provide usage of this utility and exit. |
|
static void usagehelp_exit(const char *prog_name) { |
|
printf( |
|
"\n" |
|
"To create a new BMPBLOCK file using config from YAML file:\n" |
|
"\n" |
|
" %s [-z NUM] -c YAML BMPBLOCK\n" |
|
"\n" |
|
" -z NUM = compression algorithm to use\n" |
|
" 0 = none\n" |
|
" 1 = EFIv1\n" |
|
" 2 = LZMA1\n" |
|
"\n", prog_name); |
|
printf( |
|
"To display the contents of a BMPBLOCK:\n" |
|
"\n" |
|
" %s [-y] BMPBLOCK\n" |
|
"\n" |
|
" -y = display as yaml\n" |
|
"\n", prog_name); |
|
printf( |
|
"To unpack a BMPBLOCK file:\n" |
|
"\n" |
|
" %s -x [-d DIR] [-f] BMPBLOCK\n" |
|
"\n" |
|
" -d DIR = directory to use (default '.')\n" |
|
" -f = force overwriting existing files\n" |
|
"\n", prog_name); |
|
exit(1); |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////// |
|
// main |
|
|
|
int main(int argc, char *argv[]) { |
|
|
|
const char *prog_name = strrchr(argv[0], '/'); |
|
if (prog_name) |
|
prog_name++; |
|
else |
|
prog_name = argv[0]; |
|
|
|
int overwrite = 0, extract_mode = 0; |
|
int compression = 0; |
|
int set_compression = 0; |
|
const char *config_fn = 0, *bmpblock_fn = 0, *extract_dir = "."; |
|
int show_as_yaml = 0; |
|
bool debug = false; |
|
|
|
int opt; |
|
opterr = 0; // quiet |
|
int errorcnt = 0; |
|
char *e = 0; |
|
while ((opt = getopt(argc, argv, ":c:xz:fd:yD")) != -1) { |
|
switch (opt) { |
|
case 'c': |
|
config_fn = optarg; |
|
break; |
|
case 'x': |
|
extract_mode = 1; |
|
break; |
|
case 'y': |
|
show_as_yaml = 1; |
|
break; |
|
case 'z': |
|
compression = (int)strtoul(optarg, &e, 0); |
|
if (!*optarg || (e && *e)) { |
|
fprintf(stderr, "%s: invalid argument to -%c: \"%s\"\n", |
|
prog_name, opt, optarg); |
|
errorcnt++; |
|
} |
|
if (compression >= MAX_COMPRESS) { |
|
fprintf(stderr, "%s: compression type must be less than %d\n", |
|
prog_name, MAX_COMPRESS); |
|
errorcnt++; |
|
} |
|
set_compression = 1; |
|
break; |
|
case 'f': |
|
overwrite = 1; |
|
break; |
|
case 'd': |
|
extract_dir= optarg; |
|
break; |
|
case 'D': |
|
debug = true; |
|
break; |
|
case ':': |
|
fprintf(stderr, "%s: missing argument to -%c\n", |
|
prog_name, optopt); |
|
errorcnt++; |
|
break; |
|
default: |
|
fprintf(stderr, "%s: unrecognized switch: -%c\n", |
|
prog_name, optopt); |
|
errorcnt++; |
|
break; |
|
} |
|
} |
|
argc -= optind; |
|
argv += optind; |
|
|
|
if (argc >= 1) { |
|
bmpblock_fn = argv[0]; |
|
} else { |
|
fprintf(stderr, "%s: missing BMPBLOCK name\n", prog_name); |
|
errorcnt++; |
|
} |
|
|
|
if (errorcnt) |
|
usagehelp_exit(prog_name); |
|
|
|
BmpBlockUtil util(debug); |
|
|
|
if (config_fn) { |
|
if (set_compression) |
|
util.force_compression(compression); |
|
util.load_from_config(config_fn); |
|
util.pack_bmpblock(); |
|
util.write_to_bmpblock(bmpblock_fn); |
|
} |
|
|
|
else if (extract_mode) { |
|
return dump_bmpblock(bmpblock_fn, 1, extract_dir, overwrite); |
|
} else { |
|
return dump_bmpblock(bmpblock_fn, show_as_yaml, 0, 0); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
#endif // FOR_LIBRARY
|
|
|