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

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

Powered by Google App Engine
This is Rietveld 408576698