Add generation of .mi4 files - the generic PortalPlayer firmware format used by the iriver H10, Sansa E200 etc.  Based on the documentation available at http://daniel.haxx.se/sansa/mi4.html and examination of files produced by mkmi4.sh


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@10816 a1c6a512-1295-4272-9138-f99709370657
diff --git a/tools/Makefile b/tools/Makefile
index 68fcd4b..53b1200 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -10,17 +10,18 @@
 LDFLAGS := -g
 
 CLEANALL := scramble descramble iriver sh2d bmp2rb rdf2binary convbdf	\
- generate_rocklatin mkboot ipod_fw codepages uclpack
+ generate_rocklatin mkboot ipod_fw codepages uclpack mi4
 
 all:
 	@echo "Run make in your build directory!"
 
-scramble: scramble.o iriver.o
+scramble: scramble.o iriver.o mi4.o
 descramble: descramble.o iriver.o
 
 scramble.o: scramble.c iriver.h
 descramble.o: descramble.c iriver.h
 iriver.o: iriver.c iriver.h
+mi4.o: mi4.c mi4.h
 
 sh2d: sh2d.c
 
diff --git a/tools/mi4.c b/tools/mi4.c
new file mode 100644
index 0000000..b0fff98
--- /dev/null
+++ b/tools/mi4.c
@@ -0,0 +1,188 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2006 Dave Chapman
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * CRC32 implementation taken from:
+ *
+ * efone - Distributed internet phone system.
+ *
+ * (c) 1999,2000 Krzysztof Dabrowski
+ * (c) 1999,2000 ElysiuM deeZine
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+/* based on implementation by Finn Yannick Jacobs */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/* crc_tab[] -- this crcTable is being build by chksum_crc32GenTab().
+ *		so make sure, you call it before using the other
+ *		functions!
+ */
+static unsigned int crc_tab[256];
+
+/* chksum_crc() -- to a given block, this one calculates the
+ *				crc32-checksum until the length is
+ *				reached. the crc32-checksum will be
+ *				the result.
+ */
+static unsigned int chksum_crc32 (unsigned char *block, unsigned int length)
+{
+   register unsigned long crc;
+   unsigned long i;
+
+   crc = 0;
+   for (i = 0; i < length; i++)
+   {
+      crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF];
+   }
+   return (crc);
+}
+
+/* chksum_crc32gentab() --      to a global crc_tab[256], this one will
+ *				calculate the crcTable for crc32-checksums.
+ *				it is generated to the polynom [..]
+ */
+
+static void chksum_crc32gentab (void)
+{
+   unsigned long crc, poly;
+   int i, j;
+
+   poly = 0xEDB88320L;
+   for (i = 0; i < 256; i++)
+   {
+      crc = i;
+      for (j = 8; j > 0; j--)
+      {
+	 if (crc & 1)
+	 {
+	    crc = (crc >> 1) ^ poly;
+	 }
+	 else
+	 {
+	    crc >>= 1;
+	 }
+      }
+      crc_tab[i] = crc;
+   }
+}
+
+static void int2le(unsigned int val, unsigned char* addr)
+{
+    addr[0] = val & 0xFF;
+    addr[1] = (val >> 8) & 0xff;
+    addr[2] = (val >> 16) & 0xff;
+    addr[3] = (val >> 24) & 0xff;
+}
+
+int mi4_encode(char *iname, char *oname, int version)
+{
+    size_t len;
+    int length;
+    int mi4length;
+    FILE *file;
+    unsigned int crc = 0;
+    unsigned char *outbuf;
+
+    file = fopen(iname, "rb");
+    if (!file) {
+       perror(iname);
+       return -1;
+    }
+    fseek(file,0,SEEK_END);
+    length = ftell(file);
+    
+    fseek(file,0,SEEK_SET);
+
+    /* Add 4 bytes to length (for magic), the 0x200 byte header and
+       then round to an even 0x400 bytes
+     */
+    mi4length = (length+4+0x200+0x3ff)&~0x3ff;
+
+    outbuf = malloc(mi4length);
+
+    if ( !outbuf ) {
+       printf("out of memory!\n");
+       return -1;
+    }
+
+    /* Clear the buffer to zero */
+    memset(outbuf, 0, mi4length);
+
+    len = fread(outbuf+0x200, 1, length, file);
+    if(len < length) {
+        perror(iname);
+        return -2;
+    }
+    fclose(file);
+
+    /* We need to write some data into the actual image - before calculating
+       the CRC. */
+    int2le(0x00000100,   &outbuf[0x2e0]);   /* magic */
+    int2le(0x000000ec,   &outbuf[0x2e4]);   /* magic */
+    int2le(length+4,     &outbuf[0x2e8]);   /* length plus 0xaa55aa55 */
+
+    int2le(0xaa55aa55,   &outbuf[0x200+length]);  /* More Magic */
+
+    /* Calculate CRC32 checksum */
+    chksum_crc32gentab ();
+    crc = chksum_crc32 (outbuf+28,mi4length-28);
+
+    strncpy((char *)outbuf, "PPOS", 4);     /* Magic */
+    int2le(version,      &outbuf[0x04]);    /* .mi4 version */
+    int2le(length+4,     &outbuf[0x08]);    /* Length of firmware plus magic */
+    int2le(crc,          &outbuf[0x0c]);    /* CRC32 of mi4 file */
+    int2le(0x00000002,   &outbuf[0x10]);    /* Encryption type: 2 = TEA */
+    int2le(mi4length,    &outbuf[0x14]);    /* Total .mi4 length */
+    int2le(mi4length-0x200, &outbuf[0x18]); /* Length of plaintext part */
+
+    /* v3 files require a dummy DSA signature */
+    if (version == 0x00010301) {
+        outbuf[0x2f]=0x01;                
+    }
+
+    file = fopen(oname, "wb");
+    if (!file) {
+        perror(oname);
+        return -3;
+    }
+    
+    len = fwrite(outbuf, 1, mi4length, file);
+    if(len < length) {
+        perror(oname);
+        return -4;
+    }
+
+    fclose(file);
+
+    fprintf(stderr, "File encoded successfully\n" );
+
+    return 0;
+}
diff --git a/tools/mi4.h b/tools/mi4.h
new file mode 100644
index 0000000..ec3a734
--- /dev/null
+++ b/tools/mi4.h
@@ -0,0 +1,25 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2006 Dave Chapman
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _MI4_H
+#define _MI4_H
+
+int mi4_encode(char *iname, char *oname, int version);
+
+#endif
diff --git a/tools/scramble.c b/tools/scramble.c
index c3eb178..d1e3d1d 100644
--- a/tools/scramble.c
+++ b/tools/scramble.c
@@ -22,6 +22,7 @@
 #include <stdbool.h>
 #include <string.h>
 #include "iriver.h"
+#include "mi4.h"
 
 int iaudio_encode(char *iname, char *oname, char *idstring);
 int ipod_encode(char *iname, char *oname, int fw_ver, bool fake_rsrc);
@@ -82,6 +83,8 @@
            "\t-ipod3g ipod firmware partition format (3rd Gen)\n"
            "\t-ipod4g ipod firmware partition format (4th Gen, Mini, Nano, Photo/Color)\n"
            "\t-ipod5g ipod firmware partition format (5th Gen - aka Video)\n"
+           "\t-mi4v2  PortalPlayer .mi4 format (revision 010201)\n"
+           "\t-mi4v3  PortalPlayer .mi4 format (revision 010301)\n"
            "\t-add=X  Rockbox generic \"add-up\" checksum format\n"
            "\t        (X values: h100, h120, h140, h300, ipco, nano, ipvd\n"
            "\t                   ip3g, ip4g, mini, x5, h10, h10_5gb)\n"
@@ -241,6 +244,16 @@
         oname = argv[3];
         return ipod_encode(iname, oname, 3, true);  /* Firmware image v3 */
     }
+    else if(!strcmp(argv[1], "-mi4v2")) {
+        iname = argv[2];
+        oname = argv[3];
+        return mi4_encode(iname, oname, 0x00010201);
+    }
+    else if(!strcmp(argv[1], "-mi4v3")) {
+        iname = argv[2];
+        oname = argv[3];
+        return mi4_encode(iname, oname, 0x00010301);
+    }
     
     /* open file */
     file = fopen(iname,"rb");