blob: c9188a8e76e5349cb823b267586cbc257399eed6 [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;
192 config_.images_map[image_name] = ImageConfig();
193 config_.images_map[image_name].filename = image_filename;
194 break;
195 case YAML_MAPPING_END_EVENT:
196 yaml_event_delete(&event);
197 return;
198 default:
199 error("Syntax error in parsing images.\n");
200 }
201 yaml_event_delete(&event);
202 }
203}
204
205void BmpBlockUtil::parse_layout(yaml_parser_t *parser, ScreenConfig &screen) {
206 yaml_event_t event;
207 string screen_name;
208 int depth = 0, index1 = 0, index2 = 0;
209 expect_event(parser, YAML_SEQUENCE_START_EVENT);
210 for (;;) {
211 yaml_parser_parse(parser, &event);
212 switch (event.type) {
213 case YAML_SEQUENCE_START_EVENT:
214 depth++;
215 break;
216 case YAML_SCALAR_EVENT:
217 switch (index2) {
218 case 0:
219 screen.data.images[index1].x = atoi((char*)event.data.scalar.value);
220 break;
221 case 1:
222 screen.data.images[index1].y = atoi((char*)event.data.scalar.value);
223 break;
224 case 2:
225 screen.image_names[index1] = (char*)event.data.scalar.value;
226 break;
227 default:
228 error("Syntax error in parsing layout.\n");
229 }
230 index2++;
231 break;
232 case YAML_SEQUENCE_END_EVENT:
233 if (depth == 1) {
234 index1++;
235 index2 = 0;
236 } else if (depth == 0) {
237 yaml_event_delete(&event);
238 return;
239 }
240 depth--;
241 break;
242 default:
243 error("Syntax error in paring layout.\n");
244 }
245 yaml_event_delete(&event);
246 }
247}
248
249void BmpBlockUtil::parse_screens(yaml_parser_t *parser) {
250 yaml_event_t event;
251 string screen_name;
252 expect_event(parser, YAML_MAPPING_START_EVENT);
253 for (;;) {
254 yaml_parser_parse(parser, &event);
255 switch (event.type) {
256 case YAML_SCALAR_EVENT:
257 screen_name = (char*)event.data.scalar.value;
258 config_.screens_map[screen_name] = ScreenConfig();
259 parse_layout(parser, config_.screens_map[screen_name]);
260 break;
261 case YAML_MAPPING_END_EVENT:
262 yaml_event_delete(&event);
263 return;
264 default:
265 error("Syntax error in parsing screens.\n");
266 }
267 yaml_event_delete(&event);
268 }
269}
270
271void BmpBlockUtil::parse_localizations(yaml_parser_t *parser) {
272 yaml_event_t event;
273 int depth = 0, index = 0;
274 expect_event(parser, YAML_SEQUENCE_START_EVENT);
275 for (;;) {
276 yaml_parser_parse(parser, &event);
277 switch (event.type) {
278 case YAML_SEQUENCE_START_EVENT:
279 config_.localizations.push_back(vector<string>());
280 depth++;
281 break;
282 case YAML_SCALAR_EVENT:
283 config_.localizations[index].push_back((char*)event.data.scalar.value);
284 break;
285 case YAML_SEQUENCE_END_EVENT:
286 if (depth == 1) {
287 index++;
288 } else if (depth == 0) {
289 yaml_event_delete(&event);
290 return;
291 }
292 depth--;
293 break;
294 default:
295 error("Syntax error in parsing localizations.\n");
296 }
297 yaml_event_delete(&event);
298 }
299}
300
301void BmpBlockUtil::load_all_image_files() {
302 for (StrImageConfigMap::iterator it = config_.images_map.begin();
303 it != config_.images_map.end();
304 ++it) {
305 const string &content = read_image_file(it->second.filename.c_str());
306 it->second.raw_content = content;
307 it->second.data.original_size = content.size();
Bill Richardson61362d62011-02-14 10:28:03 -0800308 switch(compression_) {
309 case COMPRESS_NONE:
310 it->second.data.compression = compression_;
311 it->second.compressed_content = content;
312 it->second.data.compressed_size = content.size();
313 break;
314 case COMPRESS_EFIv1:
315 {
316 // The content will always compress smaller (so sez the docs).
317 uint32_t tmpsize = content.size();
318 uint8_t *tmpbuf = (uint8_t *)malloc(tmpsize);
319 // The size of the compressed content is also returned.
320 if (EFI_SUCCESS != EfiCompress((uint8_t *)content.c_str(), tmpsize,
321 tmpbuf, &tmpsize)) {
322 error("Unable to compress!\n");
323 }
324 it->second.data.compression = compression_;
325 it->second.compressed_content.assign((const char *)tmpbuf, tmpsize);
326 it->second.data.compressed_size = tmpsize;
327 free(tmpbuf);
328 }
329 break;
Tom Wai-Hong Tamee2bc912011-02-17 12:58:58 +0800330 case COMPRESS_LZMA1:
331 {
332 // Calculate the worst case of buffer size.
333 uint32_t tmpsize = lzma_stream_buffer_bound(content.size());
334 uint8_t *tmpbuf = (uint8_t *)malloc(tmpsize);
335 lzma_stream stream = LZMA_STREAM_INIT;
336 lzma_options_lzma options;
337 lzma_ret result;
338
339 lzma_lzma_preset(&options, 9);
340 result = lzma_alone_encoder(&stream, &options);
341 if (result != LZMA_OK) {
342 error("Unable to initialize easy encoder (error: %d)!\n", result);
343 }
344
345 stream.next_in = (uint8_t *)content.data();
346 stream.avail_in = content.size();
347 stream.next_out = tmpbuf;
348 stream.avail_out = tmpsize;
349 result = lzma_code(&stream, LZMA_FINISH);
350 if (result != LZMA_STREAM_END) {
351 error("Unable to encode data (error: %d)!\n", result);
352 }
353
354 it->second.data.compression = compression_;
355 it->second.compressed_content.assign((const char *)tmpbuf,
356 tmpsize - stream.avail_out);
357 it->second.data.compressed_size = tmpsize - stream.avail_out;
358 lzma_end(&stream);
359 free(tmpbuf);
360 }
361 break;
Bill Richardson61362d62011-02-14 10:28:03 -0800362 default:
363 error("Unsupported compression method attempted.\n");
364 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800365 }
366}
367
368const string BmpBlockUtil::read_image_file(const char *filename) {
369 string content;
370 vector<char> buffer;
371
372 FILE *fp = fopen(filename, "rb");
373 if (!fp) {
374 perror(filename);
375 exit(errno);
376 }
377
378 if (fseek(fp, 0, SEEK_END) == 0) {
379 buffer.resize(ftell(fp));
380 rewind(fp);
381 }
382
383 if (!buffer.empty()) {
384 if(fread(&buffer[0], buffer.size(), 1, fp) != 1) {
385 perror(filename);
386 buffer.clear();
387 } else {
388 content.assign(buffer.begin(), buffer.end());
389 }
390 }
391
392 fclose(fp);
393 return content;
394}
395
396ImageFormat BmpBlockUtil::get_image_format(const string content) {
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800397 if (content.size() < sizeof(BMP_IMAGE_HEADER))
Bill Richardsond55085d2011-02-04 15:01:37 -0800398 return FORMAT_INVALID;
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800399 const BMP_IMAGE_HEADER *hdr = (const BMP_IMAGE_HEADER *)content.c_str();
400
401 if (hdr->CharB != 'B' || hdr->CharM != 'M' ||
402 hdr->Planes != 1 ||
Tom Wai-Hong Tama985ed42011-02-14 16:08:49 +0800403 (hdr->CompressionType != 0 && hdr->CompressionType != 1) ||
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800404 (hdr->BitPerPixel != 1 && hdr->BitPerPixel != 4 &&
405 hdr->BitPerPixel != 8 && hdr->BitPerPixel != 24))
406 return FORMAT_INVALID;
407
408 return FORMAT_BMP;
Bill Richardsond55085d2011-02-04 15:01:37 -0800409}
410
411uint32_t BmpBlockUtil::get_bmp_image_width(const string content) {
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800412 const BMP_IMAGE_HEADER *hdr = (const BMP_IMAGE_HEADER *)content.c_str();
413 return hdr->PixelWidth;
Bill Richardsond55085d2011-02-04 15:01:37 -0800414}
415
416uint32_t BmpBlockUtil::get_bmp_image_height(const string content) {
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800417 const BMP_IMAGE_HEADER *hdr = (const BMP_IMAGE_HEADER *)content.c_str();
418 return hdr->PixelHeight;
Bill Richardsond55085d2011-02-04 15:01:37 -0800419}
420
421void BmpBlockUtil::fill_all_image_infos() {
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800422 int errcnt = 0;
Bill Richardsond55085d2011-02-04 15:01:37 -0800423 for (StrImageConfigMap::iterator it = config_.images_map.begin();
424 it != config_.images_map.end();
425 ++it) {
426 it->second.data.format = (uint32_t)get_image_format(it->second.raw_content);
427 switch (it->second.data.format) {
428 case FORMAT_BMP:
429 it->second.data.width = get_bmp_image_width(it->second.raw_content);
430 it->second.data.height = get_bmp_image_height(it->second.raw_content);
431 break;
432 default:
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800433 fprintf(stderr, "Unsupported image format in %s\n",
434 it->second.filename.c_str());
435 errcnt++;
Bill Richardsond55085d2011-02-04 15:01:37 -0800436 }
437 }
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800438 if (errcnt)
439 error("Unable to continue due to errors.\n");
Bill Richardsond55085d2011-02-04 15:01:37 -0800440}
441
442void BmpBlockUtil::compress_all_images(const Compression compress) {
443 switch (compress) {
444 case COMPRESS_NONE:
445 for (StrImageConfigMap::iterator it = config_.images_map.begin();
446 it != config_.images_map.end();
447 ++it) {
448 it->second.data.compression = compress;
449 it->second.compressed_content = it->second.raw_content;
450 it->second.data.compressed_size = it->second.compressed_content.size();
451 }
452 break;
453 default:
Bill Richardson856e0722011-02-07 15:39:45 -0800454 error("Unsupported data compression.\n");
Bill Richardsond55085d2011-02-04 15:01:37 -0800455 }
456}
457
458void BmpBlockUtil::fill_bmpblock_header() {
459 memset(&config_.header, '\0', sizeof(config_.header));
460 memcpy(&config_.header.signature, BMPBLOCK_SIGNATURE,
461 BMPBLOCK_SIGNATURE_SIZE);
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800462 config_.header.major_version = BMPBLOCK_MAJOR_VERSION;
463 config_.header.minor_version = BMPBLOCK_MINOR_VERSION;
Bill Richardsond55085d2011-02-04 15:01:37 -0800464 config_.header.number_of_localizations = config_.localizations.size();
465 config_.header.number_of_screenlayouts = config_.localizations[0].size();
466 for (unsigned int i = 1; i < config_.localizations.size(); ++i) {
467 assert(config_.header.number_of_screenlayouts ==
468 config_.localizations[i].size());
469 }
470 config_.header.number_of_imageinfos = config_.images_map.size();
471}
472
473void BmpBlockUtil::pack_bmpblock() {
474 bmpblock_.clear();
475
476 /* Compute the ImageInfo offsets from start of BMPBLOCK. */
477 uint32_t current_offset = sizeof(BmpBlockHeader) +
Bill Richardson61362d62011-02-14 10:28:03 -0800478 sizeof(ScreenLayout) * config_.screens_map.size();
Bill Richardsond55085d2011-02-04 15:01:37 -0800479 for (StrImageConfigMap::iterator it = config_.images_map.begin();
480 it != config_.images_map.end();
481 ++it) {
482 it->second.offset = current_offset;
483 current_offset += sizeof(ImageInfo) + it->second.data.compressed_size;
484 /* Make it 4-byte aligned. */
485 if ((current_offset & 3) > 0) {
486 current_offset = (current_offset & ~3) + 4;
487 }
488 }
489 bmpblock_.resize(current_offset);
490
491 /* Fill BmpBlockHeader struct. */
492 string::iterator current_filled = bmpblock_.begin();
493 std::copy(reinterpret_cast<char*>(&config_.header),
494 reinterpret_cast<char*>(&config_.header + 1),
495 current_filled);
496 current_filled += sizeof(config_.header);
497
498 /* Fill all ScreenLayout structs. */
499 for (unsigned int i = 0; i < config_.localizations.size(); ++i) {
500 for (unsigned int j = 0; j < config_.localizations[i].size(); ++j) {
501 ScreenConfig &screen = config_.screens_map[config_.localizations[i][j]];
502 for (unsigned int k = 0;
503 k < MAX_IMAGE_IN_LAYOUT && !screen.image_names[k].empty();
504 ++k) {
505 screen.data.images[k].image_info_offset =
506 config_.images_map[screen.image_names[k]].offset;
507 }
508 std::copy(reinterpret_cast<char*>(&screen.data),
509 reinterpret_cast<char*>(&screen.data + 1),
510 current_filled);
511 current_filled += sizeof(screen.data);
512 }
513 }
514
515 /* Fill all ImageInfo structs and image contents. */
516 for (StrImageConfigMap::iterator it = config_.images_map.begin();
517 it != config_.images_map.end();
518 ++it) {
519 current_filled = bmpblock_.begin() + it->second.offset;
520 std::copy(reinterpret_cast<char*>(&it->second.data),
521 reinterpret_cast<char*>(&it->second.data + 1),
522 current_filled);
523 current_filled += sizeof(it->second.data);
524 std::copy(it->second.compressed_content.begin(),
525 it->second.compressed_content.end(),
526 current_filled);
527 }
528}
529
530void BmpBlockUtil::write_to_bmpblock(const char *filename) {
531 assert(!bmpblock_.empty());
532
533 FILE *fp = fopen(filename, "wb");
534 if (!fp) {
535 perror(filename);
536 exit(errno);
537 }
538
539 int r = fwrite(bmpblock_.c_str(), bmpblock_.size(), 1, fp);
540 fclose(fp);
541 if (r != 1) {
542 perror(filename);
543 exit(errno);
544 }
545}
546
547} // namespace vboot_reference
548
549#ifdef WITH_UTIL_MAIN
550
Bill Richardson794d4d42011-02-10 19:13:10 -0800551//////////////////////////////////////////////////////////////////////////////
552// Command line utilities.
553
554extern "C" {
555#include "bmpblk_util.h"
556}
Bill Richardsond55085d2011-02-04 15:01:37 -0800557
558using vboot_reference::BmpBlockUtil;
559
560// utility function: provide usage of this utility and exit.
561static void usagehelp_exit(const char *prog_name) {
562 printf(
Bill Richardsond55085d2011-02-04 15:01:37 -0800563 "\n"
Bill Richardson794d4d42011-02-10 19:13:10 -0800564 "To create a new BMPBLOCK file using config from YAML file:\n"
Bill Richardsond55085d2011-02-04 15:01:37 -0800565 "\n"
Bill Richardson794d4d42011-02-10 19:13:10 -0800566 " %s [-z NUM] -c YAML BMPBLOCK\n"
Bill Richardsond55085d2011-02-04 15:01:37 -0800567 "\n"
Bill Richardson794d4d42011-02-10 19:13:10 -0800568 " -z NUM = compression algorithm to use\n"
569 " 0 = none\n"
570 " 1 = EFIv1\n"
Tom Wai-Hong Tamee2bc912011-02-17 12:58:58 +0800571 " 2 = LZMA1\n"
Bill Richardson794d4d42011-02-10 19:13:10 -0800572 "\n", prog_name);
573 printf(
574 "To display the contents of a BMPBLOCK:\n"
575 "\n"
Bill Richardson61362d62011-02-14 10:28:03 -0800576 " %s [-y] BMPBLOCK\n"
577 "\n"
578 " -y = display as yaml\n"
Bill Richardson794d4d42011-02-10 19:13:10 -0800579 "\n", prog_name);
580 printf(
581 "To unpack a BMPBLOCK file:\n"
582 "\n"
583 " %s -x [-d DIR] [-f] BMPBLOCK\n"
584 "\n"
585 " -d DIR = directory to use (default '.')\n"
586 " -f = force overwriting existing files\n"
587 "\n", prog_name);
Bill Richardsond55085d2011-02-04 15:01:37 -0800588 exit(1);
589}
590
591///////////////////////////////////////////////////////////////////////
592// main
593
594int main(int argc, char *argv[]) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800595
Bill Richardson794d4d42011-02-10 19:13:10 -0800596 const char *prog_name = strrchr(argv[0], '/');
597 if (prog_name)
598 prog_name++;
599 else
600 prog_name = argv[0];
Bill Richardsond55085d2011-02-04 15:01:37 -0800601
Bill Richardson61362d62011-02-14 10:28:03 -0800602 int overwrite = 0, extract_mode = 0;
Bill Richardson794d4d42011-02-10 19:13:10 -0800603 int compression = 0;
Bill Richardson61362d62011-02-14 10:28:03 -0800604 int set_compression = 0;
Bill Richardson794d4d42011-02-10 19:13:10 -0800605 const char *config_fn = 0, *bmpblock_fn = 0, *extract_dir = ".";
Bill Richardson61362d62011-02-14 10:28:03 -0800606 int show_as_yaml = 0;
Bill Richardsond55085d2011-02-04 15:01:37 -0800607
Bill Richardson794d4d42011-02-10 19:13:10 -0800608 int opt;
609 opterr = 0; // quiet
610 int errorcnt = 0;
611 char *e = 0;
Bill Richardson61362d62011-02-14 10:28:03 -0800612 while ((opt = getopt(argc, argv, ":c:xz:fd:y")) != -1) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800613 switch (opt) {
Bill Richardson794d4d42011-02-10 19:13:10 -0800614 case 'c':
615 config_fn = optarg;
616 break;
617 case 'x':
618 extract_mode = 1;
619 break;
Bill Richardson61362d62011-02-14 10:28:03 -0800620 case 'y':
621 show_as_yaml = 1;
622 break;
Bill Richardson794d4d42011-02-10 19:13:10 -0800623 case 'z':
624 compression = (int)strtoul(optarg, &e, 0);
625 if (!*optarg || (e && *e)) {
626 fprintf(stderr, "%s: invalid argument to -%c: \"%s\"\n",
627 prog_name, opt, optarg);
628 errorcnt++;
629 }
630 if (compression >= MAX_COMPRESS) {
631 fprintf(stderr, "%s: compression type must be less than %d\n",
Bill Richardson61362d62011-02-14 10:28:03 -0800632 prog_name, MAX_COMPRESS);
Bill Richardson794d4d42011-02-10 19:13:10 -0800633 errorcnt++;
634 }
Bill Richardson61362d62011-02-14 10:28:03 -0800635 set_compression = 1;
Bill Richardson794d4d42011-02-10 19:13:10 -0800636 break;
637 case 'f':
Bill Richardson61362d62011-02-14 10:28:03 -0800638 overwrite = 1;
Bill Richardson794d4d42011-02-10 19:13:10 -0800639 break;
640 case 'd':
641 extract_dir= optarg;
642 break;
643 case ':':
644 fprintf(stderr, "%s: missing argument to -%c\n",
645 prog_name, optopt);
646 errorcnt++;
647 break;
648 default:
649 fprintf(stderr, "%s: unrecognized switch: -%c\n",
650 prog_name, optopt);
651 errorcnt++;
652 break;
Bill Richardsond55085d2011-02-04 15:01:37 -0800653 }
654 }
655 argc -= optind;
656 argv += optind;
657
Bill Richardson794d4d42011-02-10 19:13:10 -0800658 if (argc >= 1) {
659 bmpblock_fn = argv[0];
Bill Richardsond55085d2011-02-04 15:01:37 -0800660 } else {
Bill Richardson794d4d42011-02-10 19:13:10 -0800661 fprintf(stderr, "%s: missing BMPBLOCK name\n", prog_name);
662 errorcnt++;
663 }
664
665 if (errorcnt)
Bill Richardsond55085d2011-02-04 15:01:37 -0800666 usagehelp_exit(prog_name);
Bill Richardsond55085d2011-02-04 15:01:37 -0800667
Bill Richardson794d4d42011-02-10 19:13:10 -0800668 BmpBlockUtil util;
669
670 if (config_fn) {
Bill Richardson61362d62011-02-14 10:28:03 -0800671 if (set_compression)
672 util.force_compression(compression);
Bill Richardson794d4d42011-02-10 19:13:10 -0800673 util.load_from_config(config_fn);
Bill Richardsond55085d2011-02-04 15:01:37 -0800674 util.pack_bmpblock();
Bill Richardson794d4d42011-02-10 19:13:10 -0800675 util.write_to_bmpblock(bmpblock_fn);
Bill Richardsond55085d2011-02-04 15:01:37 -0800676 }
677
Bill Richardson794d4d42011-02-10 19:13:10 -0800678 else if (extract_mode) {
Bill Richardson61362d62011-02-14 10:28:03 -0800679 return dump_bmpblock(bmpblock_fn, 1, extract_dir, overwrite);
680 } else {
681 return dump_bmpblock(bmpblock_fn, show_as_yaml, 0, 0);
Bill Richardsond55085d2011-02-04 15:01:37 -0800682 }
683
Bill Richardsond55085d2011-02-04 15:01:37 -0800684 return 0;
685}
686
687#endif // WITH_UTIL_MAIN