imgtool save to format of given output filename

Updates imgtool to infer, based on output image filename, which format
should be use for output file. Filenames that end with extension `.hex`
are saved in Intel HEX, otherwise saves a binary image.

Signed-off-by: Fabio Utzig <utzig@apache.org>
diff --git a/scripts/imgtool/image.py b/scripts/imgtool/image.py
index 6116420..3ccd86f 100644
--- a/scripts/imgtool/image.py
+++ b/scripts/imgtool/image.py
@@ -81,40 +81,24 @@
         header = struct.pack(e + 'HH', TLV_INFO_MAGIC, TLV_INFO_SIZE + len(self.buf))
         return header + bytes(self.buf)
 
+
 class Image():
-    @classmethod
-    def load(cls, path, pad_header=False, **kwargs):
-        """Load an image from a given file"""
-        ext = os.path.splitext(path)[1][1:].lower()
-        if ext == INTEL_HEX_EXT:
-            cls = HexImage
-        else:
-            cls = BinImage
 
-        obj = cls(**kwargs)
-        obj.payload, obj.base_addr = obj.load(path)
-
-        # Add the image header if needed.
-        if pad_header and obj.header_size > 0:
-            if obj.base_addr:
-                # Adjust base_addr for new header
-                obj.base_addr -= obj.header_size
-            obj.payload = (b'\000' * obj.header_size) + obj.payload
-
-        obj.check()
-        return obj
-
-    def __init__(self, version=None, header_size=IMAGE_HEADER_SIZE, pad=0,
-                 align=1, slot_size=0, max_sectors=DEFAULT_MAX_SECTORS,
-                 overwrite_only=False, endian="little"):
+    def __init__(self, version=None, header_size=IMAGE_HEADER_SIZE,
+                 pad_header=False, pad=False, align=1, slot_size=0,
+                 max_sectors=DEFAULT_MAX_SECTORS, overwrite_only=False,
+                 endian="little"):
         self.version = version or versmod.decode_version("0")
-        self.header_size = header_size or IMAGE_HEADER_SIZE
+        self.header_size = header_size
+        self.pad_header = pad_header
         self.pad = pad
         self.align = align
         self.slot_size = slot_size
         self.max_sectors = max_sectors
         self.overwrite_only = overwrite_only
         self.endian = endian
+        self.base_addr = None
+        self.payload = []
 
     def __repr__(self):
         return "<Image version={}, header_size={}, base_addr={}, \
@@ -131,6 +115,43 @@
                     self.__class__.__name__,
                     len(self.payload))
 
+    def load(self, path):
+        """Load an image from a given file"""
+        ext = os.path.splitext(path)[1][1:].lower()
+        if ext == INTEL_HEX_EXT:
+            ih = IntelHex(path)
+            self.payload = ih.tobinarray()
+            self.base_addr = ih.minaddr()
+        else:
+            with open(path, 'rb') as f:
+                self.payload = f.read()
+
+        # Add the image header if needed.
+        if self.pad_header and self.header_size > 0:
+            if self.base_addr:
+                # Adjust base_addr for new header
+                self.base_addr -= self.header_size
+            self.payload = (b'\000' * self.header_size) + self.payload
+
+        self.check()
+
+    def save(self, path):
+        """Save an image from a given file"""
+        if self.pad:
+            self.pad_to(self.slot_size)
+
+        ext = os.path.splitext(path)[1][1:].lower()
+        if ext == INTEL_HEX_EXT:
+            # input was in binary format, but HEX needs to know the base addr
+            if self.base_addr is None:
+                raise Exception("Input file does not provide a base address")
+            h = IntelHex()
+            h.frombytes(bytes=self.payload, offset=self.base_addr)
+            h.tofile(path, 'hex')
+        else:
+            with open(path, 'wb') as f:
+                f.write(self.payload)
+
     def check(self):
         """Perform some sanity checking of the image."""
         # If there is a header requested, make sure that the image
@@ -243,25 +264,3 @@
         pbytes += b'\xff' * (tsize - len(boot_magic))
         pbytes += boot_magic
         self.payload += pbytes
-
-
-class HexImage(Image):
-
-    def load(self, path):
-        ih = IntelHex(path)
-        return ih.tobinarray(), ih.minaddr()
-
-    def save(self, path):
-        h = IntelHex()
-        h.frombytes(bytes = self.payload, offset = self.base_addr)
-        h.tofile(path, 'hex')
-
-class BinImage(Image):
-
-    def load(self, path):
-        with open(path, 'rb') as f:
-            return f.read(), None
-
-    def save(self, path):
-        with open(path, 'wb') as f:
-            f.write(self.payload)
diff --git a/scripts/imgtool/main.py b/scripts/imgtool/main.py
index 1917a97..a03a164 100755
--- a/scripts/imgtool/main.py
+++ b/scripts/imgtool/main.py
@@ -142,15 +142,16 @@
 @click.option('--align', type=click.Choice(['1', '2', '4', '8']),
               required=True)
 @click.option('-k', '--key', metavar='filename')
-@click.command(help='Create a signed or unsigned image')
+@click.command(help='''Create a signed or unsigned image\n
+               INFILE and OUTFILE are parsed as Intel HEX if the params have
+               .hex extension, othewise binary format is used''')
 def sign(key, align, version, header_size, pad_header, slot_size, pad,
          max_sectors, overwrite_only, endian, encrypt, infile, outfile):
-    img = image.Image.load(infile, version=decode_version(version),
-                           header_size=header_size, pad_header=pad_header,
-                           pad=pad, align=int(align), slot_size=slot_size,
-                           max_sectors=max_sectors,
-                           overwrite_only=overwrite_only,
-                           endian=endian)
+    img = image.Image(version=decode_version(version), header_size=header_size,
+                      pad_header=pad_header, pad=pad, align=int(align),
+                      slot_size=slot_size, max_sectors=max_sectors,
+                      overwrite_only=overwrite_only, endian=endian)
+    img.load(infile)
     key = load_key(key) if key else None
     enckey = load_key(encrypt) if encrypt else None
     if enckey:
@@ -159,10 +160,6 @@
         if key and not isinstance(key, (keys.RSA2048, keys.RSA2048Public)):
             raise Exception("Encryption with sign only available with RSA")
     img.create(key, enckey)
-
-    if pad:
-        img.pad_to(slot_size)
-
     img.save(outfile)