Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(115)

Side by Side Diff: utility/bmpblk_utility.cc

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

Powered by Google App Engine
This is Rietveld 408576698