Allow yaml file to specify default compression for images

BUG=chromium-os:11490
TEST=manual

  cd src/platform/vboot_reference
  make
  make runbmptests

Change-Id: Ia887fc1aa1de873c6da6c04995bc0a9ad6b364aa

Review URL: http://codereview.chromium.org/6541001
diff --git a/tests/bitmaps/TestBmpBlock.py b/tests/bitmaps/TestBmpBlock.py
index 265bd24..ca7ef4a 100755
--- a/tests/bitmaps/TestBmpBlock.py
+++ b/tests/bitmaps/TestBmpBlock.py
@@ -73,6 +73,7 @@
 class TestPackUnpack(unittest.TestCase):
 
   def setUp(self):
+    self._cwd = os.getcwd()
     rc, out, err = runprog('/bin/rm', '-rf', './FOO_DIR', 'FOO')
     self.assertEqual(0, rc)
 
@@ -110,7 +111,39 @@
     """Create, unpack, recreate with LZMA compression"""
     self.doPackUnpackZ('2');
 
+  def doPackUnpackImplicitZ(self, comp, noncomp):
+    """Create with given compression, unpack, repack without specifying"""
+    # create with the specified compression scheme
+    rc, out, err = runprog(prog, '-z', comp, '-c', 'case_simple.yaml', 'FOO')
+    self.assertEqual(0, rc)
+    # unpack. yaml file should have compression scheme in it
+    rc, out, err = runprog(prog, '-f', '-x', '-d', './FOO_DIR', 'FOO')
+    self.assertEqual(0, rc)
+    os.chdir('./FOO_DIR')
+    # create with no compression specified, should use default from yaml
+    rc, out, err = runprog(prog, '-c', 'config.yaml', 'BAR')
+    self.assertEqual(0, rc)
+    # so new output should match original
+    rc, out, err = runprog('/usr/bin/cmp', '../FOO', 'BAR')
+    self.assertEqual(0, rc)
+    # Now make sure that specifying a compression arg will override the default
+    for mycomp in noncomp:
+      # create with compression scheme different from default
+      rc, out, err = runprog(prog, '-z', str(mycomp), '-c', 'config.yaml', 'BAR')
+      self.assertEqual(0, rc)
+      # should be different binary
+      rc, out, err = runprog('/usr/bin/cmp', '../FOO', 'BAR')
+      self.assertNotEqual(0, rc)
+    os.chdir('..')
+
+  def testPackUnpackImplicitZ(self):
+    """Create, unpack, recreate with implicit compression"""
+    self._allowed = range(3)
+    for c in self._allowed:
+      self.doPackUnpackImplicitZ(str(c), [x for x in self._allowed if x != c])
+
   def tearDown(self):
+    os.chdir(self._cwd)
     rc, out, err = runprog('/bin/rm', '-rf', './FOO_DIR', 'FOO')
     self.assertEqual(0, rc)
 
diff --git a/utility/bmpblk_util.c b/utility/bmpblk_util.c
index f2b0adc..5c94d8e 100644
--- a/utility/bmpblk_util.c
+++ b/utility/bmpblk_util.c
@@ -264,11 +264,22 @@
 
   // Write out yaml
   fprintf(yfp, "bmpblock: %d.%d\n", hdr->major_version, hdr->minor_version);
-  fprintf(yfp, "images:\n");
   offset = sizeof(BmpBlockHeader) +
     (sizeof(ScreenLayout) *
      hdr->number_of_localizations *
      hdr->number_of_screenlayouts);
+  // FIXME(chromium-os:12134): The bmbblock structure allows each image to be
+  // compressed differently, but we haven't provided a way for the yaml file to
+  // specify that. Additionally, we allow the yaml file to specify a default
+  // compression scheme for all images, but only if that line appears in the
+  // yaml file before any images. Accordingly, we'll just check the first image
+  // to see if it has any compression, and if it does, we'll write that out as
+  // the default. When this bug is fixed, we should just write each image's
+  // compression setting separately.
+  img = (ImageInfo *)(ptr + offset);
+  if (img->compression)
+    fprintf(yfp, "compression: %d\n", img->compression);
+  fprintf(yfp, "images:\n");
   for(i=0; i<hdr->number_of_imageinfos; i++) {
     img = (ImageInfo *)(ptr + offset);
     sprintf(image_name, "img_%08x.bmp", offset);
diff --git a/utility/bmpblk_utility.cc b/utility/bmpblk_utility.cc
index 8605dde..078a5ce 100644
--- a/utility/bmpblk_utility.cc
+++ b/utility/bmpblk_utility.cc
@@ -139,6 +139,8 @@
         keyword = (char*)event.data.scalar.value;
         if (keyword == "bmpblock") {
           parse_bmpblock(parser);
+        } else if (keyword == "compression") {
+          parse_compression(parser);
         } else if (keyword == "images") {
           parse_images(parser);
         } else if (keyword == "screens") {
@@ -174,6 +176,24 @@
   yaml_event_delete(&event);
 }
 
+void BmpBlockUtil::parse_compression(yaml_parser_t *parser) {
+  yaml_event_t event;
+  yaml_parser_parse(parser, &event);
+  if (event.type != YAML_SCALAR_EVENT) {
+    error("Syntax error in parsing bmpblock.\n");
+  }
+  char *comp_str = (char *)event.data.scalar.value;
+  char *e = 0;
+  uint32_t comp = (uint32_t)strtoul(comp_str, &e, 0);
+  if (!*comp_str || (e && *e) || comp >= MAX_COMPRESS) {
+    error("Invalid compression specified in config file\n");
+  }
+  if (!set_compression_) {
+    compression_ = comp;
+  }
+  yaml_event_delete(&event);
+}
+
 void BmpBlockUtil::parse_images(yaml_parser_t *parser) {
   yaml_event_t event;
   string image_name, image_filename;
diff --git a/utility/include/bmpblk_utility.h b/utility/include/bmpblk_utility.h
index 53f135d..9c708ba 100644
--- a/utility/include/bmpblk_utility.h
+++ b/utility/include/bmpblk_utility.h
@@ -93,6 +93,7 @@
   void parse_config(yaml_parser_t *parser);
   void parse_first_layer(yaml_parser_t *parser);
   void parse_bmpblock(yaml_parser_t *parser);
+  void parse_compression(yaml_parser_t *parser);
   void parse_images(yaml_parser_t *parser);
   void parse_layout(yaml_parser_t *parser, ScreenConfig &screen);
   void parse_screens(yaml_parser_t *parser);