blob: 078a5cebbe8fab423e83c3f955fbaeeb5300aae0 [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);
Bill Richardsona7209ee2011-02-17 14:30:14 -0800142 } else if (keyword == "compression") {
143 parse_compression(parser);
Bill Richardsond55085d2011-02-04 15:01:37 -0800144 } else if (keyword == "images") {
145 parse_images(parser);
146 } else if (keyword == "screens") {
147 parse_screens(parser);
148 } else if (keyword == "localizations") {
149 parse_localizations(parser);
150 }
151 break;
152 case YAML_MAPPING_END_EVENT:
153 yaml_event_delete(&event);
154 return;
155 default:
156 error("Syntax error in parsing config file.\n");
157 }
158 yaml_event_delete(&event);
159 }
160}
161
162void BmpBlockUtil::parse_bmpblock(yaml_parser_t *parser) {
163 yaml_event_t event;
164 yaml_parser_parse(parser, &event);
165 if (event.type != YAML_SCALAR_EVENT) {
166 error("Syntax error in parsing bmpblock.\n");
167 }
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800168 char wantversion[20];
169 sprintf(wantversion, "%d.%d",
170 BMPBLOCK_MAJOR_VERSION,
171 BMPBLOCK_MINOR_VERSION);
172 string gotversion = (char*)event.data.scalar.value;
173 if (gotversion != wantversion) {
174 error("Invalid version specified in config file\n");
175 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800176 yaml_event_delete(&event);
177}
178
Bill Richardsona7209ee2011-02-17 14:30:14 -0800179void BmpBlockUtil::parse_compression(yaml_parser_t *parser) {
180 yaml_event_t event;
181 yaml_parser_parse(parser, &event);
182 if (event.type != YAML_SCALAR_EVENT) {
183 error("Syntax error in parsing bmpblock.\n");
184 }
185 char *comp_str = (char *)event.data.scalar.value;
186 char *e = 0;
187 uint32_t comp = (uint32_t)strtoul(comp_str, &e, 0);
188 if (!*comp_str || (e && *e) || comp >= MAX_COMPRESS) {
189 error("Invalid compression specified in config file\n");
190 }
191 if (!set_compression_) {
192 compression_ = comp;
193 }
194 yaml_event_delete(&event);
195}
196
Bill Richardsond55085d2011-02-04 15:01:37 -0800197void BmpBlockUtil::parse_images(yaml_parser_t *parser) {
198 yaml_event_t event;
199 string image_name, image_filename;
200 expect_event(parser, YAML_MAPPING_START_EVENT);
201 for (;;) {
202 yaml_parser_parse(parser, &event);
203 switch (event.type) {
204 case YAML_SCALAR_EVENT:
205 image_name = (char*)event.data.scalar.value;
206 yaml_event_delete(&event);
207 yaml_parser_parse(parser, &event);
208 if (event.type != YAML_SCALAR_EVENT) {
209 error("Syntax error in parsing images.\n");
210 }
211 image_filename = (char*)event.data.scalar.value;
Bill Richardsonf82f9412011-02-17 08:56:33 -0800212 config_.image_names.push_back(image_name);
Bill Richardsond55085d2011-02-04 15:01:37 -0800213 config_.images_map[image_name] = ImageConfig();
214 config_.images_map[image_name].filename = image_filename;
215 break;
216 case YAML_MAPPING_END_EVENT:
217 yaml_event_delete(&event);
218 return;
219 default:
220 error("Syntax error in parsing images.\n");
221 }
222 yaml_event_delete(&event);
223 }
224}
225
226void BmpBlockUtil::parse_layout(yaml_parser_t *parser, ScreenConfig &screen) {
227 yaml_event_t event;
228 string screen_name;
229 int depth = 0, index1 = 0, index2 = 0;
230 expect_event(parser, YAML_SEQUENCE_START_EVENT);
231 for (;;) {
232 yaml_parser_parse(parser, &event);
233 switch (event.type) {
234 case YAML_SEQUENCE_START_EVENT:
235 depth++;
236 break;
237 case YAML_SCALAR_EVENT:
238 switch (index2) {
239 case 0:
240 screen.data.images[index1].x = atoi((char*)event.data.scalar.value);
241 break;
242 case 1:
243 screen.data.images[index1].y = atoi((char*)event.data.scalar.value);
244 break;
245 case 2:
246 screen.image_names[index1] = (char*)event.data.scalar.value;
247 break;
248 default:
249 error("Syntax error in parsing layout.\n");
250 }
251 index2++;
252 break;
253 case YAML_SEQUENCE_END_EVENT:
254 if (depth == 1) {
255 index1++;
256 index2 = 0;
257 } else if (depth == 0) {
258 yaml_event_delete(&event);
259 return;
260 }
261 depth--;
262 break;
263 default:
264 error("Syntax error in paring layout.\n");
265 }
266 yaml_event_delete(&event);
267 }
268}
269
270void BmpBlockUtil::parse_screens(yaml_parser_t *parser) {
271 yaml_event_t event;
272 string screen_name;
273 expect_event(parser, YAML_MAPPING_START_EVENT);
274 for (;;) {
275 yaml_parser_parse(parser, &event);
276 switch (event.type) {
277 case YAML_SCALAR_EVENT:
278 screen_name = (char*)event.data.scalar.value;
279 config_.screens_map[screen_name] = ScreenConfig();
280 parse_layout(parser, config_.screens_map[screen_name]);
281 break;
282 case YAML_MAPPING_END_EVENT:
283 yaml_event_delete(&event);
284 return;
285 default:
286 error("Syntax error in parsing screens.\n");
287 }
288 yaml_event_delete(&event);
289 }
290}
291
292void BmpBlockUtil::parse_localizations(yaml_parser_t *parser) {
293 yaml_event_t event;
294 int depth = 0, index = 0;
295 expect_event(parser, YAML_SEQUENCE_START_EVENT);
296 for (;;) {
297 yaml_parser_parse(parser, &event);
298 switch (event.type) {
299 case YAML_SEQUENCE_START_EVENT:
300 config_.localizations.push_back(vector<string>());
301 depth++;
302 break;
303 case YAML_SCALAR_EVENT:
304 config_.localizations[index].push_back((char*)event.data.scalar.value);
305 break;
306 case YAML_SEQUENCE_END_EVENT:
307 if (depth == 1) {
308 index++;
309 } else if (depth == 0) {
310 yaml_event_delete(&event);
311 return;
312 }
313 depth--;
314 break;
315 default:
316 error("Syntax error in parsing localizations.\n");
317 }
318 yaml_event_delete(&event);
319 }
320}
321
322void BmpBlockUtil::load_all_image_files() {
323 for (StrImageConfigMap::iterator it = config_.images_map.begin();
324 it != config_.images_map.end();
325 ++it) {
326 const string &content = read_image_file(it->second.filename.c_str());
327 it->second.raw_content = content;
328 it->second.data.original_size = content.size();
Bill Richardson61362d62011-02-14 10:28:03 -0800329 switch(compression_) {
330 case COMPRESS_NONE:
331 it->second.data.compression = compression_;
332 it->second.compressed_content = content;
333 it->second.data.compressed_size = content.size();
334 break;
335 case COMPRESS_EFIv1:
336 {
337 // The content will always compress smaller (so sez the docs).
338 uint32_t tmpsize = content.size();
339 uint8_t *tmpbuf = (uint8_t *)malloc(tmpsize);
340 // The size of the compressed content is also returned.
341 if (EFI_SUCCESS != EfiCompress((uint8_t *)content.c_str(), tmpsize,
342 tmpbuf, &tmpsize)) {
343 error("Unable to compress!\n");
344 }
345 it->second.data.compression = compression_;
346 it->second.compressed_content.assign((const char *)tmpbuf, tmpsize);
347 it->second.data.compressed_size = tmpsize;
348 free(tmpbuf);
349 }
350 break;
Tom Wai-Hong Tamee2bc912011-02-17 12:58:58 +0800351 case COMPRESS_LZMA1:
352 {
353 // Calculate the worst case of buffer size.
354 uint32_t tmpsize = lzma_stream_buffer_bound(content.size());
355 uint8_t *tmpbuf = (uint8_t *)malloc(tmpsize);
356 lzma_stream stream = LZMA_STREAM_INIT;
357 lzma_options_lzma options;
358 lzma_ret result;
359
360 lzma_lzma_preset(&options, 9);
361 result = lzma_alone_encoder(&stream, &options);
362 if (result != LZMA_OK) {
363 error("Unable to initialize easy encoder (error: %d)!\n", result);
364 }
365
366 stream.next_in = (uint8_t *)content.data();
367 stream.avail_in = content.size();
368 stream.next_out = tmpbuf;
369 stream.avail_out = tmpsize;
370 result = lzma_code(&stream, LZMA_FINISH);
371 if (result != LZMA_STREAM_END) {
372 error("Unable to encode data (error: %d)!\n", result);
373 }
374
375 it->second.data.compression = compression_;
376 it->second.compressed_content.assign((const char *)tmpbuf,
377 tmpsize - stream.avail_out);
378 it->second.data.compressed_size = tmpsize - stream.avail_out;
379 lzma_end(&stream);
380 free(tmpbuf);
381 }
382 break;
Bill Richardson61362d62011-02-14 10:28:03 -0800383 default:
384 error("Unsupported compression method attempted.\n");
385 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800386 }
387}
388
389const string BmpBlockUtil::read_image_file(const char *filename) {
390 string content;
391 vector<char> buffer;
392
393 FILE *fp = fopen(filename, "rb");
394 if (!fp) {
395 perror(filename);
396 exit(errno);
397 }
398
399 if (fseek(fp, 0, SEEK_END) == 0) {
400 buffer.resize(ftell(fp));
401 rewind(fp);
402 }
403
404 if (!buffer.empty()) {
405 if(fread(&buffer[0], buffer.size(), 1, fp) != 1) {
406 perror(filename);
407 buffer.clear();
408 } else {
409 content.assign(buffer.begin(), buffer.end());
410 }
411 }
412
413 fclose(fp);
414 return content;
415}
416
417ImageFormat BmpBlockUtil::get_image_format(const string content) {
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800418 if (content.size() < sizeof(BMP_IMAGE_HEADER))
Bill Richardsond55085d2011-02-04 15:01:37 -0800419 return FORMAT_INVALID;
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800420 const BMP_IMAGE_HEADER *hdr = (const BMP_IMAGE_HEADER *)content.c_str();
421
422 if (hdr->CharB != 'B' || hdr->CharM != 'M' ||
423 hdr->Planes != 1 ||
Tom Wai-Hong Tama985ed42011-02-14 16:08:49 +0800424 (hdr->CompressionType != 0 && hdr->CompressionType != 1) ||
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800425 (hdr->BitPerPixel != 1 && hdr->BitPerPixel != 4 &&
426 hdr->BitPerPixel != 8 && hdr->BitPerPixel != 24))
427 return FORMAT_INVALID;
428
429 return FORMAT_BMP;
Bill Richardsond55085d2011-02-04 15:01:37 -0800430}
431
432uint32_t BmpBlockUtil::get_bmp_image_width(const string content) {
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800433 const BMP_IMAGE_HEADER *hdr = (const BMP_IMAGE_HEADER *)content.c_str();
434 return hdr->PixelWidth;
Bill Richardsond55085d2011-02-04 15:01:37 -0800435}
436
437uint32_t BmpBlockUtil::get_bmp_image_height(const string content) {
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800438 const BMP_IMAGE_HEADER *hdr = (const BMP_IMAGE_HEADER *)content.c_str();
439 return hdr->PixelHeight;
Bill Richardsond55085d2011-02-04 15:01:37 -0800440}
441
442void BmpBlockUtil::fill_all_image_infos() {
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800443 int errcnt = 0;
Bill Richardsond55085d2011-02-04 15:01:37 -0800444 for (StrImageConfigMap::iterator it = config_.images_map.begin();
445 it != config_.images_map.end();
446 ++it) {
447 it->second.data.format = (uint32_t)get_image_format(it->second.raw_content);
448 switch (it->second.data.format) {
449 case FORMAT_BMP:
450 it->second.data.width = get_bmp_image_width(it->second.raw_content);
451 it->second.data.height = get_bmp_image_height(it->second.raw_content);
452 break;
453 default:
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800454 fprintf(stderr, "Unsupported image format in %s\n",
455 it->second.filename.c_str());
456 errcnt++;
Bill Richardsond55085d2011-02-04 15:01:37 -0800457 }
458 }
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800459 if (errcnt)
460 error("Unable to continue due to errors.\n");
Bill Richardsond55085d2011-02-04 15:01:37 -0800461}
462
463void BmpBlockUtil::compress_all_images(const Compression compress) {
464 switch (compress) {
465 case COMPRESS_NONE:
466 for (StrImageConfigMap::iterator it = config_.images_map.begin();
467 it != config_.images_map.end();
468 ++it) {
469 it->second.data.compression = compress;
470 it->second.compressed_content = it->second.raw_content;
471 it->second.data.compressed_size = it->second.compressed_content.size();
472 }
473 break;
474 default:
Bill Richardson856e0722011-02-07 15:39:45 -0800475 error("Unsupported data compression.\n");
Bill Richardsond55085d2011-02-04 15:01:37 -0800476 }
477}
478
479void BmpBlockUtil::fill_bmpblock_header() {
480 memset(&config_.header, '\0', sizeof(config_.header));
481 memcpy(&config_.header.signature, BMPBLOCK_SIGNATURE,
482 BMPBLOCK_SIGNATURE_SIZE);
Bill Richardsonfc05bb82011-02-08 15:03:36 -0800483 config_.header.major_version = BMPBLOCK_MAJOR_VERSION;
484 config_.header.minor_version = BMPBLOCK_MINOR_VERSION;
Bill Richardsond55085d2011-02-04 15:01:37 -0800485 config_.header.number_of_localizations = config_.localizations.size();
486 config_.header.number_of_screenlayouts = config_.localizations[0].size();
487 for (unsigned int i = 1; i < config_.localizations.size(); ++i) {
488 assert(config_.header.number_of_screenlayouts ==
489 config_.localizations[i].size());
490 }
491 config_.header.number_of_imageinfos = config_.images_map.size();
492}
493
494void BmpBlockUtil::pack_bmpblock() {
495 bmpblock_.clear();
496
497 /* Compute the ImageInfo offsets from start of BMPBLOCK. */
498 uint32_t current_offset = sizeof(BmpBlockHeader) +
Bill Richardsonf456e832011-02-17 11:29:51 -0800499 sizeof(ScreenLayout) * (config_.header.number_of_localizations *
500 config_.header.number_of_screenlayouts);
Bill Richardsonf82f9412011-02-17 08:56:33 -0800501 for (unsigned int i = 0; i < config_.image_names.size(); ++i) {
502 string image_name = config_.image_names[i];
503 ImageConfig &img = config_.images_map[image_name];
504 img.offset = current_offset;
505 current_offset += sizeof(ImageInfo) +
506 config_.images_map[image_name].data.compressed_size;
Bill Richardsond55085d2011-02-04 15:01:37 -0800507 /* Make it 4-byte aligned. */
508 if ((current_offset & 3) > 0) {
509 current_offset = (current_offset & ~3) + 4;
510 }
511 }
512 bmpblock_.resize(current_offset);
513
514 /* Fill BmpBlockHeader struct. */
515 string::iterator current_filled = bmpblock_.begin();
516 std::copy(reinterpret_cast<char*>(&config_.header),
517 reinterpret_cast<char*>(&config_.header + 1),
518 current_filled);
519 current_filled += sizeof(config_.header);
520
521 /* Fill all ScreenLayout structs. */
522 for (unsigned int i = 0; i < config_.localizations.size(); ++i) {
523 for (unsigned int j = 0; j < config_.localizations[i].size(); ++j) {
524 ScreenConfig &screen = config_.screens_map[config_.localizations[i][j]];
525 for (unsigned int k = 0;
526 k < MAX_IMAGE_IN_LAYOUT && !screen.image_names[k].empty();
527 ++k) {
528 screen.data.images[k].image_info_offset =
529 config_.images_map[screen.image_names[k]].offset;
530 }
531 std::copy(reinterpret_cast<char*>(&screen.data),
532 reinterpret_cast<char*>(&screen.data + 1),
533 current_filled);
534 current_filled += sizeof(screen.data);
535 }
536 }
537
538 /* Fill all ImageInfo structs and image contents. */
539 for (StrImageConfigMap::iterator it = config_.images_map.begin();
540 it != config_.images_map.end();
541 ++it) {
542 current_filled = bmpblock_.begin() + it->second.offset;
543 std::copy(reinterpret_cast<char*>(&it->second.data),
544 reinterpret_cast<char*>(&it->second.data + 1),
545 current_filled);
546 current_filled += sizeof(it->second.data);
547 std::copy(it->second.compressed_content.begin(),
548 it->second.compressed_content.end(),
549 current_filled);
550 }
551}
552
553void BmpBlockUtil::write_to_bmpblock(const char *filename) {
554 assert(!bmpblock_.empty());
555
556 FILE *fp = fopen(filename, "wb");
557 if (!fp) {
558 perror(filename);
559 exit(errno);
560 }
561
562 int r = fwrite(bmpblock_.c_str(), bmpblock_.size(), 1, fp);
563 fclose(fp);
564 if (r != 1) {
565 perror(filename);
566 exit(errno);
567 }
568}
569
570} // namespace vboot_reference
571
572#ifdef WITH_UTIL_MAIN
573
Bill Richardson794d4d42011-02-10 19:13:10 -0800574//////////////////////////////////////////////////////////////////////////////
575// Command line utilities.
576
577extern "C" {
578#include "bmpblk_util.h"
579}
Bill Richardsond55085d2011-02-04 15:01:37 -0800580
581using vboot_reference::BmpBlockUtil;
582
583// utility function: provide usage of this utility and exit.
584static void usagehelp_exit(const char *prog_name) {
585 printf(
Bill Richardsond55085d2011-02-04 15:01:37 -0800586 "\n"
Bill Richardson794d4d42011-02-10 19:13:10 -0800587 "To create a new BMPBLOCK file using config from YAML file:\n"
Bill Richardsond55085d2011-02-04 15:01:37 -0800588 "\n"
Bill Richardson794d4d42011-02-10 19:13:10 -0800589 " %s [-z NUM] -c YAML BMPBLOCK\n"
Bill Richardsond55085d2011-02-04 15:01:37 -0800590 "\n"
Bill Richardson794d4d42011-02-10 19:13:10 -0800591 " -z NUM = compression algorithm to use\n"
592 " 0 = none\n"
593 " 1 = EFIv1\n"
Tom Wai-Hong Tamee2bc912011-02-17 12:58:58 +0800594 " 2 = LZMA1\n"
Bill Richardson794d4d42011-02-10 19:13:10 -0800595 "\n", prog_name);
596 printf(
597 "To display the contents of a BMPBLOCK:\n"
598 "\n"
Bill Richardson61362d62011-02-14 10:28:03 -0800599 " %s [-y] BMPBLOCK\n"
600 "\n"
601 " -y = display as yaml\n"
Bill Richardson794d4d42011-02-10 19:13:10 -0800602 "\n", prog_name);
603 printf(
604 "To unpack a BMPBLOCK file:\n"
605 "\n"
606 " %s -x [-d DIR] [-f] BMPBLOCK\n"
607 "\n"
608 " -d DIR = directory to use (default '.')\n"
609 " -f = force overwriting existing files\n"
610 "\n", prog_name);
Bill Richardsond55085d2011-02-04 15:01:37 -0800611 exit(1);
612}
613
614///////////////////////////////////////////////////////////////////////
615// main
616
617int main(int argc, char *argv[]) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800618
Bill Richardson794d4d42011-02-10 19:13:10 -0800619 const char *prog_name = strrchr(argv[0], '/');
620 if (prog_name)
621 prog_name++;
622 else
623 prog_name = argv[0];
Bill Richardsond55085d2011-02-04 15:01:37 -0800624
Bill Richardson61362d62011-02-14 10:28:03 -0800625 int overwrite = 0, extract_mode = 0;
Bill Richardson794d4d42011-02-10 19:13:10 -0800626 int compression = 0;
Bill Richardson61362d62011-02-14 10:28:03 -0800627 int set_compression = 0;
Bill Richardson794d4d42011-02-10 19:13:10 -0800628 const char *config_fn = 0, *bmpblock_fn = 0, *extract_dir = ".";
Bill Richardson61362d62011-02-14 10:28:03 -0800629 int show_as_yaml = 0;
Bill Richardsond55085d2011-02-04 15:01:37 -0800630
Bill Richardson794d4d42011-02-10 19:13:10 -0800631 int opt;
632 opterr = 0; // quiet
633 int errorcnt = 0;
634 char *e = 0;
Bill Richardson61362d62011-02-14 10:28:03 -0800635 while ((opt = getopt(argc, argv, ":c:xz:fd:y")) != -1) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800636 switch (opt) {
Bill Richardson794d4d42011-02-10 19:13:10 -0800637 case 'c':
638 config_fn = optarg;
639 break;
640 case 'x':
641 extract_mode = 1;
642 break;
Bill Richardson61362d62011-02-14 10:28:03 -0800643 case 'y':
644 show_as_yaml = 1;
645 break;
Bill Richardson794d4d42011-02-10 19:13:10 -0800646 case 'z':
647 compression = (int)strtoul(optarg, &e, 0);
648 if (!*optarg || (e && *e)) {
649 fprintf(stderr, "%s: invalid argument to -%c: \"%s\"\n",
650 prog_name, opt, optarg);
651 errorcnt++;
652 }
653 if (compression >= MAX_COMPRESS) {
654 fprintf(stderr, "%s: compression type must be less than %d\n",
Bill Richardson61362d62011-02-14 10:28:03 -0800655 prog_name, MAX_COMPRESS);
Bill Richardson794d4d42011-02-10 19:13:10 -0800656 errorcnt++;
657 }
Bill Richardson61362d62011-02-14 10:28:03 -0800658 set_compression = 1;
Bill Richardson794d4d42011-02-10 19:13:10 -0800659 break;
660 case 'f':
Bill Richardson61362d62011-02-14 10:28:03 -0800661 overwrite = 1;
Bill Richardson794d4d42011-02-10 19:13:10 -0800662 break;
663 case 'd':
664 extract_dir= optarg;
665 break;
666 case ':':
667 fprintf(stderr, "%s: missing argument to -%c\n",
668 prog_name, optopt);
669 errorcnt++;
670 break;
671 default:
672 fprintf(stderr, "%s: unrecognized switch: -%c\n",
673 prog_name, optopt);
674 errorcnt++;
675 break;
Bill Richardsond55085d2011-02-04 15:01:37 -0800676 }
677 }
678 argc -= optind;
679 argv += optind;
680
Bill Richardson794d4d42011-02-10 19:13:10 -0800681 if (argc >= 1) {
682 bmpblock_fn = argv[0];
Bill Richardsond55085d2011-02-04 15:01:37 -0800683 } else {
Bill Richardson794d4d42011-02-10 19:13:10 -0800684 fprintf(stderr, "%s: missing BMPBLOCK name\n", prog_name);
685 errorcnt++;
686 }
687
688 if (errorcnt)
Bill Richardsond55085d2011-02-04 15:01:37 -0800689 usagehelp_exit(prog_name);
Bill Richardsond55085d2011-02-04 15:01:37 -0800690
Bill Richardson794d4d42011-02-10 19:13:10 -0800691 BmpBlockUtil util;
692
693 if (config_fn) {
Bill Richardson61362d62011-02-14 10:28:03 -0800694 if (set_compression)
695 util.force_compression(compression);
Bill Richardson794d4d42011-02-10 19:13:10 -0800696 util.load_from_config(config_fn);
Bill Richardsond55085d2011-02-04 15:01:37 -0800697 util.pack_bmpblock();
Bill Richardson794d4d42011-02-10 19:13:10 -0800698 util.write_to_bmpblock(bmpblock_fn);
Bill Richardsond55085d2011-02-04 15:01:37 -0800699 }
700
Bill Richardson794d4d42011-02-10 19:13:10 -0800701 else if (extract_mode) {
Bill Richardson61362d62011-02-14 10:28:03 -0800702 return dump_bmpblock(bmpblock_fn, 1, extract_dir, overwrite);
703 } else {
704 return dump_bmpblock(bmpblock_fn, show_as_yaml, 0, 0);
Bill Richardsond55085d2011-02-04 15:01:37 -0800705 }
706
Bill Richardsond55085d2011-02-04 15:01:37 -0800707 return 0;
708}
709
710#endif // WITH_UTIL_MAIN