blob: 8605dde046fa4d699197a7d238ef4ea5db69875b [file] [log] [blame]
Bill Richardsond55085d2011-02-04 15:01:37 -08001// 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>
Tom Wai-Hong Tamee2bc912011-02-17 12:58:58 +080013#include <lzma.h>
Bill Richardsond55085d2011-02-04 15:01:37 -080014#include <stdarg.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <yaml.h>
19
Bill Richardson61362d62011-02-14 10:28:03 -080020extern "C" {
21#include "eficompress.h"
22}
23
24
Bill Richardsonfc05bb82011-02-08 15:03:36 -080025/* BMP header, used to validate image requirements
26 * See http://en.wikipedia.org/wiki/BMP_file_format
27 */
28typedef struct {
29 uint8_t CharB; // must be 'B'
30 uint8_t CharM; // must be 'M'
31 uint32_t Size;
32 uint16_t Reserved[2];
33 uint32_t ImageOffset;
34 uint32_t HeaderSize;
35 uint32_t PixelWidth;
36 uint32_t PixelHeight;
37 uint16_t Planes; // Must be 1 for x86
38 uint16_t BitPerPixel; // 1, 4, 8, or 24 for x86
Tom Wai-Hong Tama985ed42011-02-14 16:08:49 +080039 uint32_t CompressionType; // 0 (none) for x86, 1 (RLE) for arm
Bill Richardsonfc05bb82011-02-08 15:03:36 -080040 uint32_t ImageSize;
41 uint32_t XPixelsPerMeter;
42 uint32_t YPixelsPerMeter;
43 uint32_t NumberOfColors;
44 uint32_t ImportantColors;
45} __attribute__((packed)) BMP_IMAGE_HEADER;
46
Bill Richardsond55085d2011-02-04 15:01:37 -080047
48static void error(const char *format, ...) {
49 va_list ap;
50 va_start(ap, format);
51 fprintf(stderr, "ERROR: ");
52 vfprintf(stderr, format, ap);
53 va_end(ap);
54 exit(1);
55}
56
57///////////////////////////////////////////////////////////////////////
58// BmpBlock Utility implementation
59
60namespace vboot_reference {
61
62BmpBlockUtil::BmpBlockUtil() {
63 initialize();
64}
65
66BmpBlockUtil::~BmpBlockUtil() {
67}
68
69void BmpBlockUtil::initialize() {
70 config_.config_filename.clear();
71 memset(&config_.header, '\0', BMPBLOCK_SIGNATURE_SIZE);
72 config_.images_map.clear();
73 config_.screens_map.clear();
74 config_.localizations.clear();
75 bmpblock_.clear();
Bill Richardson61362d62011-02-14 10:28:03 -080076 set_compression_ = false;
77 compression_ = COMPRESS_NONE;
78}
79
80void BmpBlockUtil::force_compression(uint32_t compression) {
81 compression_ = compression;
82 set_compression_ = true;
Bill Richardsond55085d2011-02-04 15:01:37 -080083}
84
85void BmpBlockUtil::load_from_config(const char *filename) {
86 load_yaml_config(filename);
87 fill_bmpblock_header();
88 load_all_image_files();
89 fill_all_image_infos();
90}
91
92void BmpBlockUtil::load_yaml_config(const char *filename) {
93 yaml_parser_t parser;
94
95 config_.config_filename = filename;
96 config_.images_map.clear();
97 config_.screens_map.clear();
98 config_.localizations.clear();
99
100 FILE *fp = fopen(filename, "rb");
101 if (!fp) {
102 perror(filename);
103 exit(errno);
104 }
105
106 yaml_parser_initialize(&parser);
107 yaml_parser_set_input_file(&parser, fp);
108 parse_config(&parser);
109 yaml_parser_delete(&parser);
110 fclose(fp);
111}
112
113void BmpBlockUtil::expect_event(yaml_parser_t *parser,
114 const yaml_event_type_e type) {
115 yaml_event_t event;
116 yaml_parser_parse(parser, &event);
117 if (event.type != type) {
118 error("Syntax error.\n");
119 }
120 yaml_event_delete(&event);
121}
122
123void BmpBlockUtil::parse_config(yaml_parser_t *parser) {
124 expect_event(parser, YAML_STREAM_START_EVENT);
125 expect_event(parser, YAML_DOCUMENT_START_EVENT);
126 parse_first_layer(parser);
127 expect_event(parser, YAML_DOCUMENT_END_EVENT);
128 expect_event(parser, YAML_STREAM_END_EVENT);
129}
130
131void BmpBlockUtil::parse_first_layer(yaml_parser_t *parser) {
132 yaml_event_t event;
133 string keyword;
134 expect_event(parser, YAML_MAPPING_START_EVENT);
135 for (;;) {
136 yaml_parser_parse(parser, &event);
137 switch (event.type) {
138 case YAML_SCALAR_EVENT:
139 keyword = (char*)event.data.scalar.value;
140 if (keyword == "bmpblock") {
141 parse_bmpblock(parser);
142 } else if (keyword == "images") {
143 parse_images(parser);
144 } else if (keyword == "screens") {
145 parse_screens(parser);
146 } else if (keyword == "localizations") {
147 parse_localizations(parser);
148 }
149 break;
150 case YAML_MAPPING_END_EVENT:
151 yaml_event_delete(&event);
152 return;
153 default:
154 error("Syntax error in parsing config file.\n");
155 }
156 yaml_event_delete(&event);
157 }
158}
159
160void BmpBlockUtil::parse_bmpblock(yaml_parser_t *parser) {
161 yaml_event_t event;
162 yaml_parser_parse(parser, &event);
163 if (event.type != YAML_SCALAR_EVENT) {
164 error("Syntax error in parsing bmpblock.\n");
165 }
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800166 char wantversion[20];
167 sprintf(wantversion, "%d.%d",
168 BMPBLOCK_MAJOR_VERSION,
169 BMPBLOCK_MINOR_VERSION);
170 string gotversion = (char*)event.data.scalar.value;
171 if (gotversion != wantversion) {
172 error("Invalid version specified in config file\n");
173 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800174 yaml_event_delete(&event);
175}
176
177void BmpBlockUtil::parse_images(yaml_parser_t *parser) {
178 yaml_event_t event;
179 string image_name, image_filename;
180 expect_event(parser, YAML_MAPPING_START_EVENT);
181 for (;;) {
182 yaml_parser_parse(parser, &event);
183 switch (event.type) {
184 case YAML_SCALAR_EVENT:
185 image_name = (char*)event.data.scalar.value;
186 yaml_event_delete(&event);
187 yaml_parser_parse(parser, &event);
188 if (event.type != YAML_SCALAR_EVENT) {
189 error("Syntax error in parsing images.\n");
190 }
191 image_filename = (char*)event.data.scalar.value;
Bill Richardsonf82f9412011-02-17 08:56:33 -0800192 config_.image_names.push_back(image_name);
Bill Richardsond55085d2011-02-04 15:01:37 -0800193 config_.images_map[image_name] = ImageConfig();
194 config_.images_map[image_name].filename = image_filename;
195 break;
196 case YAML_MAPPING_END_EVENT:
197 yaml_event_delete(&event);
198 return;
199 default:
200 error("Syntax error in parsing images.\n");
201 }
202 yaml_event_delete(&event);
203 }
204}
205
206void BmpBlockUtil::parse_layout(yaml_parser_t *parser, ScreenConfig &screen) {
207 yaml_event_t event;
208 string screen_name;
209 int depth = 0, index1 = 0, index2 = 0;
210 expect_event(parser, YAML_SEQUENCE_START_EVENT);
211 for (;;) {
212 yaml_parser_parse(parser, &event);
213 switch (event.type) {
214 case YAML_SEQUENCE_START_EVENT:
215 depth++;
216 break;
217 case YAML_SCALAR_EVENT:
218 switch (index2) {
219 case 0:
220 screen.data.images[index1].x = atoi((char*)event.data.scalar.value);
221 break;
222 case 1:
223 screen.data.images[index1].y = atoi((char*)event.data.scalar.value);
224 break;
225 case 2:
226 screen.image_names[index1] = (char*)event.data.scalar.value;
227 break;
228 default:
229 error("Syntax error in parsing layout.\n");
230 }
231 index2++;
232 break;
233 case YAML_SEQUENCE_END_EVENT:
234 if (depth == 1) {
235 index1++;
236 index2 = 0;
237 } else if (depth == 0) {
238 yaml_event_delete(&event);
239 return;
240 }
241 depth--;
242 break;
243 default:
244 error("Syntax error in paring layout.\n");
245 }
246 yaml_event_delete(&event);
247 }
248}
249
250void BmpBlockUtil::parse_screens(yaml_parser_t *parser) {
251 yaml_event_t event;
252 string screen_name;
253 expect_event(parser, YAML_MAPPING_START_EVENT);
254 for (;;) {
255 yaml_parser_parse(parser, &event);
256 switch (event.type) {
257 case YAML_SCALAR_EVENT:
258 screen_name = (char*)event.data.scalar.value;
259 config_.screens_map[screen_name] = ScreenConfig();
260 parse_layout(parser, config_.screens_map[screen_name]);
261 break;
262 case YAML_MAPPING_END_EVENT:
263 yaml_event_delete(&event);
264 return;
265 default:
266 error("Syntax error in parsing screens.\n");
267 }
268 yaml_event_delete(&event);
269 }
270}
271
272void BmpBlockUtil::parse_localizations(yaml_parser_t *parser) {
273 yaml_event_t event;
274 int depth = 0, index = 0;
275 expect_event(parser, YAML_SEQUENCE_START_EVENT);
276 for (;;) {
277 yaml_parser_parse(parser, &event);
278 switch (event.type) {
279 case YAML_SEQUENCE_START_EVENT:
280 config_.localizations.push_back(vector<string>());
281 depth++;
282 break;
283 case YAML_SCALAR_EVENT:
284 config_.localizations[index].push_back((char*)event.data.scalar.value);
285 break;
286 case YAML_SEQUENCE_END_EVENT:
287 if (depth == 1) {
288 index++;
289 } else if (depth == 0) {
290 yaml_event_delete(&event);
291 return;
292 }
293 depth--;
294 break;
295 default:
296 error("Syntax error in parsing localizations.\n");
297 }
298 yaml_event_delete(&event);
299 }
300}
301
302void BmpBlockUtil::load_all_image_files() {
303 for (StrImageConfigMap::iterator it = config_.images_map.begin();
304 it != config_.images_map.end();
305 ++it) {
306 const string &content = read_image_file(it->second.filename.c_str());
307 it->second.raw_content = content;
308 it->second.data.original_size = content.size();
Bill Richardson61362d62011-02-14 10:28:03 -0800309 switch(compression_) {
310 case COMPRESS_NONE:
311 it->second.data.compression = compression_;
312 it->second.compressed_content = content;
313 it->second.data.compressed_size = content.size();
314 break;
315 case COMPRESS_EFIv1:
316 {
317 // The content will always compress smaller (so sez the docs).
318 uint32_t tmpsize = content.size();
319 uint8_t *tmpbuf = (uint8_t *)malloc(tmpsize);
320 // The size of the compressed content is also returned.
321 if (EFI_SUCCESS != EfiCompress((uint8_t *)content.c_str(), tmpsize,
322 tmpbuf, &tmpsize)) {
323 error("Unable to compress!\n");
324 }
325 it->second.data.compression = compression_;
326 it->second.compressed_content.assign((const char *)tmpbuf, tmpsize);
327 it->second.data.compressed_size = tmpsize;
328 free(tmpbuf);
329 }
330 break;
Tom Wai-Hong Tamee2bc912011-02-17 12:58:58 +0800331 case COMPRESS_LZMA1:
332 {
333 // Calculate the worst case of buffer size.
334 uint32_t tmpsize = lzma_stream_buffer_bound(content.size());
335 uint8_t *tmpbuf = (uint8_t *)malloc(tmpsize);
336 lzma_stream stream = LZMA_STREAM_INIT;
337 lzma_options_lzma options;
338 lzma_ret result;
339
340 lzma_lzma_preset(&options, 9);
341 result = lzma_alone_encoder(&stream, &options);
342 if (result != LZMA_OK) {
343 error("Unable to initialize easy encoder (error: %d)!\n", result);
344 }
345
346 stream.next_in = (uint8_t *)content.data();
347 stream.avail_in = content.size();
348 stream.next_out = tmpbuf;
349 stream.avail_out = tmpsize;
350 result = lzma_code(&stream, LZMA_FINISH);
351 if (result != LZMA_STREAM_END) {
352 error("Unable to encode data (error: %d)!\n", result);
353 }
354
355 it->second.data.compression = compression_;
356 it->second.compressed_content.assign((const char *)tmpbuf,
357 tmpsize - stream.avail_out);
358 it->second.data.compressed_size = tmpsize - stream.avail_out;
359 lzma_end(&stream);
360 free(tmpbuf);
361 }
362 break;
Bill Richardson61362d62011-02-14 10:28:03 -0800363 default:
364 error("Unsupported compression method attempted.\n");
365 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800366 }
367}
368
369const string BmpBlockUtil::read_image_file(const char *filename) {
370 string content;
371 vector<char> buffer;
372
373 FILE *fp = fopen(filename, "rb");
374 if (!fp) {
375 perror(filename);
376 exit(errno);
377 }
378
379 if (fseek(fp, 0, SEEK_END) == 0) {
380 buffer.resize(ftell(fp));
381 rewind(fp);
382 }
383
384 if (!buffer.empty()) {
385 if(fread(&buffer[0], buffer.size(), 1, fp) != 1) {
386 perror(filename);
387 buffer.clear();
388 } else {
389 content.assign(buffer.begin(), buffer.end());
390 }
391 }
392
393 fclose(fp);
394 return content;
395}
396
397ImageFormat BmpBlockUtil::get_image_format(const string content) {
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800398 if (content.size() < sizeof(BMP_IMAGE_HEADER))
Bill Richardsond55085d2011-02-04 15:01:37 -0800399 return FORMAT_INVALID;
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800400 const BMP_IMAGE_HEADER *hdr = (const BMP_IMAGE_HEADER *)content.c_str();
401
402 if (hdr->CharB != 'B' || hdr->CharM != 'M' ||
403 hdr->Planes != 1 ||
Tom Wai-Hong Tama985ed42011-02-14 16:08:49 +0800404 (hdr->CompressionType != 0 && hdr->CompressionType != 1) ||
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800405 (hdr->BitPerPixel != 1 && hdr->BitPerPixel != 4 &&
406 hdr->BitPerPixel != 8 && hdr->BitPerPixel != 24))
407 return FORMAT_INVALID;
408
409 return FORMAT_BMP;
Bill Richardsond55085d2011-02-04 15:01:37 -0800410}
411
412uint32_t BmpBlockUtil::get_bmp_image_width(const string content) {
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800413 const BMP_IMAGE_HEADER *hdr = (const BMP_IMAGE_HEADER *)content.c_str();
414 return hdr->PixelWidth;
Bill Richardsond55085d2011-02-04 15:01:37 -0800415}
416
417uint32_t BmpBlockUtil::get_bmp_image_height(const string content) {
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800418 const BMP_IMAGE_HEADER *hdr = (const BMP_IMAGE_HEADER *)content.c_str();
419 return hdr->PixelHeight;
Bill Richardsond55085d2011-02-04 15:01:37 -0800420}
421
422void BmpBlockUtil::fill_all_image_infos() {
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800423 int errcnt = 0;
Bill Richardsond55085d2011-02-04 15:01:37 -0800424 for (StrImageConfigMap::iterator it = config_.images_map.begin();
425 it != config_.images_map.end();
426 ++it) {
427 it->second.data.format = (uint32_t)get_image_format(it->second.raw_content);
428 switch (it->second.data.format) {
429 case FORMAT_BMP:
430 it->second.data.width = get_bmp_image_width(it->second.raw_content);
431 it->second.data.height = get_bmp_image_height(it->second.raw_content);
432 break;
433 default:
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800434 fprintf(stderr, "Unsupported image format in %s\n",
435 it->second.filename.c_str());
436 errcnt++;
Bill Richardsond55085d2011-02-04 15:01:37 -0800437 }
438 }
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800439 if (errcnt)
440 error("Unable to continue due to errors.\n");
Bill Richardsond55085d2011-02-04 15:01:37 -0800441}
442
443void BmpBlockUtil::compress_all_images(const Compression compress) {
444 switch (compress) {
445 case COMPRESS_NONE:
446 for (StrImageConfigMap::iterator it = config_.images_map.begin();
447 it != config_.images_map.end();
448 ++it) {
449 it->second.data.compression = compress;
450 it->second.compressed_content = it->second.raw_content;
451 it->second.data.compressed_size = it->second.compressed_content.size();
452 }
453 break;
454 default:
Bill Richardson856e0722011-02-07 15:39:45 -0800455 error("Unsupported data compression.\n");
Bill Richardsond55085d2011-02-04 15:01:37 -0800456 }
457}
458
459void BmpBlockUtil::fill_bmpblock_header() {
460 memset(&config_.header, '\0', sizeof(config_.header));
461 memcpy(&config_.header.signature, BMPBLOCK_SIGNATURE,
462 BMPBLOCK_SIGNATURE_SIZE);
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800463 config_.header.major_version = BMPBLOCK_MAJOR_VERSION;
464 config_.header.minor_version = BMPBLOCK_MINOR_VERSION;
Bill Richardsond55085d2011-02-04 15:01:37 -0800465 config_.header.number_of_localizations = config_.localizations.size();
466 config_.header.number_of_screenlayouts = config_.localizations[0].size();
467 for (unsigned int i = 1; i < config_.localizations.size(); ++i) {
468 assert(config_.header.number_of_screenlayouts ==
469 config_.localizations[i].size());
470 }
471 config_.header.number_of_imageinfos = config_.images_map.size();
472}
473
474void BmpBlockUtil::pack_bmpblock() {
475 bmpblock_.clear();
476
477 /* Compute the ImageInfo offsets from start of BMPBLOCK. */
478 uint32_t current_offset = sizeof(BmpBlockHeader) +
Bill Richardsonf456e832011-02-17 11:29:51 -0800479 sizeof(ScreenLayout) * (config_.header.number_of_localizations *
480 config_.header.number_of_screenlayouts);
Bill Richardsonf82f9412011-02-17 08:56:33 -0800481 for (unsigned int i = 0; i < config_.image_names.size(); ++i) {
482 string image_name = config_.image_names[i];
483 ImageConfig &img = config_.images_map[image_name];
484 img.offset = current_offset;
485 current_offset += sizeof(ImageInfo) +
486 config_.images_map[image_name].data.compressed_size;
Bill Richardsond55085d2011-02-04 15:01:37 -0800487 /* Make it 4-byte aligned. */
488 if ((current_offset & 3) > 0) {
489 current_offset = (current_offset & ~3) + 4;
490 }
491 }
492 bmpblock_.resize(current_offset);
493
494 /* Fill BmpBlockHeader struct. */
495 string::iterator current_filled = bmpblock_.begin();
496 std::copy(reinterpret_cast<char*>(&config_.header),
497 reinterpret_cast<char*>(&config_.header + 1),
498 current_filled);
499 current_filled += sizeof(config_.header);
500
501 /* Fill all ScreenLayout structs. */
502 for (unsigned int i = 0; i < config_.localizations.size(); ++i) {
503 for (unsigned int j = 0; j < config_.localizations[i].size(); ++j) {
504 ScreenConfig &screen = config_.screens_map[config_.localizations[i][j]];
505 for (unsigned int k = 0;
506 k < MAX_IMAGE_IN_LAYOUT && !screen.image_names[k].empty();
507 ++k) {
508 screen.data.images[k].image_info_offset =
509 config_.images_map[screen.image_names[k]].offset;
510 }
511 std::copy(reinterpret_cast<char*>(&screen.data),
512 reinterpret_cast<char*>(&screen.data + 1),
513 current_filled);
514 current_filled += sizeof(screen.data);
515 }
516 }
517
518 /* Fill all ImageInfo structs and image contents. */
519 for (StrImageConfigMap::iterator it = config_.images_map.begin();
520 it != config_.images_map.end();
521 ++it) {
522 current_filled = bmpblock_.begin() + it->second.offset;
523 std::copy(reinterpret_cast<char*>(&it->second.data),
524 reinterpret_cast<char*>(&it->second.data + 1),
525 current_filled);
526 current_filled += sizeof(it->second.data);
527 std::copy(it->second.compressed_content.begin(),
528 it->second.compressed_content.end(),
529 current_filled);
530 }
531}
532
533void BmpBlockUtil::write_to_bmpblock(const char *filename) {
534 assert(!bmpblock_.empty());
535
536 FILE *fp = fopen(filename, "wb");
537 if (!fp) {
538 perror(filename);
539 exit(errno);
540 }
541
542 int r = fwrite(bmpblock_.c_str(), bmpblock_.size(), 1, fp);
543 fclose(fp);
544 if (r != 1) {
545 perror(filename);
546 exit(errno);
547 }
548}
549
550} // namespace vboot_reference
551
552#ifdef WITH_UTIL_MAIN
553
Bill Richardson794d4d42011-02-10 19:13:10 -0800554//////////////////////////////////////////////////////////////////////////////
555// Command line utilities.
556
557extern "C" {
558#include "bmpblk_util.h"
559}
Bill Richardsond55085d2011-02-04 15:01:37 -0800560
561using vboot_reference::BmpBlockUtil;
562
563// utility function: provide usage of this utility and exit.
564static void usagehelp_exit(const char *prog_name) {
565 printf(
Bill Richardsond55085d2011-02-04 15:01:37 -0800566 "\n"
Bill Richardson794d4d42011-02-10 19:13:10 -0800567 "To create a new BMPBLOCK file using config from YAML file:\n"
Bill Richardsond55085d2011-02-04 15:01:37 -0800568 "\n"
Bill Richardson794d4d42011-02-10 19:13:10 -0800569 " %s [-z NUM] -c YAML BMPBLOCK\n"
Bill Richardsond55085d2011-02-04 15:01:37 -0800570 "\n"
Bill Richardson794d4d42011-02-10 19:13:10 -0800571 " -z NUM = compression algorithm to use\n"
572 " 0 = none\n"
573 " 1 = EFIv1\n"
Tom Wai-Hong Tamee2bc912011-02-17 12:58:58 +0800574 " 2 = LZMA1\n"
Bill Richardson794d4d42011-02-10 19:13:10 -0800575 "\n", prog_name);
576 printf(
577 "To display the contents of a BMPBLOCK:\n"
578 "\n"
Bill Richardson61362d62011-02-14 10:28:03 -0800579 " %s [-y] BMPBLOCK\n"
580 "\n"
581 " -y = display as yaml\n"
Bill Richardson794d4d42011-02-10 19:13:10 -0800582 "\n", prog_name);
583 printf(
584 "To unpack a BMPBLOCK file:\n"
585 "\n"
586 " %s -x [-d DIR] [-f] BMPBLOCK\n"
587 "\n"
588 " -d DIR = directory to use (default '.')\n"
589 " -f = force overwriting existing files\n"
590 "\n", prog_name);
Bill Richardsond55085d2011-02-04 15:01:37 -0800591 exit(1);
592}
593
594///////////////////////////////////////////////////////////////////////
595// main
596
597int main(int argc, char *argv[]) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800598
Bill Richardson794d4d42011-02-10 19:13:10 -0800599 const char *prog_name = strrchr(argv[0], '/');
600 if (prog_name)
601 prog_name++;
602 else
603 prog_name = argv[0];
Bill Richardsond55085d2011-02-04 15:01:37 -0800604
Bill Richardson61362d62011-02-14 10:28:03 -0800605 int overwrite = 0, extract_mode = 0;
Bill Richardson794d4d42011-02-10 19:13:10 -0800606 int compression = 0;
Bill Richardson61362d62011-02-14 10:28:03 -0800607 int set_compression = 0;
Bill Richardson794d4d42011-02-10 19:13:10 -0800608 const char *config_fn = 0, *bmpblock_fn = 0, *extract_dir = ".";
Bill Richardson61362d62011-02-14 10:28:03 -0800609 int show_as_yaml = 0;
Bill Richardsond55085d2011-02-04 15:01:37 -0800610
Bill Richardson794d4d42011-02-10 19:13:10 -0800611 int opt;
612 opterr = 0; // quiet
613 int errorcnt = 0;
614 char *e = 0;
Bill Richardson61362d62011-02-14 10:28:03 -0800615 while ((opt = getopt(argc, argv, ":c:xz:fd:y")) != -1) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800616 switch (opt) {
Bill Richardson794d4d42011-02-10 19:13:10 -0800617 case 'c':
618 config_fn = optarg;
619 break;
620 case 'x':
621 extract_mode = 1;
622 break;
Bill Richardson61362d62011-02-14 10:28:03 -0800623 case 'y':
624 show_as_yaml = 1;
625 break;
Bill Richardson794d4d42011-02-10 19:13:10 -0800626 case 'z':
627 compression = (int)strtoul(optarg, &e, 0);
628 if (!*optarg || (e && *e)) {
629 fprintf(stderr, "%s: invalid argument to -%c: \"%s\"\n",
630 prog_name, opt, optarg);
631 errorcnt++;
632 }
633 if (compression >= MAX_COMPRESS) {
634 fprintf(stderr, "%s: compression type must be less than %d\n",
Bill Richardson61362d62011-02-14 10:28:03 -0800635 prog_name, MAX_COMPRESS);
Bill Richardson794d4d42011-02-10 19:13:10 -0800636 errorcnt++;
637 }
Bill Richardson61362d62011-02-14 10:28:03 -0800638 set_compression = 1;
Bill Richardson794d4d42011-02-10 19:13:10 -0800639 break;
640 case 'f':
Bill Richardson61362d62011-02-14 10:28:03 -0800641 overwrite = 1;
Bill Richardson794d4d42011-02-10 19:13:10 -0800642 break;
643 case 'd':
644 extract_dir= optarg;
645 break;
646 case ':':
647 fprintf(stderr, "%s: missing argument to -%c\n",
648 prog_name, optopt);
649 errorcnt++;
650 break;
651 default:
652 fprintf(stderr, "%s: unrecognized switch: -%c\n",
653 prog_name, optopt);
654 errorcnt++;
655 break;
Bill Richardsond55085d2011-02-04 15:01:37 -0800656 }
657 }
658 argc -= optind;
659 argv += optind;
660
Bill Richardson794d4d42011-02-10 19:13:10 -0800661 if (argc >= 1) {
662 bmpblock_fn = argv[0];
Bill Richardsond55085d2011-02-04 15:01:37 -0800663 } else {
Bill Richardson794d4d42011-02-10 19:13:10 -0800664 fprintf(stderr, "%s: missing BMPBLOCK name\n", prog_name);
665 errorcnt++;
666 }
667
668 if (errorcnt)
Bill Richardsond55085d2011-02-04 15:01:37 -0800669 usagehelp_exit(prog_name);
Bill Richardsond55085d2011-02-04 15:01:37 -0800670
Bill Richardson794d4d42011-02-10 19:13:10 -0800671 BmpBlockUtil util;
672
673 if (config_fn) {
Bill Richardson61362d62011-02-14 10:28:03 -0800674 if (set_compression)
675 util.force_compression(compression);
Bill Richardson794d4d42011-02-10 19:13:10 -0800676 util.load_from_config(config_fn);
Bill Richardsond55085d2011-02-04 15:01:37 -0800677 util.pack_bmpblock();
Bill Richardson794d4d42011-02-10 19:13:10 -0800678 util.write_to_bmpblock(bmpblock_fn);
Bill Richardsond55085d2011-02-04 15:01:37 -0800679 }
680
Bill Richardson794d4d42011-02-10 19:13:10 -0800681 else if (extract_mode) {
Bill Richardson61362d62011-02-14 10:28:03 -0800682 return dump_bmpblock(bmpblock_fn, 1, extract_dir, overwrite);
683 } else {
684 return dump_bmpblock(bmpblock_fn, show_as_yaml, 0, 0);
Bill Richardsond55085d2011-02-04 15:01:37 -0800685 }
686
Bill Richardsond55085d2011-02-04 15:01:37 -0800687 return 0;
688}
689
690#endif // WITH_UTIL_MAIN