mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-11-24 02:05:01 +00:00
This change adds an additional (optional) section to the .yaml file which can enumerate the names of the locales. If present, these names will be appended to the end of the bmpblock and the (new) locale_string_offset field in the BmpBlockHeader will point to it. The names are encoded as a series of null-terminated ASCII strings. The end of the series is indicated by an extra null (for example, "en_US\0fr\0\0" names two locales). The BIOS does not use this information. Factory or OOBE could use it to select the initiale locale for the BIOS screens from the list of locales included in the BmpBlock. BUG=chrome-os-partner:3868 TEST=none Change-Id: I34fd9ece27343d56ec43772de975ac6f2ad7c9a6 Reviewed-on: http://gerrit.chromium.org/gerrit/1156 Reviewed-by: Hung-Te Lin <hungte@chromium.org> Tested-by: Bill Richardson <wfrichar@chromium.org>
815 lines
26 KiB
C++
815 lines
26 KiB
C++
// 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 "bmpblk_utility.h"
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <getopt.h>
|
|
#include <lzma.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <yaml.h>
|
|
|
|
extern "C" {
|
|
#include "eficompress.h"
|
|
}
|
|
|
|
|
|
/* BMP header, used to validate image requirements
|
|
* See http://en.wikipedia.org/wiki/BMP_file_format
|
|
*/
|
|
typedef struct {
|
|
uint8_t CharB; // must be 'B'
|
|
uint8_t CharM; // must be 'M'
|
|
uint32_t Size;
|
|
uint16_t Reserved[2];
|
|
uint32_t ImageOffset;
|
|
uint32_t HeaderSize;
|
|
uint32_t PixelWidth;
|
|
uint32_t PixelHeight;
|
|
uint16_t Planes; // Must be 1 for x86
|
|
uint16_t BitPerPixel; // 1, 4, 8, or 24 for x86
|
|
uint32_t CompressionType; // 0 (none) for x86, 1 (RLE) for arm
|
|
uint32_t ImageSize;
|
|
uint32_t XPixelsPerMeter;
|
|
uint32_t YPixelsPerMeter;
|
|
uint32_t NumberOfColors;
|
|
uint32_t ImportantColors;
|
|
} __attribute__((packed)) BMP_IMAGE_HEADER;
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
|
|
|
|
// HEY: Check the yaml file for self-consistency now. 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\n",
|
|
it->first.c_str(),
|
|
it->second.filename.c_str(),
|
|
it->second.offset,
|
|
it->second.data.tag);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
// TODO(wfrichar): print debugging info about locale_names
|
|
}
|
|
}
|
|
|
|
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 == "1.1") {
|
|
render_hwid_ = true;
|
|
} else if (gotversion == "1.0") {
|
|
minor_version_ = 0;
|
|
render_hwid_ = false;
|
|
} else {
|
|
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;
|
|
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 shouldn't
|
|
// exist in the list of images, but we will still need an
|
|
// ImageInfo struct to remember where to draw the text.
|
|
// Note that if the image name DOES exist, we still will won't
|
|
// display it (yet). Future versions may use that image to hold the
|
|
// font glpyhs, which is why we pass it around now.
|
|
if (render_hwid_) {
|
|
if (screen.image_names[index1] == RENDER_HWID) {
|
|
config_.images_map[RENDER_HWID].data.tag = TAG_HWID;
|
|
} else if (screen.image_names[index1] == RENDER_HWID_RTOL) {
|
|
config_.images_map[RENDER_HWID_RTOL].data.tag = TAG_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 = get_image_format(content);
|
|
switch (it->second.data.format) {
|
|
case FORMAT_BMP:
|
|
it->second.data.width = get_bmp_image_width(it->second.raw_content);
|
|
it->second.data.height = get_bmp_image_height(it->second.raw_content);
|
|
break;
|
|
default:
|
|
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;
|
|
}
|
|
|
|
ImageFormat BmpBlockUtil::get_image_format(const string content) {
|
|
if (content.size() < sizeof(BMP_IMAGE_HEADER))
|
|
return FORMAT_INVALID;
|
|
const BMP_IMAGE_HEADER *hdr = (const BMP_IMAGE_HEADER *)content.c_str();
|
|
|
|
if (hdr->CharB != 'B' || hdr->CharM != 'M' ||
|
|
hdr->Planes != 1 ||
|
|
(hdr->CompressionType != 0 && hdr->CompressionType != 1) ||
|
|
(hdr->BitPerPixel != 1 && hdr->BitPerPixel != 4 &&
|
|
hdr->BitPerPixel != 8 && hdr->BitPerPixel != 24))
|
|
return FORMAT_INVALID;
|
|
|
|
return FORMAT_BMP;
|
|
}
|
|
|
|
uint32_t BmpBlockUtil::get_bmp_image_width(const string content) {
|
|
const BMP_IMAGE_HEADER *hdr = (const BMP_IMAGE_HEADER *)content.c_str();
|
|
return hdr->PixelWidth;
|
|
}
|
|
|
|
uint32_t BmpBlockUtil::get_bmp_image_height(const string content) {
|
|
const BMP_IMAGE_HEADER *hdr = (const BMP_IMAGE_HEADER *)content.c_str();
|
|
return hdr->PixelHeight;
|
|
}
|
|
|
|
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();
|
|
// HEY: 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\n",
|
|
it->first.c_str(),
|
|
it->second.filename.c_str(),
|
|
it->second.offset,
|
|
it->second.data.tag);
|
|
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
|
|
|
|
#ifdef WITH_UTIL_MAIN
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// 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 // WITH_UTIL_MAIN
|