FS#10253 : mkamsboot v1.0

- Bump version to 1.0
- Add Clipv2 target
- Make mkamsboot work as a library (work by domonoky : FS#10185, with a few modifications by me)
  . Use a macro with variadic arguments for error cases in functions which might error.
  . Add detailed descriptions to functions exported by the library (in the header file)
- modify bin2c.c to produce only one pair of .c/.h files with several files embedded in it
- move files needing to be built by an ARM cross compiler into dualboot/
- commit produced .c/.h files (containing nrv2e_d8.S and dualboot.S built for Clip, Fuze, e200v2, c200v2, m200v4, Clipv2)
- Write a real README file
- cosmetics: indent dualboot.S properly, remove trailing spaces, limit lines to 80 characters
- comments: add/correct comments in dualboot.S and mkamsboot.c
- move back extract_fw.c to utils/AMS/hacking

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@21118 a1c6a512-1295-4272-9138-f99709370657
diff --git a/rbutil/mkamsboot/Makefile b/rbutil/mkamsboot/Makefile
index 5a62650..1a5ffd0 100644
--- a/rbutil/mkamsboot/Makefile
+++ b/rbutil/mkamsboot/Makefile
@@ -1,90 +1,48 @@
-CC=gcc
-
 # We use the UCL code available in the Rockbox tools/ directory
-CFLAGS=-I../../tools/ucl/include
+CFLAGS=-I../../tools/ucl/include -Wall
 LIBUCL=../../tools/ucl/src/libucl.a
 
-# Edit the following variables (plus copy/paste another set of rules) when
-# adding a new target.  mkamsboot.c also needs to be edited to refer to these
-# new images.
+ifeq ($(findstring CYGWIN,$(shell uname)),CYGWIN)
+OUTPUT=mkamsboot.exe
+CFLAGS+=-mno-cygwin
+else
+ifeq ($(findstring MINGW,$(shell uname)),MINGW)
+OUTPUT=mkamsboot.exe
+else
+ifeq ($(findstring mingw,$(CC)),mingw)
+OUTPUT=mkamsboot.exe
+else
+OUTPUT=mkamsboot
+endif
+endif
+endif
 
-BOOTIMAGES = dualboot_clip.o dualboot_e200v2.o dualboot_c200v2.o dualboot_m200v4.o dualboot_fuze.o
-BOOTHEADERS = dualboot_clip.h dualboot_e200v2.h dualboot_c200v2.h dualboot_m200v4.h dualboot_fuze.h
+CC?= gcc
 
-CLIPFILES = dualboot_clip.arm-o dualboot_clip.o dualboot_clip.c dualboot_clip.h
-
-E200V2FILES = dualboot_e200v2.arm-o dualboot_e200v2.o dualboot_e200v2.c \
-              dualboot_e200v2.h
-
-M200V4FILES = dualboot_m200v4.arm-o dualboot_m200v4.o dualboot_m200v4.arm-bin \
-              dualboot_m200v4.c dualboot_m200v4.h
-
-C200V2FILES = dualboot_c200v2.arm-o dualboot_c200v2.o dualboot_c200v2.c \
-              dualboot_c200v2.h
-
-FUZEFILES = dualboot_fuze.arm-o dualboot_fuze.o dualboot_fuze.c dualboot_fuze.h
-
-all: mkamsboot
-
-# Dualboot bootloaders
-
-dualboot_clip.arm-o: dualboot.S
-	arm-elf-gcc -DSANSA_CLIP -c -o dualboot_clip.arm-o dualboot.S
-
-dualboot_fuze.arm-o: dualboot.S
-	arm-elf-gcc -DSANSA_FUZE -c -o dualboot_fuze.arm-o dualboot.S
-
-dualboot_e200v2.arm-o: dualboot.S
-	arm-elf-gcc -DSANSA_E200V2 -c -o dualboot_e200v2.arm-o dualboot.S
-
-dualboot_m200v4.arm-o: dualboot.S
-	arm-elf-gcc -DSANSA_M200V4 -c -o dualboot_m200v4.arm-o dualboot.S
-
-dualboot_c200v2.arm-o: dualboot.S
-	arm-elf-gcc -DSANSA_C200V2 -c -o dualboot_c200v2.arm-o dualboot.S
-
-# Rules for the ucl unpack function
-nrv2e_d8.arm-o: nrv2e_d8.S
-	arm-elf-gcc -DPURE_THUMB -c -o nrv2e_d8.arm-o nrv2e_d8.S
-
-
+all: $(OUTPUT)
 
 $(LIBUCL):
 	make -C ../../tools/ucl/src libucl.a
 
+# This file can be generated in the dualboot/ directory
+dualboot.o: dualboot.c
+	$(CC) $(CFLAGS) -c -o dualboot.o dualboot.c
+
 md5.o: md5.c md5.h
 	$(CC) $(CFLAGS) -c -o md5.o -W -Wall md5.c
 
-mkamsboot.o: mkamsboot.c $(BOOTHEADERS) nrv2e_d8.h md5.h
+mkamsboot.o: mkamsboot.c dualboot.h md5.h
 	$(CC) $(CFLAGS) -c -o mkamsboot.o -W -Wall mkamsboot.c
 
-mkamsboot: mkamsboot.o $(BOOTIMAGES) nrv2e_d8.o md5.o $(LIBUCL)
-	$(CC) -o mkamsboot mkamsboot.o $(BOOTIMAGES) nrv2e_d8.o md5.o $(LIBUCL)
+$(OUTPUT): mkamsboot.o md5.o dualboot.o $(LIBUCL)
+	$(CC) $(CFLAGS) -o $(OUTPUT) mkamsboot.o md5.o dualboot.o $(LIBUCL)
 
-# Rules for the ARM code embedded in mkamsboot - assemble, link, then extract
-# the binary code and finally convert to .c/.h for linking with mkamsboot
+libmkamsboot.o: mkamsboot.c dualboot.h md5.h
+	$(CC) $(CFLAGS) -DLIB -c -o libmkamsboot.o -W -Wall mkamsboot.c
 
-%.arm-elf: %.arm-o
-	arm-elf-ld -e 0 -Ttext=0 -o $@ $<
-
-%.arm-bin: %.arm-elf
-	arm-elf-objcopy -O binary $< $@
-
-%.c %.h: %.arm-bin bin2c
-	./bin2c $< $*
-
-# Generic host rule.
-%.o: %.c
-	$(CC) $(CFLAGS) -c -o $@ $<
-
-# Cancel the implicit .S -> .o rule
-%.o: %.S
-
-bin2c: bin2c.c
-	$(CC) -o bin2c bin2c.c
+libmkamsboot.a: libmkamsboot.o md5.o dualboot.o
+	$(AR) ruv libmkamsboot.a libmkamsboot.o md5.o dualboot.o
 
 clean:
-	rm -f mkamsboot mkamsboot.o nrv2e_d8.arm-o nrv2e_d8.arm-elf \
-	nrv2e_d8.arm-bin *~ bin2c nrv2e_d8.c nrv2e_d8.h nrv2e_d8.o md5.o \
-	$(BOOTIMAGES) $(CLIPFILES) $(E200V2FILES) $(M200V4FILES) $(FUZEFILES) \
-	$(C200V2FILES)
+	rm -f $(OUTPUT) mkamsboot.o *~ md5.o dualboot.o \
+	libmkamsboot.o libmkamsboot.a
diff --git a/rbutil/mkamsboot/README b/rbutil/mkamsboot/README
index bd0d2a3..53f1fba 100644
--- a/rbutil/mkamsboot/README
+++ b/rbutil/mkamsboot/README
@@ -3,4 +3,63 @@
 
 A tool to inject a bootloader into a Sansa V2 (AMS) firmware file.
 
-See comments in mkamsboot.c and dualboot.S for more information.
+
+Usage
+-----
+
+mkamsboot <firmware file> <boot file> <output file>
+
+<firmware file> is an original Sansa firmware file obtained from the Sansa
+    forums for example : http://forums.sandisk.com/sansa/?category.id=devices
+
+<boot file> is the code you want to execute (a rockbox bootloader), previously
+    scrambled with tools/scramble utility.
+
+<output file> is the resulting firmware file which you'll have to copy on your
+    Sansa player. See "Firmware filenames".
+
+
+Supported models
+----------------
+
+Sansa Clip  :   firmware version starting with "01."
+Sansa Clipv2:   firmware version starting with "02."
+Sansa Fuze  :   firmware version starting with "01."
+Sansa E200v2:   firmware version starting with "03."
+Sansa C200v2:   firmware version starting with "03."
+Sansa M200v4:   firmware version starting with "4."
+
+
+Firmware filenames
+------------------
+
+For the firmware upgrade to happen, the firmware has to be named specially:
+
+clip v2 :   m30pa.bin
+clip    :   m300a.bin
+fuze    :   fuzea.bin
+e200v2  :   e200pa.bin
+c200v2  :   c200pa.bin
+m200v4  :   m200a.bin
+
+
+Dual-Boot
+---------
+
+  The purpose of this program is to provide dual-boot between the original
+firmware and the new (rockbox) firmware.
+
+  By default the player will boot into the new firmware.
+
+  To boot into the Original Firmware, you need to press the Left key.
+  ***Note : on the c200v2 you need to press the Right key.
+
+  The player will boot into the Original Firmware as well if it is powered up by
+inserting an usb cable.
+  ***Note : on the m200v4 powering up by usb will cause booting into the new
+firmware
+
+Hacking
+-------
+
+See comments in mkamsboot.c and dualboot/dualboot.S for more information.
diff --git a/rbutil/mkamsboot/bin2c.c b/rbutil/mkamsboot/bin2c.c
deleted file mode 100644
index dce8013..0000000
--- a/rbutil/mkamsboot/bin2c.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/***************************************************************************
- *             __________               __   ___.
- *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
- *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
- *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
- *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
- *                     \/            \/     \/    \/            \/
- * $Id$
- *
- * Copyright (C) 2007 Dave Chapman
- *
- * 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.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdlib.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-static off_t filesize(int fd)
-{
-    struct stat buf;
-
-    fstat(fd,&buf);
-    return buf.st_size;
-}
-
-static int write_cfile(const unsigned char* buf, off_t len, const char* cname)
-{
-    char filename[256];
-    FILE* fp;
-    int i;
-
-    snprintf(filename,256,"%s.c",cname);
-
-    fp = fopen(filename,"w+");
-    if (fp == NULL) {
-        fprintf(stderr,"Couldn't open %s\n",filename);
-        return -1;
-    }
-
-    fprintf(fp,"/* Generated by bin2c */\n\n");
-    fprintf(fp,"unsigned char %s[%d] = {",cname,len);
-
-    for (i=0;i<len;i++) {
-        if ((i % 16) == 0) {
-           fprintf(fp,"\n    ");
-        }
-        if (i == (len-1)) {
-            fprintf(fp,"0x%02x",buf[i]);
-        } else {
-            fprintf(fp,"0x%02x, ",buf[i]);
-        }
-    }
-    fprintf(fp,"\n};\n");
-
-    fclose(fp);
-    return 0;
-}
-
-static int write_hfile(off_t len, const char* cname)
-{
-    char filename[256];
-    FILE* fp;
-
-    snprintf(filename,256,"%s.h",cname);
-    fp = fopen(filename,"w+");
-    if (fp == NULL) {
-        fprintf(stderr,"Couldn't open %s\n",filename);
-        return -1;
-    }
-
-    fprintf(fp,"/* Generated by bin2c */\n\n");
-    fprintf(fp,"extern unsigned char %s[%d];\n",cname,len);
-    fclose(fp);
-    return 0;
-}
-
-int main (int argc, char* argv[])
-{
-    char* infile;
-    char* cname;
-    int fd;
-    unsigned char* buf;
-    int len;
-    int n;
-
-    if (argc != 3) {
-        fprintf(stderr,"Usage: bin2c file cname\n");
-        return 0;
-    }
-
-    infile=argv[1];
-    cname=argv[2];
-
-    fd = open(infile,O_RDONLY|O_BINARY);
-    if (fd < 0) {
-        fprintf(stderr,"Can not open %s\n",infile);
-        return 0;
-    }
-
-    len = filesize(fd);
-
-    buf = malloc(len);
-    n = read(fd,buf,len);
-    if (n < len) {
-        fprintf(stderr,"Short read, aborting\n");
-        return 0;
-    }
-    close(fd);
-
-    if (write_cfile(buf,len,cname) < 0) {
-        return -1;
-    }
-    if (write_hfile(len,cname) < 0) {
-        return -1;
-    }
-
-    return 0;
-}
diff --git a/rbutil/mkamsboot/dualboot.S b/rbutil/mkamsboot/dualboot.S
deleted file mode 100644
index a4d0469..0000000
--- a/rbutil/mkamsboot/dualboot.S
+++ /dev/null
@@ -1,184 +0,0 @@
-/***************************************************************************
- *             __________               __   ___.
- *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
- *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
- *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
- *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
- *                     \/            \/     \/    \/            \/
- * $Id$
- *
- * Copyright (C) 2008 Rafaël Carré
- *
- * 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.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-
-.text
-
-.set IRAM_SIZE, 0x50000
-
-.set GPIOA,     0xC80B0000
-.set GPIOB,     0xC80C0000
-.set GPIOC,     0xC80D0000
-.set GPIOD,     0xC80E0000
-.set CGU_PERI,  0xC80F0014
-
-
-/* Vectors */
-        ldr   pc, =start
-.word   0
-.word   0
-.word   0
-.word   0
-.word   0
-.word   0
-.word   0
-
-/* These values are filled in by mkamsboot - don't move them from offset 0x20 */
-uclunpack_end:   .word   0 /* End of the ucl_unpack function */
-uclunpack_size:  .word   0 /* Size in bytes of the ucl_unpack function */
-
-ucl_of_end:      .word   0 /* End of the ucl-compressed OF image */
-ucl_of_size:     .word   0 /* Size in bytes of the compressed OF image */
-
-ucl_rb_end:      .word   0 /* End of the ucl-compressed RB image */
-ucl_rb_size:     .word   0 /* Size in bytes of the compressed RB image */
-
-
-start:
-        /* First copy the UCL unpack function to the end of RAM */
-        ldr   r0, uclunpack_end     /* Source */
-        ldr   r1, uclunpack_size    /* Source length */
-        sub   r2, r0, r1            /* Source start - 1*/
-
-        ldr   r3, =(IRAM_SIZE-1)    /* Destination end */
-
-uclcopy:
-        ldrb  r4, [r0], #-1
-        strb  r4, [r3], #-1
-        cmp   r2, r0
-        bne   uclcopy
-
-        add   r5, r3, #2            /* r5 is entry point of copy of uclunpack */
-                                    /* function, plus one (for thumb mode */
-
-        /* enable gpio clock */
-        ldr   r0, =CGU_PERI
-        ldr   r1, [r0]
-        orr   r1, r1, #(1<<16)
-        str   r1, [r0]
-
-        
-#ifndef SANSA_M200V4  /* this doesnt work for m200 */         
-        /* we check A3 unconditionally of the model because it seems to be */
-        /* either hold, either usb on every model */
-        /* TODO: make it USB on all AMS Sansas for consistency, USB is safer too */
-
-        ldr   r0, =GPIOA
-        mov   r1, #0
-        str   r1, [r0, #0x400]
-#ifdef SANSA_C200V2
-        ldr   r1, [r0, #0x8]    /* USB is A1 on C200 */
-#elif defined(SANSA_CLIP)
-        ldr   r1, [r0, #0x100]    /* USB is A6 on Clip */
-#else
-        ldr   r1, [r0, #0x20]    /* read pin A3 */
-#endif
-        cmp   r1, #0
-        bne   boot_of
-#endif
-
-        /* here are model specific tests, for dual boot without a computer */
-
-#ifdef SANSA_CLIP
-        /* LEFT button */
-.set row, (1<<5) /* enable output on C5 */
-        ldr   r0, =GPIOC
-        mov   r1, #row
-        str   r1, [r0, #0x400]
-        str   r1, [r0, #(4*row)]
-
-.set col, (1<<0) /* read keyscan column B0 */
-        ldr   r0, =GPIOB
-        mov   r1, #0
-        str   r1, [r0, #0x400]
-        ldr   r1, [r0, #(4*col)]
-
-        cmp   r1, #0
-        bne   boot_of
-#elif defined(SANSA_E200V2) || defined(SANSA_FUZE)
-        /* LEFT button */
-        ldr   r0, =GPIOC
-        mov   r1, #0
-        str   r1, [r0, #0x400]
-        ldr   r1, [r0, #0x20]    /* read pin C3 */
-
-        cmp   r1, #0             /* C3 = #0 means button pressed */
-        beq   boot_of
-#elif defined(SANSA_C200V2)
-        /* check for RIGHT on C6, should maybe changed to LEFT as soon as it
-         * known in which pin that is in order for consistency  */
-        ldr   r0, =GPIOC
-        mov   r1, #0
-        str   r1, [r0, #0x400]      /* set pin to output */
-
-        ldr   r1, [r0, #256]        /* 1<<(6+2) */
-        cmp   r1, #0                /* C6 low means button pressed */
-        beq   boot_of
-#elif defined(SANSA_M200V4)
-        /* LEFT button */
-.set row, (1<<5) /* enable output on A5 */
-        ldr   r0, =GPIOA
-        mov   r1, #row
-        str   r1, [r0, #0x400]
-        str   r1, [r0, #(4*row)]
-
-.set col, (1<<0) /* read keyscan column A0 */
-        ldr   r2, [r0, #(4*col)]
-
-        /* check value read (1 means button pressed) */
-        cmp   r2, #0
-        bne   boot_of
-#else
-        #error No target-specific key check defined!
-#endif
-
-        /* No button was held, so we boot rockbox */
-        ldr   r0, ucl_rb_end   /* Address of compressed image */
-        ldr   r1, ucl_rb_size    /* Compressed size */
-        b     decompress
-
-boot_of:
-        ldr   r0, ucl_of_end     /* Address of compressed image */
-        ldr   r1, ucl_of_size    /* Compressed size */
-
-
-decompress:
-        /* At this point:                                              */
-        /* r5 = entry point (plus one for thumb) of uclunpack function */
-        /* r3 = destination_end for copy of UCL image                  */
-        /* r0 = source_end for UCL image to copy                       */
-        /* r1 = size of UCL image to copy                              */
-
-        sub   r4, r3, r1      /* r4 := destination_start - 1 */
-        
-fw_copy:
-        ldrb  r2, [r0], #-1
-        strb  r2, [r3], #-1
-        cmp   r3, r4          /* Stop when we reached dest_start-1 */
-        bne   fw_copy
-
-        /* Call the ucl decompress function, which will branch to 0x0 */
-        /* on completion */
-        add   r0, r3, #1      /* r0 := Start of compressed image */
-                              /* r1 already contains compressed size */
-        mov   r2, #0          /* r2 := Destination for unpacking */
-        bx    r5              /* Branch to uclunpack, switching to thumb */
-
-        /* never reached */
diff --git a/rbutil/mkamsboot/dualboot.c b/rbutil/mkamsboot/dualboot.c
new file mode 100644
index 0000000..2eda393
--- /dev/null
+++ b/rbutil/mkamsboot/dualboot.c
@@ -0,0 +1,118 @@
+/* Generated by bin2c */
+
+unsigned char nrv2e_d8[168] = {
+    0x0f, 0x18, 0x01, 0x24, 0x65, 0x42, 0xe4, 0x07, 0x05, 0x26, 0x36, 0x02, 0x0a, 0xe0, 0x00, 0x20,
+    0x00, 0x47, 0x04, 0x78, 0x64, 0x41, 0x01, 0x30, 0x24, 0x06, 0xf7, 0x46, 0x03, 0x78, 0x01, 0x30,
+    0x13, 0x70, 0x01, 0x32, 0x24, 0x19, 0xfe, 0x46, 0xf3, 0xd0, 0xf7, 0xd2, 0x01, 0x21, 0x04, 0xe0,
+    0x01, 0x39, 0x24, 0x19, 0xfe, 0x46, 0xec, 0xd0, 0x49, 0x41, 0x24, 0x19, 0xfe, 0x46, 0xe8, 0xd0,
+    0x49, 0x41, 0x24, 0x19, 0xfe, 0x46, 0xe4, 0xd0, 0xf2, 0xd3, 0xcb, 0x1e, 0x00, 0x21, 0x08, 0xd3,
+    0x1b, 0x02, 0x05, 0x78, 0x01, 0x30, 0x1d, 0x43, 0xed, 0x43, 0xd8, 0xd0, 0x6d, 0x10, 0x13, 0xd2,
+    0x03, 0xe0, 0x24, 0x19, 0xfe, 0x46, 0xd4, 0xd0, 0x0e, 0xd2, 0x01, 0x21, 0x24, 0x19, 0xfe, 0x46,
+    0xcf, 0xd0, 0x09, 0xd2, 0x24, 0x19, 0xfe, 0x46, 0xcb, 0xd0, 0x49, 0x41, 0x24, 0x19, 0xfe, 0x46,
+    0xc7, 0xd0, 0xf7, 0xd3, 0x04, 0x31, 0x04, 0xe0, 0x24, 0x19, 0xfe, 0x46, 0xc1, 0xd0, 0x49, 0x41,
+    0x02, 0x31, 0xee, 0x42, 0x00, 0xd2, 0x01, 0x31, 0x13, 0x78, 0x53, 0x5d, 0x13, 0x70, 0x01, 0x32,
+    0x01, 0x39, 0xfa, 0xd1, 0xbe, 0xe7, 0x00, 0x00
+};
+unsigned char dualboot_clip[248] = {
+    0xd8, 0xf0, 0x9f, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x1f, 0xe5, 0x20, 0x10, 0x1f, 0xe5,
+    0x01, 0x20, 0x40, 0xe0, 0x98, 0x30, 0x9f, 0xe5, 0x01, 0x40, 0x50, 0xe4, 0x01, 0x40, 0x43, 0xe4,
+    0x00, 0x00, 0x52, 0xe1, 0xfb, 0xff, 0xff, 0x1a, 0x02, 0x50, 0x83, 0xe2, 0x84, 0x00, 0x9f, 0xe5,
+    0x00, 0x10, 0x90, 0xe5, 0x01, 0x18, 0x81, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x78, 0x00, 0x9f, 0xe5,
+    0x00, 0x10, 0xa0, 0xe3, 0x00, 0x14, 0x80, 0xe5, 0x00, 0x11, 0x90, 0xe5, 0x00, 0x00, 0x51, 0xe3,
+    0x0c, 0x00, 0x00, 0x1a, 0x64, 0x00, 0x9f, 0xe5, 0x20, 0x10, 0xa0, 0xe3, 0x00, 0x14, 0x80, 0xe5,
+    0x80, 0x10, 0x80, 0xe5, 0x58, 0x00, 0x9f, 0xe5, 0x00, 0x10, 0xa0, 0xe3, 0x00, 0x14, 0x80, 0xe5,
+    0x04, 0x10, 0x90, 0xe5, 0x00, 0x00, 0x51, 0xe3, 0x02, 0x00, 0x00, 0x1a, 0x84, 0x00, 0x1f, 0xe5,
+    0x84, 0x10, 0x1f, 0xe5, 0x01, 0x00, 0x00, 0xea, 0x98, 0x00, 0x1f, 0xe5, 0x98, 0x10, 0x1f, 0xe5,
+    0x01, 0x40, 0x43, 0xe0, 0x01, 0x20, 0x50, 0xe4, 0x01, 0x20, 0x43, 0xe4, 0x04, 0x00, 0x53, 0xe1,
+    0xfb, 0xff, 0xff, 0x1a, 0x01, 0x00, 0x83, 0xe2, 0x00, 0x20, 0xa0, 0xe3, 0x15, 0xff, 0x2f, 0xe1,
+    0x38, 0x00, 0x00, 0x00, 0xff, 0xff, 0x04, 0x00, 0x14, 0x00, 0x0f, 0xc8, 0x00, 0x00, 0x0b, 0xc8,
+    0x00, 0x00, 0x0d, 0xc8, 0x00, 0x00, 0x0c, 0xc8
+};
+unsigned char dualboot_e200v2[228] = {
+    0xc8, 0xf0, 0x9f, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x1f, 0xe5, 0x20, 0x10, 0x1f, 0xe5,
+    0x01, 0x20, 0x40, 0xe0, 0x88, 0x30, 0x9f, 0xe5, 0x01, 0x40, 0x50, 0xe4, 0x01, 0x40, 0x43, 0xe4,
+    0x00, 0x00, 0x52, 0xe1, 0xfb, 0xff, 0xff, 0x1a, 0x02, 0x50, 0x83, 0xe2, 0x74, 0x00, 0x9f, 0xe5,
+    0x00, 0x10, 0x90, 0xe5, 0x01, 0x18, 0x81, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x68, 0x00, 0x9f, 0xe5,
+    0x00, 0x10, 0xa0, 0xe3, 0x00, 0x14, 0x80, 0xe5, 0x20, 0x10, 0x90, 0xe5, 0x00, 0x00, 0x51, 0xe3,
+    0x08, 0x00, 0x00, 0x1a, 0x54, 0x00, 0x9f, 0xe5, 0x00, 0x10, 0xa0, 0xe3, 0x00, 0x14, 0x80, 0xe5,
+    0x20, 0x10, 0x90, 0xe5, 0x00, 0x00, 0x51, 0xe3, 0x02, 0x00, 0x00, 0x0a, 0x74, 0x00, 0x1f, 0xe5,
+    0x74, 0x10, 0x1f, 0xe5, 0x01, 0x00, 0x00, 0xea, 0x88, 0x00, 0x1f, 0xe5, 0x88, 0x10, 0x1f, 0xe5,
+    0x01, 0x40, 0x43, 0xe0, 0x01, 0x20, 0x50, 0xe4, 0x01, 0x20, 0x43, 0xe4, 0x04, 0x00, 0x53, 0xe1,
+    0xfb, 0xff, 0xff, 0x1a, 0x01, 0x00, 0x83, 0xe2, 0x00, 0x20, 0xa0, 0xe3, 0x15, 0xff, 0x2f, 0xe1,
+    0x38, 0x00, 0x00, 0x00, 0xff, 0xff, 0x04, 0x00, 0x14, 0x00, 0x0f, 0xc8, 0x00, 0x00, 0x0b, 0xc8,
+    0x00, 0x00, 0x0d, 0xc8
+};
+unsigned char dualboot_c200v2[228] = {
+    0xc8, 0xf0, 0x9f, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x1f, 0xe5, 0x20, 0x10, 0x1f, 0xe5,
+    0x01, 0x20, 0x40, 0xe0, 0x88, 0x30, 0x9f, 0xe5, 0x01, 0x40, 0x50, 0xe4, 0x01, 0x40, 0x43, 0xe4,
+    0x00, 0x00, 0x52, 0xe1, 0xfb, 0xff, 0xff, 0x1a, 0x02, 0x50, 0x83, 0xe2, 0x74, 0x00, 0x9f, 0xe5,
+    0x00, 0x10, 0x90, 0xe5, 0x01, 0x18, 0x81, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x68, 0x00, 0x9f, 0xe5,
+    0x00, 0x10, 0xa0, 0xe3, 0x00, 0x14, 0x80, 0xe5, 0x08, 0x10, 0x90, 0xe5, 0x00, 0x00, 0x51, 0xe3,
+    0x08, 0x00, 0x00, 0x1a, 0x54, 0x00, 0x9f, 0xe5, 0x00, 0x10, 0xa0, 0xe3, 0x00, 0x14, 0x80, 0xe5,
+    0x00, 0x11, 0x90, 0xe5, 0x00, 0x00, 0x51, 0xe3, 0x02, 0x00, 0x00, 0x0a, 0x74, 0x00, 0x1f, 0xe5,
+    0x74, 0x10, 0x1f, 0xe5, 0x01, 0x00, 0x00, 0xea, 0x88, 0x00, 0x1f, 0xe5, 0x88, 0x10, 0x1f, 0xe5,
+    0x01, 0x40, 0x43, 0xe0, 0x01, 0x20, 0x50, 0xe4, 0x01, 0x20, 0x43, 0xe4, 0x04, 0x00, 0x53, 0xe1,
+    0xfb, 0xff, 0xff, 0x1a, 0x01, 0x00, 0x83, 0xe2, 0x00, 0x20, 0xa0, 0xe3, 0x15, 0xff, 0x2f, 0xe1,
+    0x38, 0x00, 0x00, 0x00, 0xff, 0xff, 0x04, 0x00, 0x14, 0x00, 0x0f, 0xc8, 0x00, 0x00, 0x0b, 0xc8,
+    0x00, 0x00, 0x0d, 0xc8
+};
+unsigned char dualboot_m200v4[204] = {
+    0xb4, 0xf0, 0x9f, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x1f, 0xe5, 0x20, 0x10, 0x1f, 0xe5,
+    0x01, 0x20, 0x40, 0xe0, 0x74, 0x30, 0x9f, 0xe5, 0x01, 0x40, 0x50, 0xe4, 0x01, 0x40, 0x43, 0xe4,
+    0x00, 0x00, 0x52, 0xe1, 0xfb, 0xff, 0xff, 0x1a, 0x02, 0x50, 0x83, 0xe2, 0x60, 0x00, 0x9f, 0xe5,
+    0x00, 0x10, 0x90, 0xe5, 0x01, 0x18, 0x81, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x54, 0x00, 0x9f, 0xe5,
+    0x20, 0x10, 0xa0, 0xe3, 0x00, 0x14, 0x80, 0xe5, 0x80, 0x10, 0x80, 0xe5, 0x04, 0x20, 0x90, 0xe5,
+    0x00, 0x00, 0x52, 0xe3, 0x02, 0x00, 0x00, 0x1a, 0x60, 0x00, 0x1f, 0xe5, 0x60, 0x10, 0x1f, 0xe5,
+    0x01, 0x00, 0x00, 0xea, 0x74, 0x00, 0x1f, 0xe5, 0x74, 0x10, 0x1f, 0xe5, 0x01, 0x40, 0x43, 0xe0,
+    0x01, 0x20, 0x50, 0xe4, 0x01, 0x20, 0x43, 0xe4, 0x04, 0x00, 0x53, 0xe1, 0xfb, 0xff, 0xff, 0x1a,
+    0x01, 0x00, 0x83, 0xe2, 0x00, 0x20, 0xa0, 0xe3, 0x15, 0xff, 0x2f, 0xe1, 0x38, 0x00, 0x00, 0x00,
+    0xff, 0xff, 0x04, 0x00, 0x14, 0x00, 0x0f, 0xc8, 0x00, 0x00, 0x0b, 0xc8
+};
+unsigned char dualboot_fuze[228] = {
+    0xc8, 0xf0, 0x9f, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x1f, 0xe5, 0x20, 0x10, 0x1f, 0xe5,
+    0x01, 0x20, 0x40, 0xe0, 0x88, 0x30, 0x9f, 0xe5, 0x01, 0x40, 0x50, 0xe4, 0x01, 0x40, 0x43, 0xe4,
+    0x00, 0x00, 0x52, 0xe1, 0xfb, 0xff, 0xff, 0x1a, 0x02, 0x50, 0x83, 0xe2, 0x74, 0x00, 0x9f, 0xe5,
+    0x00, 0x10, 0x90, 0xe5, 0x01, 0x18, 0x81, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x68, 0x00, 0x9f, 0xe5,
+    0x00, 0x10, 0xa0, 0xe3, 0x00, 0x14, 0x80, 0xe5, 0x20, 0x10, 0x90, 0xe5, 0x00, 0x00, 0x51, 0xe3,
+    0x08, 0x00, 0x00, 0x1a, 0x54, 0x00, 0x9f, 0xe5, 0x00, 0x10, 0xa0, 0xe3, 0x00, 0x14, 0x80, 0xe5,
+    0x20, 0x10, 0x90, 0xe5, 0x00, 0x00, 0x51, 0xe3, 0x02, 0x00, 0x00, 0x0a, 0x74, 0x00, 0x1f, 0xe5,
+    0x74, 0x10, 0x1f, 0xe5, 0x01, 0x00, 0x00, 0xea, 0x88, 0x00, 0x1f, 0xe5, 0x88, 0x10, 0x1f, 0xe5,
+    0x01, 0x40, 0x43, 0xe0, 0x01, 0x20, 0x50, 0xe4, 0x01, 0x20, 0x43, 0xe4, 0x04, 0x00, 0x53, 0xe1,
+    0xfb, 0xff, 0xff, 0x1a, 0x01, 0x00, 0x83, 0xe2, 0x00, 0x20, 0xa0, 0xe3, 0x15, 0xff, 0x2f, 0xe1,
+    0x38, 0x00, 0x00, 0x00, 0xff, 0xff, 0x04, 0x00, 0x14, 0x00, 0x0f, 0xc8, 0x00, 0x00, 0x0b, 0xc8,
+    0x00, 0x00, 0x0d, 0xc8
+};
+unsigned char dualboot_clipv2[272] = {
+    0xf4, 0xf0, 0x9f, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x1f, 0xe5, 0x20, 0x10, 0x1f, 0xe5,
+    0x01, 0x20, 0x40, 0xe0, 0xb4, 0x30, 0x9f, 0xe5, 0x01, 0x40, 0x50, 0xe4, 0x01, 0x40, 0x43, 0xe4,
+    0x00, 0x00, 0x52, 0xe1, 0xfb, 0xff, 0xff, 0x1a, 0x02, 0x50, 0x83, 0xe2, 0xa0, 0x00, 0x9f, 0xe5,
+    0x00, 0x10, 0x90, 0xe5, 0x01, 0x18, 0x81, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x94, 0x00, 0x9f, 0xe5,
+    0x00, 0x10, 0xa0, 0xe3, 0x00, 0x14, 0x80, 0xe5, 0x00, 0x11, 0x90, 0xe5, 0x00, 0x00, 0x51, 0xe3,
+    0x13, 0x00, 0x00, 0x1a, 0x80, 0x00, 0x9f, 0xe5, 0x38, 0x10, 0xa0, 0xe3, 0x00, 0x14, 0x80, 0xe5,
+    0x08, 0x10, 0xa0, 0xe3, 0x20, 0x10, 0x80, 0xe5, 0x10, 0x10, 0xa0, 0xe3, 0x40, 0x10, 0x80, 0xe5,
+    0x20, 0x10, 0xa0, 0xe3, 0x80, 0x10, 0x80, 0xe5, 0x00, 0x10, 0xa0, 0xe3, 0x40, 0x10, 0x80, 0xe5,
+    0x05, 0x10, 0xa0, 0xe3, 0x01, 0x10, 0x51, 0xe2, 0xfd, 0xff, 0xff, 0x1a, 0x04, 0x10, 0x90, 0xe5,
+    0x00, 0x00, 0x51, 0xe3, 0x02, 0x00, 0x00, 0x0a, 0xa0, 0x00, 0x1f, 0xe5, 0xa0, 0x10, 0x1f, 0xe5,
+    0x01, 0x00, 0x00, 0xea, 0xb4, 0x00, 0x1f, 0xe5, 0xb4, 0x10, 0x1f, 0xe5, 0x01, 0x40, 0x43, 0xe0,
+    0x01, 0x20, 0x50, 0xe4, 0x01, 0x20, 0x43, 0xe4, 0x04, 0x00, 0x53, 0xe1, 0xfb, 0xff, 0xff, 0x1a,
+    0x01, 0x00, 0x83, 0xe2, 0x00, 0x20, 0xa0, 0xe3, 0x15, 0xff, 0x2f, 0xe1, 0x38, 0x00, 0x00, 0x00,
+    0xff, 0xff, 0x0f, 0x00, 0x14, 0x00, 0x0f, 0xc8, 0x00, 0x00, 0x0b, 0xc8, 0x00, 0x00, 0x0e, 0xc8
+};
diff --git a/rbutil/mkamsboot/dualboot.h b/rbutil/mkamsboot/dualboot.h
new file mode 100644
index 0000000..b88a182
--- /dev/null
+++ b/rbutil/mkamsboot/dualboot.h
@@ -0,0 +1,9 @@
+/* Generated by bin2c */
+
+extern unsigned char nrv2e_d8[168];
+extern unsigned char dualboot_clip[248];
+extern unsigned char dualboot_e200v2[228];
+extern unsigned char dualboot_c200v2[228];
+extern unsigned char dualboot_m200v4[204];
+extern unsigned char dualboot_fuze[228];
+extern unsigned char dualboot_clipv2[272];
diff --git a/rbutil/mkamsboot/dualboot/Makefile b/rbutil/mkamsboot/dualboot/Makefile
new file mode 100644
index 0000000..06b90c7
--- /dev/null
+++ b/rbutil/mkamsboot/dualboot/Makefile
@@ -0,0 +1,54 @@
+CC=gcc
+
+# Edit the following variables (plus copy/paste another set of rules) when
+# adding a new target.  mkamsboot.c also needs to be edited to refer to these
+# new images.
+
+BOOTOBJS = nrv2e_d8.o dualboot_clip.o dualboot_e200v2.o dualboot_c200v2.o dualboot_m200v4.o dualboot_fuze.o dualboot_clipv2.o
+BOOTBINS = nrv2e_d8.arm-bin dualboot_clip.arm-bin dualboot_e200v2.arm-bin dualboot_c200v2.arm-bin dualboot_m200v4.arm-bin dualboot_fuze.arm-bin dualboot_clipv2.arm-bin
+
+all: dualboot.h
+
+dualboot.h: $(BOOTBINS)
+
+# Dualboot bootloaders
+
+dualboot_clip.o: dualboot.S
+	arm-elf-gcc -DSANSA_CLIP -c -o dualboot_clip.o dualboot.S
+
+dualboot_fuze.o: dualboot.S
+	arm-elf-gcc -DSANSA_FUZE -c -o dualboot_fuze.o dualboot.S
+
+dualboot_e200v2.o: dualboot.S
+	arm-elf-gcc -DSANSA_E200V2 -c -o dualboot_e200v2.o dualboot.S
+
+dualboot_m200v4.o: dualboot.S
+	arm-elf-gcc -DSANSA_M200V4 -c -o dualboot_m200v4.o dualboot.S
+
+dualboot_c200v2.o: dualboot.S
+	arm-elf-gcc -DSANSA_C200V2 -c -o dualboot_c200v2.o dualboot.S
+
+dualboot_clipv2.o: dualboot.S
+	arm-elf-gcc -DSANSA_CLIPV2 -c -o dualboot_clipv2.o dualboot.S
+
+# Rules for the ucl unpack function
+nrv2e_d8.o: nrv2e_d8.S
+	arm-elf-gcc -DPURE_THUMB -c -o nrv2e_d8.o nrv2e_d8.S
+
+# Rules for the ARM code embedded in mkamsboot - assemble, link, then extract
+# the binary code and finally convert to .h for building in mkamsboot
+
+%.arm-elf: %.o
+	arm-elf-ld -e 0 -Ttext=0 -o $@ $<
+
+%.arm-bin: %.arm-elf
+	arm-elf-objcopy -O binary $< $@
+
+dualboot.c dualboot.h: $(BOOTBINS) bin2c
+	./bin2c dualboot $(BOOTBINS)
+
+bin2c: bin2c.c
+	$(CC) -o bin2c bin2c.c
+
+clean:
+	rm -f *~ bin2c $(BOOTBINS) $(BOOTOBJS) dualboot.c dualboot.h
diff --git a/rbutil/mkamsboot/dualboot/bin2c.c b/rbutil/mkamsboot/dualboot/bin2c.c
new file mode 100644
index 0000000..e8619ea
--- /dev/null
+++ b/rbutil/mkamsboot/dualboot/bin2c.c
@@ -0,0 +1,131 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2007 Dave Chapman
+ *
+ * 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+static off_t filesize(int fd)
+{
+    struct stat buf;
+
+    fstat(fd,&buf);
+    return buf.st_size;
+}
+
+static void write_cfile(const unsigned char* buf, off_t len, FILE* fp, const char *name)
+{
+    int i;
+
+    fprintf(fp,"unsigned char %s[%ld] = {",name,len);
+
+    for (i=0;i<len;i++) {
+        if ((i % 16) == 0) {
+           fprintf(fp,"\n    ");
+        }
+        if (i == (len-1)) {
+            fprintf(fp,"0x%02x",buf[i]);
+        } else if ((i % 16) == 15) {
+            fprintf(fp,"0x%02x,",buf[i]);
+        } else {
+            fprintf(fp,"0x%02x, ",buf[i]);
+        }
+    }
+    fprintf(fp,"\n};\n");
+}
+
+int main (int argc, char* argv[])
+{
+    char* cname;
+    int i;
+    FILE *cfile, *hfile;
+    char cfilename[256], hfilename[256];
+
+    if (argc < 3) {
+        fprintf(stderr,"Usage: bin2c cname file1 [file2 [file3 ...]]\n");
+        return 1;
+    }
+
+    cname=argv[1];
+
+    snprintf(cfilename,256,"%s.c",cname);
+    cfile = fopen(cfilename,"w+");
+    if (cfile == NULL) {
+        fprintf(stderr,"Couldn't open %s\n",cfilename);
+        return 2;
+    }
+
+    snprintf(hfilename,256,"%s.h",cname);
+    hfile = fopen(hfilename,"w+");
+    if (hfile == NULL) {
+        fprintf(stderr,"Couldn't open %s\n",hfilename);
+        fclose(cfile);
+        return 3;
+    }
+
+    fprintf(cfile,"/* Generated by bin2c */\n\n");
+    fprintf(hfile,"/* Generated by bin2c */\n\n");
+
+    for(i=0; i < argc - 2; i++) {
+        unsigned char* buf;
+        off_t len;
+        char *ext;
+        char *array = argv[2+i];
+
+        int fd = open(array,O_RDONLY|O_BINARY);
+        if (fd < 0) {
+            fprintf(stderr,"Can not open %s\n",argv[2+i]);
+            fclose(cfile);
+            fclose(hfile);
+            return 4;
+        }
+
+        len = filesize(fd);
+
+        buf = malloc(len);
+        if (read(fd,buf,len) < len) {
+            fprintf(stderr,"Short read, aborting\n");
+            return 5;
+        }
+
+        /* remove file extension */
+        ext = strchr (array, '.');
+        if (ext != NULL)
+            *ext = '\0';
+        write_cfile (buf, len, cfile, array);
+        fprintf(hfile,"extern unsigned char %s[%ld];\n",array,len);
+
+        close(fd);
+    }
+
+    fclose(cfile);
+    fclose(hfile);
+
+    return 0;
+}
diff --git a/rbutil/mkamsboot/dualboot/dualboot.S b/rbutil/mkamsboot/dualboot/dualboot.S
new file mode 100644
index 0000000..5210ba5
--- /dev/null
+++ b/rbutil/mkamsboot/dualboot/dualboot.S
@@ -0,0 +1,220 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2008 Rafaël Carré
+ *
+ * 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+.text
+
+ #if defined(SANSA_CLIPV2)
+.set RAM_SIZE, 0x100000 /* Use 1MB of SDRAM on v2 firmwares (bigger firmware) */
+#else
+.set RAM_SIZE, 0x50000  /* Use full IRAM on v1 firmwares */
+#endif
+
+/* AS3525 hardware registers */
+.set GPIOA,     0xC80B0000
+.set GPIOB,     0xC80C0000
+.set GPIOC,     0xC80D0000
+.set GPIOD,     0xC80E0000
+.set CGU_PERI,  0xC80F0014
+
+
+/* Vectors */
+
+        ldr   pc, =start    /* reset vector */
+        /* next vectors are unused */
+        .word   0
+        .word   0
+        .word   0
+        .word   0
+        .word   0
+        .word   0
+        .word   0
+
+/* These values are filled in by mkamsboot - don't move them from offset 0x20 */
+
+uclunpack_end:   .word   0 /* End of the ucl_unpack function */
+uclunpack_size:  .word   0 /* Size in bytes of the ucl_unpack function */
+
+ucl_of_end:      .word   0 /* End of the ucl-compressed OF image */
+ucl_of_size:     .word   0 /* Size in bytes of the compressed OF image */
+
+ucl_rb_end:      .word   0 /* End of the ucl-compressed RB image */
+ucl_rb_size:     .word   0 /* Size in bytes of the compressed RB image */
+
+
+start:
+        /* First copy the UCL unpack function to the end of RAM */
+        ldr     r0, uclunpack_end   /* Source */
+        ldr     r1, uclunpack_size  /* Source length */
+        sub     r2, r0, r1          /* Source start - 1*/
+
+        ldr     r3, =(RAM_SIZE-1)  /* Destination end */
+
+uclcopy:
+        ldrb    r4, [r0], #-1
+        strb    r4, [r3], #-1
+        cmp     r2, r0
+        bne     uclcopy
+
+        add     r5, r3, #2          /* r5 is entry point of copy of uclunpack */
+                                    /* function, plus one (for thumb mode */
+
+        /* enable gpio clock */
+        ldr     r0, =CGU_PERI
+        ldr     r1, [r0]
+        orr     r1, r1, #(1<<16)
+        str     r1, [r0]
+
+
+/* TODO : M200V4 */
+#if defined(SANSA_C200V2)
+#define USB_PIN 1
+#elif defined(SANSA_CLIP) || defined(SANSA_CLIPV2)
+#define USB_PIN 6
+#elif defined(SANSA_FUZE) || defined(SANSA_E200V2)
+#define USB_PIN 3
+#endif
+
+#ifdef USB_PIN  /* TODO : remove this check when we'll have an USB driver */
+        ldr     r0, =GPIOA
+        mov     r1, #0
+        str     r1, [r0, #0x400]
+        ldr     r1, [r0, #(4*(1<<USB_PIN))]
+        cmp     r1, #0
+        bne     boot_of
+#endif
+
+        /* Here are model specific tests, for dual boot without a computer */
+        /* All models use left button */
+        /* /!\ Right button for c200v2 (left button is unkwown) */
+
+#ifdef SANSA_CLIP
+.set row, (1<<5) /* enable output on C5 */
+.set col, (1<<0) /* read keyscan column B0 */
+
+        ldr     r0, =GPIOC
+        mov     r1, #row
+        str     r1, [r0, #0x400]
+        str     r1, [r0, #(4*row)]
+
+        ldr     r0, =GPIOB
+        mov     r1, #0
+        str     r1, [r0, #0x400]
+        ldr     r1, [r0, #(4*col)]
+
+        cmp     r1, #0
+        bne     boot_of
+#elif defined(SANSA_CLIPV2)
+.set row, (1<<4) /* enable output on D4 */
+.set col, (1<<0) /* read keyscan column D0 */
+
+        ldr     r0, =GPIOD
+        mov     r1, #((1<<5)|(1<<4)|(1<<3)) /* all rows as output */
+        str     r1, [r0, #0x400]
+
+        /* all rows high */
+        mov     r1, #(1<<3)
+        str     r1, [r0, #(4*(1<<3))]
+        mov     r1, #(1<<4)
+        str     r1, [r0, #(4*(1<<4))]
+        mov     r1, #(1<<5)
+        str     r1, [r0, #(4*(1<<5))]
+
+        mov     r1, #0                      /* button row low */
+        str     r1, [r0, #(4*row)]
+
+        mov     r1, #5                      /* small delay */
+1:      subs    r1, r1, #1
+        bne     1b
+
+        ldr     r1, [r0, #(4*col)]
+
+        cmp     r1, #0
+        beq     boot_of
+#elif defined(SANSA_E200V2) || defined(SANSA_FUZE)
+        ldr     r0, =GPIOC
+        mov     r1, #0
+        str     r1, [r0, #0x400]
+        ldr     r1, [r0, #0x20]    /* read pin C3 */
+
+        cmp     r1, #0             /* C3 = #0 means button pressed */
+        beq     boot_of
+#elif defined(SANSA_C200V2)
+        /* check for RIGHT on C6, should changed to LEFT as soon as it
+         * known in which pin that is in order for consistency  */
+        ldr     r0, =GPIOC
+        mov     r1, #0
+        str     r1, [r0, #0x400]      /* set pin to output */
+
+        ldr     r1, [r0, #256]        /* 1<<(6+2) */
+        cmp     r1, #0                /* C6 low means button pressed */
+        beq     boot_of
+#elif defined(SANSA_M200V4)
+.set row, (1<<5) /* enable output on A5 */
+.set col, (1<<0) /* read keyscan column A0 */
+
+        ldr     r0, =GPIOA
+        mov     r1, #row
+        str     r1, [r0, #0x400]
+        str     r1, [r0, #(4*row)]
+
+        ldr     r2, [r0, #(4*col)]
+
+        /* check value read (1 means button pressed) */
+        cmp     r2, #0
+        bne     boot_of
+#else
+        #error No target-specific key check defined!
+#endif
+
+
+        /* The dualboot button was not held, so we boot rockbox */
+        ldr     r0, ucl_rb_end      /* Address of compressed image */
+        ldr     r1, ucl_rb_size     /* Compressed size */
+        b       decompress
+
+boot_of:
+        ldr     r0, ucl_of_end      /* Address of compressed image */
+        ldr     r1, ucl_of_size     /* Compressed size */
+
+
+decompress:
+        /* At this point:                                              */
+        /* r5 = entry point (plus one for thumb) of uclunpack function */
+        /* r3 = destination_end for copy of UCL image                  */
+        /* r0 = source_end for UCL image to copy                       */
+        /* r1 = size of UCL image to copy                              */
+
+        sub     r4, r3, r1      /* r4 := destination_start - 1 */
+
+fw_copy:
+        ldrb    r2, [r0], #-1
+        strb    r2, [r3], #-1
+        cmp     r3, r4          /* Stop when we reached dest_start-1 */
+        bne     fw_copy
+
+        /* Call the ucl decompress function, which will branch to 0x0 */
+        /* on completion */
+        add     r0, r3, #1      /* r0 := Start of compressed image */
+                                /* r1 already contains compressed size */
+        mov     r2, #0          /* r2 := Destination for unpacking */
+        bx      r5              /* Branch to uclunpack, switching to thumb */
+
+        /* never reached : uclunpack will branch to the reset vector (0x0) */
diff --git a/rbutil/mkamsboot/nrv2e_d8.S b/rbutil/mkamsboot/dualboot/nrv2e_d8.S
similarity index 99%
rename from rbutil/mkamsboot/nrv2e_d8.S
rename to rbutil/mkamsboot/dualboot/nrv2e_d8.S
index 89cb76d..b112ab2 100644
--- a/rbutil/mkamsboot/nrv2e_d8.S
+++ b/rbutil/mkamsboot/dualboot/nrv2e_d8.S
@@ -191,4 +191,3 @@
 /*
 vi:ts=8:et:nowrap
  */
-
diff --git a/rbutil/mkamsboot/md5.c b/rbutil/mkamsboot/md5.c
index 6c5e812..530d8df 100644
--- a/rbutil/mkamsboot/md5.c
+++ b/rbutil/mkamsboot/md5.c
@@ -123,7 +123,7 @@
     P( B, C, D, A, 12, 20, 0x8D2A4C8A );
 
 #undef F
-    
+
 #define F(x,y,z) (x ^ y ^ z)
 
     P( A, B, C, D,  5,  4, 0xFFFA3942 );
diff --git a/rbutil/mkamsboot/mkamsboot.c b/rbutil/mkamsboot/mkamsboot.c
index 65b88af..4210d3b 100644
--- a/rbutil/mkamsboot/mkamsboot.c
+++ b/rbutil/mkamsboot/mkamsboot.c
@@ -24,7 +24,17 @@
 
 /*
 
-Insert a Rockbox bootloader into an AMS original firmware file.
+Insert a Rockbox bootloader into a Sansa AMS original firmware file.
+
+Layout of a Sansa AMS original firmware file:
+
+ ----------------------  0x0
+|        HEADER        |
+|----------------------| 0x400
+|    FIRMWARE BLOCK    |
+|----------------------| 0x400 + firmware block size
+|    LIBRARIES/DATA    |
+ ----------------------  END
 
 We replace the main firmware block (bytes 0x400..0x400+firmware_size)
 as follows:
@@ -50,9 +60,12 @@
 
 This entire block fits into the space previously occupied by the main
 firmware block - the space saved by compressing the OF image is used
-to store the compressed version of the Rockbox bootloader.  The OF
-image is typically about 120KB, which allows us to store a Rockbox
-bootloader with an uncompressed size of about 60KB-70KB.
+to store the compressed version of the Rockbox bootloader.
+
+On version 1 firmwares, the OF image is typically about 120KB, which allows
+us to store a Rockbox bootloader with an uncompressed size of about 60KB-70KB.
+Version 2 firmwares are bigger and are stored in SDRAM (instead of IRAM).
+In both cases, the RAM we are using is mapped at offset 0x0.
 
 mkamsboot then corrects the checksums and writes a new legal firmware
 file which can be installed on the device.
@@ -65,7 +78,7 @@
 
 Then, depending on the detection of the dual-boot keypress, either the
 OF image or the Rockbox image is copied to the end of RAM (just before
-the ucl unpack function) and uncompress it to the start of RAM.
+the ucl unpack function) and uncompressed to the start of RAM.
 
 Finally, the ucl unpack function branches to address 0x0, passing
 execution to the uncompressed firmware.
@@ -73,7 +86,6 @@
 
 */
 
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
@@ -85,27 +97,22 @@
 
 #include <ucl/ucl.h>
 
-/* Headers for ARM code binaries */
-#include "nrv2e_d8.h"
+#include "mkamsboot.h"
+
 #include "md5.h"
 
-#include "dualboot_clip.h"
-#include "dualboot_e200v2.h"
-#include "dualboot_fuze.h"
-#include "dualboot_m200v4.h"
-#include "dualboot_c200v2.h"
+/* Header for ARM code binaries */
+#include "dualboot.h"
 
 /* Win32 compatibility */
 #ifndef O_BINARY
 #define O_BINARY 0
 #endif
 
-#ifndef VERSION
-#define VERSION "0.1"
-#endif
+#define VERSION "1.0"
 
-enum
-{
+/* Supported models */
+enum {
     MODEL_UNKNOWN = -1,
     MODEL_FUZE = 0,
     MODEL_CLIP,
@@ -115,31 +122,31 @@
     MODEL_C200V2,
 };
 
-static const char* model_names[] = 
-{
+/* Descriptive name of these models */
+static const char* model_names[] = {
     "Fuze",
     "Clip",
-    "Clip V2",
+    "Clip v2",
     "e200 v2",
     "m200 v4",
     "c200 v2"
 };
 
-static const unsigned char* bootloaders[] = 
-{
+/* Dualboot functions for these models */
+static const unsigned char* bootloaders[] = {
     dualboot_fuze,
     dualboot_clip,
-    NULL,
+    dualboot_clipv2,
     dualboot_e200v2,
     dualboot_m200v4,
     dualboot_c200v2,
 };
 
-static const int bootloader_sizes[] = 
-{
+/* Size of dualboot functions for these models */
+static const int bootloader_sizes[] = {
     sizeof(dualboot_fuze),
     sizeof(dualboot_clip),
-    0,
+    sizeof(dualboot_clipv2),
     sizeof(dualboot_e200v2),
     sizeof(dualboot_m200v4),
     sizeof(dualboot_c200v2),
@@ -147,11 +154,10 @@
 
 /* Model names used in the Rockbox header in ".sansa" files - these match the
    -add parameter to the "scramble" tool */
-static const char* rb_model_names[] =
-{
+static const char* rb_model_names[] = {
     "fuze",
     "clip",
-    NULL,
+    "clv2",
     "e2v2",
     "m2v4",
     "c2v2",
@@ -159,11 +165,10 @@
 
 /* Model numbers used to initialise the checksum in the Rockbox header in
    ".sansa" files - these are the same as MODEL_NUMBER in config-target.h */
-static const int rb_model_num[] =
-{
+static const int rb_model_num[] = {
     43,
     40,
-    0,
+    60,
     41,
     42,
     44
@@ -172,7 +177,7 @@
 struct md5sums {
     int model;
     char *version;
-    int fw_version;
+    int fw_version; /* version 2 is used in Clipv2 and Fuzev2 firmwares */
     char *md5;
 };
 
@@ -181,27 +186,32 @@
 static struct md5sums sansasums[] = {
     /* NOTE: Different regional versions of the firmware normally only
              differ in the filename - the md5sums are identical */
-    { MODEL_E200V2, "3.01.11",   1, "e622ca8cb6df423f54b8b39628a1f0a3" },
-    { MODEL_E200V2, "3.01.14",   1, "2c1d0383fc3584b2cc83ba8cc2243af6" },
-    { MODEL_E200V2, "3.01.16",   1, "12563ad71b25a1034cf2092d1e0218c4" },
 
-    { MODEL_FUZE, "1.01.11",   1, "cac8ffa03c599330ac02c4d41de66166" },
-    { MODEL_FUZE, "1.01.15",   1, "df0e2c1612727f722c19a3c764cff7f2" },
-    { MODEL_FUZE, "1.01.22",   1, "5aff5486fe8dd64239cc71eac470af98" },
-    { MODEL_FUZE, "1.02.26",   1, "7c632c479461c48c8833baed74eb5e4f" },
+    /*   model       version    fw_version      md5                        */
+    { MODEL_E200V2, "3.01.11",      1,  "e622ca8cb6df423f54b8b39628a1f0a3" },
+    { MODEL_E200V2, "3.01.14",      1,  "2c1d0383fc3584b2cc83ba8cc2243af6" },
+    { MODEL_E200V2, "3.01.16",      1,  "12563ad71b25a1034cf2092d1e0218c4" },
 
-    { MODEL_C200V2, "3.02.05",   1, "b6378ebd720b0ade3fad4dc7ab61c1a5" },
+    { MODEL_FUZE,   "1.01.11",      1,  "cac8ffa03c599330ac02c4d41de66166" },
+    { MODEL_FUZE,   "1.01.15",      1,  "df0e2c1612727f722c19a3c764cff7f2" },
+    { MODEL_FUZE,   "1.01.22",      1,  "5aff5486fe8dd64239cc71eac470af98" },
+    { MODEL_FUZE,   "1.02.26",      1,  "7c632c479461c48c8833baed74eb5e4f" },
 
-    { MODEL_M200V4, "4.00.45",   1, "82e3194310d1514e3bbcd06e84c4add3" },
-    { MODEL_M200V4, "4.01.08-A", 1, "fc9dd6116001b3e6a150b898f1b091f0" },
-    { MODEL_M200V4, "4.01.08-E", 1, "d3fb7d8ec8624ee65bc99f8dab0e2369" },
+    { MODEL_C200V2, "3.02.05",      1,  "b6378ebd720b0ade3fad4dc7ab61c1a5" },
 
-    { MODEL_CLIP, "1.01.17",   1, "12caad785d506219d73f538772afd99e" },
-    { MODEL_CLIP, "1.01.18",   1, "d720b266bd5afa38a198986ef0508a45" },
-    { MODEL_CLIP, "1.01.20",   1, "236d8f75189f468462c03f6d292cf2ac" },
-    { MODEL_CLIP, "1.01.29",   1, "c12711342169c66e209540cd1f27cd26" },
-    { MODEL_CLIP, "1.01.30",   1, "f2974d47c536549c9d8259170f1dbe4d" },
-    { MODEL_CLIP, "1.01.32",   1, "d835d12342500732ffb9c4ee54abec15" },
+    { MODEL_M200V4, "4.00.45",      1,  "82e3194310d1514e3bbcd06e84c4add3" },
+    { MODEL_M200V4, "4.01.08-A",    1,  "fc9dd6116001b3e6a150b898f1b091f0" },
+    { MODEL_M200V4, "4.01.08-E",    1,  "d3fb7d8ec8624ee65bc99f8dab0e2369" },
+
+    { MODEL_CLIP,   "1.01.17",      1,  "12caad785d506219d73f538772afd99e" },
+    { MODEL_CLIP,   "1.01.18",      1,  "d720b266bd5afa38a198986ef0508a45" },
+    { MODEL_CLIP,   "1.01.20",      1,  "236d8f75189f468462c03f6d292cf2ac" },
+    { MODEL_CLIP,   "1.01.29",      1,  "c12711342169c66e209540cd1f27cd26" },
+    { MODEL_CLIP,   "1.01.30",      1,  "f2974d47c536549c9d8259170f1dbe4d" },
+    { MODEL_CLIP,   "1.01.32",      1,  "d835d12342500732ffb9c4ee54abec15" },
+
+    { MODEL_CLIPV2, "2.01.16",      2,  "c57fb3fcbe07c2c9b360f060938f80cb" },
+    { MODEL_CLIPV2, "2.01.32",      2,  "0ad3723e52022509089d938d0fbbf8c5" }
 };
 
 #define NUM_MD5S (sizeof(sansasums)/sizeof(sansasums[0]))
@@ -209,7 +219,7 @@
 static off_t filesize(int fd) {
     struct stat buf;
 
-    if (fstat(fd,&buf) < 0) {
+    if (fstat(fd, &buf) < 0) {
         perror("[ERR]  Checking filesize of input file");
         return -1;
     } else {
@@ -217,30 +227,26 @@
     }
 }
 
-static uint32_t get_uint32le(unsigned char* p)
-{
+static uint32_t get_uint32le(unsigned char* p) {
     return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
 }
 
-static uint32_t get_uint32be(unsigned char* p)
-{
+static uint32_t get_uint32be(unsigned char* p) {
     return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
 }
 
-static void put_uint32le(unsigned char* p, uint32_t x)
-{
+static void put_uint32le(unsigned char* p, uint32_t x) {
     p[0] = x & 0xff;
     p[1] = (x >> 8) & 0xff;
     p[2] = (x >> 16) & 0xff;
     p[3] = (x >> 24) & 0xff;
 }
 
-void calc_MD5(unsigned char* buf, int len, char *md5str)
-{
+void calc_MD5(unsigned char* buf, int len, char *md5str) {
     int i;
     md5_context ctx;
     unsigned char md5sum[16];
-    
+
     md5_starts(&ctx);
     md5_update(&ctx, buf, len);
     md5_finish(&ctx, md5sum);
@@ -249,9 +255,8 @@
         sprintf(md5str + 2*i, "%02x", md5sum[i]);
 }
 
-
-static uint32_t calc_checksum(unsigned char* buf, uint32_t n)
-{
+/* Calculate a simple checksum used in Sansa Original Firmwares */
+static uint32_t calc_checksum(unsigned char* buf, uint32_t n) {
     uint32_t sum = 0;
     uint32_t i;
 
@@ -261,10 +266,8 @@
     return sum;
 }
 
-static int get_model(int model_id)
-{
-    switch(model_id)
-    {
+static int get_model(int model_id) {
+    switch(model_id) {
         case 0x1e:
             return MODEL_FUZE;
         case 0x22:
@@ -282,9 +285,8 @@
     return MODEL_UNKNOWN;
 }
 
-
-static unsigned char* uclpack(unsigned char* inbuf, int insize, int* outsize)
-{
+/* Compress using nrv2e algorithm : Thumb decompressor fits in 168 bytes ! */
+static unsigned char* uclpack(unsigned char* inbuf, int insize, int* outsize) {
     int maxsize;
     unsigned char* outbuf;
     int r;
@@ -295,10 +297,9 @@
     /* Allocate some memory for the output buffer */
     outbuf = malloc(maxsize);
 
-    if (outbuf == NULL) {
+    if (outbuf == NULL)
         return NULL;
-    }
-        
+
     r = ucl_nrv2e_99_compress(
             (const ucl_bytep) inbuf,
             (ucl_uint) insize,
@@ -306,8 +307,7 @@
             (ucl_uintp) outsize,
             0, 10, NULL, NULL);
 
-    if (r != UCL_E_OK || *outsize > maxsize)
-    {
+    if (r != UCL_E_OK || *outsize > maxsize) {
         /* this should NEVER happen, and implies memory corruption */
         fprintf(stderr, "internal error - compression failed: %d\n", r);
         free(outbuf);
@@ -317,42 +317,105 @@
     return outbuf;
 }
 
-static unsigned char* load_file(char* filename, off_t* bufsize)
-{
+#define ERROR(format, ...) \
+    do { \
+        snprintf(errstr, errstrsize, format, __VA_ARGS__); \
+        goto error; \
+    } while(0)
+
+/* Loads a Sansa AMS Original Firmware file into memory */
+unsigned char* load_of_file(
+        char* filename, off_t* bufsize, char* md5sum, int* model,
+        int* fw_version, int* firmware_size, unsigned char** of_packed,
+        int* of_packedsize, char* errstr, int errstrsize
+) {
     int fd;
-    unsigned char* buf;
+    unsigned char* buf =NULL;
     off_t n;
+    unsigned int i=0;
+    uint32_t checksum;
+    int model_id;
+    unsigned int last_word;
 
     fd = open(filename, O_RDONLY|O_BINARY);
     if (fd < 0)
-    {
-        fprintf(stderr,"[ERR]  Could not open %s for reading\n",filename);
-        return NULL;
-    }
+        ERROR("[ERR]  Could not open %s for reading\n", filename);
 
     *bufsize = filesize(fd);
 
     buf = malloc(*bufsize);
-    if (buf == NULL) {
-        fprintf(stderr,"[ERR]  Could not allocate memory for %s\n",filename);
-        return NULL;
-    }
+    if (buf == NULL)
+        ERROR("[ERR]  Could not allocate memory for %s\n", filename);
 
     n = read(fd, buf, *bufsize);
 
-    if (n != *bufsize) {
-        fprintf(stderr,"[ERR]  Could not read file %s\n",filename);
-        return NULL;
+    if (n != *bufsize)
+        ERROR("[ERR]  Could not read file %s\n", filename);
+
+    /* check the file */
+
+    /* Calculate MD5 checksum of OF */
+    calc_MD5(buf, *bufsize, md5sum);
+
+    while ((i < NUM_MD5S) && (strcmp(sansasums[i].md5, md5sum) != 0))
+        i++;
+
+    if (i < NUM_MD5S) {
+        *model = sansasums[i].model;
+        *fw_version = sansasums[i].fw_version;
+    } else {
+        fprintf(stderr, "[WARN] ****** Original firmware unknown ******\n");
+        if (get_uint32le(&buf[0x204])==0x0000f000) {
+            *fw_version = 2;
+            model_id = buf[0x219];
+        } else {
+            *fw_version = 1;
+            model_id = buf[0x215];
+        }
+
+        *model = get_model(model_id);
+
+        if (*model == MODEL_UNKNOWN)
+            ERROR("[ERR]  Unknown firmware - model id 0x%02x\n", model_id);
     }
 
+    /* TODO: Do some more sanity checks on the OF image. Some images (like
+       m200v4) dont have a checksum at the end, only padding (0xdeadbeef). */
+    last_word = *bufsize - 4;
+    checksum = get_uint32le(buf + last_word);
+    if (checksum != 0xefbeadde && checksum != calc_checksum(buf, last_word))
+        ERROR("%s", "[ERR]  Whole file checksum failed\n");
+
+    if (bootloaders[*model] == NULL)
+        ERROR("[ERR]  Unsupported model - \"%s\"\n", model_names[*model]);
+
+
+    /* Get the firmware size */
+    if (*fw_version == 1)
+        *firmware_size = get_uint32le(&buf[0x0c]);
+    else    /* fw_version == 2 */
+        *firmware_size = get_uint32le(&buf[0x10]);
+
+    /* Compress the original firmware image */
+    *of_packed = uclpack(buf + 0x400, *firmware_size, of_packedsize);
+    if (*of_packed == NULL)
+        ERROR("[ERR]  Could not compress %s\n", filename);
+
     return buf;
+
+error:
+    free(buf);
+    return NULL;
 }
 
-
-static unsigned char* load_rockbox_file(char* filename, int model, off_t* bufsize)
-{
+/* Loads a rockbox bootloader file into memory */
+unsigned char* load_rockbox_file(
+            char* filename, int model, int* bufsize, int* rb_packedsize,
+            char* errstr, int errstrsize
+) {
     int fd;
-    unsigned char* buf;
+    unsigned char* buf = NULL;
+    unsigned char* packed = NULL;
     unsigned char header[8];
     uint32_t sum;
     off_t n;
@@ -360,39 +423,28 @@
 
     fd = open(filename, O_RDONLY|O_BINARY);
     if (fd < 0)
-    {
-        fprintf(stderr,"[ERR]  Could not open %s for reading\n",filename);
-        return NULL;
-    }
+        ERROR("[ERR]  Could not open %s for reading\n", filename);
 
     /* Read Rockbox header */
     n = read(fd, header, sizeof(header));
-    if (n != sizeof(header)) {
-        fprintf(stderr,"[ERR]  Could not read file %s\n",filename);
-        return NULL;
-    }
+    if (n != sizeof(header))
+        ERROR("[ERR]  Could not read file %s\n", filename);
 
     /* Check for correct model string */
-    if (memcmp(rb_model_names[model],header + 4,4)!=0) {
-        fprintf(stderr,"[ERR]  Model name \"%s\" not found in %s\n",
-                       rb_model_names[model],filename);
-        return NULL;
-    }
+    if (memcmp(rb_model_names[model], header + 4, 4)!=0)
+        ERROR("[ERR]  Model name \"%s\" not found in %s\n",
+                       rb_model_names[model], filename);
 
     *bufsize = filesize(fd) - sizeof(header);
 
     buf = malloc(*bufsize);
-    if (buf == NULL) {
-        fprintf(stderr,"[ERR]  Could not allocate memory for %s\n",filename);
-        return NULL;
-    }
+    if (buf == NULL)
+        ERROR("[ERR]  Could not allocate memory for %s\n", filename);
 
     n = read(fd, buf, *bufsize);
 
-    if (n != *bufsize) {
-        fprintf(stderr,"[ERR]  Could not read file %s\n",filename);
-        return NULL;
-    }
+    if (n != *bufsize)
+        ERROR("[ERR]  Could not read file %s\n", filename);
 
     /* Check checksum */
     sum = rb_model_num[model];
@@ -401,176 +453,36 @@
          sum += buf[i];
     }
 
-    if (sum != get_uint32be(header)) {
-        fprintf(stderr,"[ERR]  Checksum mismatch in %s\n",filename);
-        return NULL;
-    }
-    return buf;
+    if (sum != get_uint32be(header))
+        ERROR("[ERR]  Checksum mismatch in %s\n", filename);
+
+    packed = uclpack(buf, *bufsize, rb_packedsize);
+    if(packed == NULL)
+        ERROR("[ERR]  Could not compress %s\n", filename);
+
+    free(buf);
+    return packed;
+
+error:
+    free(buf);
+    return NULL;
 }
 
+#undef ERROR
 
-int main(int argc, char* argv[])
-{
-    char *infile, *bootfile, *outfile;
-    int fdout;
-    off_t len;
-    uint32_t n;
-    unsigned char* buf;
-    int firmware_size;
-    off_t bootloader_size;
-    uint32_t sum,filesum;
-    uint8_t  model_id;
-    int model;
-    uint32_t i;
-    unsigned char* of_packed;
-    int of_packedsize;
-    unsigned char* rb_unpacked;
-    unsigned char* rb_packed;
-    int rb_packedsize;
-    int fw_version;
-    int totalsize;
-    unsigned char* p;
-    uint32_t checksum;
-    char md5sum[33]; /* 32 hex digits, plus terminating zero */
-
-    fprintf(stderr,"mkamsboot v" VERSION " - (C) Dave Chapman and Rafaël Carré 2008\n");
-    fprintf(stderr,"This is free software; see the source for copying conditions.  There is NO\n");
-    fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
-
-    if(argc != 4) {
-        printf("Usage: mkamsboot <firmware file> <boot file> <output file>\n\n");
-        return 1;
-    }
-
-    infile = argv[1];
-    bootfile = argv[2];
-    outfile = argv[3];
-
-    /* Load original firmware file */
-    buf = load_file(infile, &len);
-
-    if (buf == NULL) {
-        fprintf(stderr,"[ERR]  Could not load %s\n",infile);
-        return 1;
-    }
-
-    /* Calculate MD5 checksum of OF */
-    calc_MD5(buf, len, md5sum);
-
-    fprintf(stderr,"[INFO] MD5 sum - %s\n",md5sum);
-
-    i = 0;
-    while ((i < NUM_MD5S) && (strcmp(sansasums[i].md5, md5sum) != 0))
-        i++;
-
-    if (i < NUM_MD5S) {
-        model = sansasums[i].model;
-        fw_version = sansasums[i].fw_version;
-        fprintf(stderr,"[INFO] Original firmware MD5 checksum match - %s %s\n",
-                       model_names[model], sansasums[i].version);
-    } else {
-        if (get_uint32le(&buf[0x204])==0x0000f000) {
-            fw_version = 2;
-            model_id = buf[0x219];
-        } else {
-            fw_version = 1;
-            model_id = buf[0x215];
-        }
-
-        model = get_model(model_id);
-
-#if 0
-        /* if you are a tester this info might help */
-        fprintf(stderr,"[WARN] ****** Original firmware unknown ******\n");
-        if (model == MODEL_UNKNOWN) {
-            fprintf(stderr,"[ERR]  Unknown firmware - model id 0x%02x\n",
-                           model_id);
-            free(buf);
-            return 1;
-        }
-#else
-        /* else you don't want to brick your player */
-        fprintf(stderr, "[ERR]  Original firmware untested - aborting\n");
-        free(buf);
-        return 1;
-#endif
-    }
-    
-
-    /* TODO: Do some more sanity checks on the OF image. Some images (like
-       m200v4) dont have a checksum at the end, only padding (0xdeadbeef). */
-    checksum = get_uint32le(buf + len - 4); 
-    if (checksum != 0xefbeadde && checksum != calc_checksum(buf, len - 4)) {
-
-        fprintf(stderr,"[ERR]  Whole file checksum failed - %s\n",infile);
-        free(buf);
-        return 1;
-    }
-
-    if (bootloaders[model] == NULL) {
-        fprintf(stderr,"[ERR]  Unsupported model - \"%s\"\n",model_names[model]);
-        free(buf);
-        return 1;
-    }
-
-    /* Load bootloader file */
-    rb_unpacked = load_rockbox_file(bootfile, model, &bootloader_size);
-    if (rb_unpacked == NULL) {
-        fprintf(stderr,"[ERR]  Could not load %s\n",bootfile);
-        free(buf);
-        return 1;
-    }
-
-    printf("[INFO] Patching %s firmware\n",model_names[model]);
-
-    /* Get the firmware size */
-    firmware_size = get_uint32le(&buf[0x0c]);
-
-    /* Compress the original firmware image */
-    of_packed = uclpack(buf + 0x400, firmware_size, &of_packedsize);
-    if (of_packed == NULL) {
-        fprintf(stderr,"[ERR]  Could not compress original firmware\n");
-        free(buf);
-        free(rb_unpacked);
-        return 1;
-    }
-
-    rb_packed = uclpack(rb_unpacked, bootloader_size, &rb_packedsize);
-    if (rb_packed == NULL) {
-        fprintf(stderr,"[ERR]  Could not compress %s\n",bootfile);
-        free(buf);
-        free(rb_unpacked);
-        free(of_packed);
-        return 1;
-    }
-
-    /* We are finished with the unpacked version of the bootloader */
-    free(rb_unpacked);
-
-    fprintf(stderr,"[INFO] Original firmware size:   %d bytes\n",firmware_size);
-    fprintf(stderr,"[INFO] Packed OF size:           %d bytes\n",of_packedsize);
-    fprintf(stderr,"[INFO] Bootloader size:          %d bytes\n",(int)bootloader_size);
-    fprintf(stderr,"[INFO] Packed bootloader size:   %d bytes\n",rb_packedsize);
-    fprintf(stderr,"[INFO] Dual-boot function size:  %d bytes\n",bootloader_sizes[model]);
-    fprintf(stderr,"[INFO] UCL unpack function size: %d bytes\n",sizeof(nrv2e_d8));
-
-    totalsize = bootloader_sizes[model] + sizeof(nrv2e_d8) + of_packedsize + 
-                rb_packedsize;
-
-    fprintf(stderr,"[INFO] Total size of new image:  %d bytes\n",totalsize);
-
-    if (totalsize > firmware_size) {
-        fprintf(stderr,"[ERR]  No room to insert bootloader, aborting\n");
-        free(buf);
-        free(rb_unpacked);
-        free(of_packed);
-        return 1;
-    }
+/* Patches a Sansa AMS Original Firmware file */
+void patch_firmware(
+        int model, int fw_version, int firmware_size, unsigned char* buf,
+        int len, unsigned char* of_packed, int of_packedsize,
+        unsigned char* rb_packed, int rb_packedsize
+) {
+    unsigned char *p;
+    uint32_t sum, filesum;
+    unsigned int i;
 
     /* Zero the original firmware area - not needed, but helps debugging */
     memset(buf + 0x400, 0, firmware_size);
 
-
     /* Insert dual-boot bootloader at offset 0 */
     memcpy(buf + 0x400, bootloaders[model], bootloader_sizes[model]);
 
@@ -589,7 +501,7 @@
     p -= rb_packedsize;
     memcpy(p, rb_packed, rb_packedsize);
 
-    /* Write the locations of the various images to the variables at the 
+    /* Write the locations of the various images to the variables at the
        start of the dualboot image - we save the location of the last byte
        in each image, along with the size in bytes */
 
@@ -602,19 +514,18 @@
     put_uint32le(&buf[0x42c], of_packedsize);
 
     /* Compressed Rockbox image */
-    put_uint32le(&buf[0x430], firmware_size - sizeof(nrv2e_d8) - of_packedsize - 1);
+    put_uint32le(&buf[0x430], firmware_size - sizeof(nrv2e_d8) - of_packedsize
+            - 1);
     put_uint32le(&buf[0x434], rb_packedsize);
 
 
     /* Update the firmware block checksum */
-    sum = calc_checksum(buf + 0x400,firmware_size);
+    sum = calc_checksum(buf + 0x400, firmware_size);
 
     if (fw_version == 1) {
         put_uint32le(&buf[0x04], sum);
         put_uint32le(&buf[0x204], sum);
     } else {
-        /* TODO: Verify that this is correct for the v2 firmware */
-
         put_uint32le(&buf[0x08], sum);
         put_uint32le(&buf[0x208], sum);
 
@@ -629,29 +540,130 @@
         filesum += get_uint32le(&buf[i]);
 
     put_uint32le(buf + len - 4, filesum);
+}
 
+/* returns size of new firmware block */
+int total_size(int model, int rb_packedsize, int of_packedsize) {
+    return bootloader_sizes[model] + sizeof(nrv2e_d8) + of_packedsize +
+                rb_packedsize;
+}
+
+#ifndef LIB
+/* standalone executable */
+int main(int argc, char* argv[]) {
+    char *infile, *bootfile, *outfile;
+    int fdout;
+    off_t len;
+    uint32_t n;
+    unsigned char* buf;
+    int firmware_size;
+    int bootloader_size;
+    int model;
+    unsigned char* of_packed;
+    int of_packedsize;
+    unsigned char* rb_packed;
+    int rb_packedsize;
+    int fw_version;
+    int totalsize;
+    char md5sum[33]; /* 32 hex digits, plus terminating zero */
+    char errstr[200];
+
+    fprintf(stderr,
+"mkamsboot v" VERSION " - (C) Dave Chapman and Rafaël Carré 2008\n"
+"This is free software; see the source for copying conditions.  There is NO\n"
+"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
+"\n");
+
+    if(argc != 4) {
+        printf("Usage: mkamsboot <firmware file> <boot file> <output file>\n");
+        return 1;
+    }
+
+    infile = argv[1];
+    bootfile = argv[2];
+    outfile = argv[3];
+
+    /* Load original firmware file */
+    buf = load_of_file(infile, &len, md5sum, &model, &fw_version,
+            &firmware_size, &of_packed, &of_packedsize, errstr, sizeof(errstr));
+
+    if (buf == NULL) {
+        fprintf(stderr, "%s", errstr);
+        fprintf(stderr, "[ERR]  Could not load %s\n", infile);
+        return 1;
+    }
+
+    fprintf(stderr, "[INFO] Original firmware MD5 checksum match - %s\n",
+            model_names[model]);
+
+
+    /* Load bootloader file */
+    rb_packed = load_rockbox_file(bootfile, model, &bootloader_size,
+            &rb_packedsize, errstr, sizeof(errstr));
+    if (rb_packed == NULL) {
+        fprintf(stderr, "%s", errstr);
+        fprintf(stderr, "[ERR]  Could not load %s\n", bootfile);
+        free(buf);
+        free(of_packed);
+        return 1;
+    }
+
+    printf("[INFO] Patching %s firmware\n", model_names[model]);
+
+    fprintf(stderr, "[INFO] Original firmware size:   %d bytes\n",
+            firmware_size);
+    fprintf(stderr, "[INFO] Packed OF size:           %d bytes\n",
+            of_packedsize);
+    fprintf(stderr, "[INFO] Bootloader size:          %d bytes\n",
+            (int)bootloader_size);
+    fprintf(stderr, "[INFO] Packed bootloader size:   %d bytes\n",
+            rb_packedsize);
+    fprintf(stderr, "[INFO] Dual-boot function size:  %d bytes\n",
+            bootloader_sizes[model]);
+    fprintf(stderr, "[INFO] UCL unpack function size: %d bytes\n",
+            sizeof(nrv2e_d8));
+
+    totalsize = total_size(model, of_packedsize, rb_packedsize);
+
+    fprintf(stderr, "[INFO] Total size of new image:  %d bytes\n", totalsize);
+
+    if (totalsize > firmware_size) {
+        fprintf(stderr, "[ERR]  No room to insert bootloader, aborting\n");
+        free(buf);
+        free(of_packed);
+        free(rb_packed);
+        return 1;
+    }
+
+    patch_firmware(model, fw_version, firmware_size, buf, len, of_packed,
+            of_packedsize, rb_packed, rb_packedsize);
 
     /* Write the new firmware */
-    fdout = open(outfile, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666);
+    fdout = open(outfile, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, 0666);
 
     if (fdout < 0) {
-        fprintf(stderr,"[ERR]  Could not open %s for writing\n",outfile);
+        fprintf(stderr, "[ERR]  Could not open %s for writing\n", outfile);
+        free(buf);
+        free(of_packed);
+        free(rb_packed);
         return 1;
     }
 
     n = write(fdout, buf, len);
 
     if (n != (unsigned)len) {
-        fprintf(stderr,"[ERR]  Could not write firmware file\n");
+        fprintf(stderr, "[ERR]  Could not write firmware file\n");
+        free(buf);
+        free(of_packed);
+        free(rb_packed);
         return 1;
     }
 
     close(fdout);
-
-    fprintf(stderr," *****************************************************************************\n");
-    fprintf(stderr," *** THIS CODE IS UNTESTED - DO NOT USE IF YOU CAN NOT RECOVER YOUR DEVICE ***\n");
-    fprintf(stderr," *****************************************************************************\n");
+    free(buf);
+    free(of_packed);
+    free(rb_packed);
 
     return 0;
-
 }
+#endif
diff --git a/rbutil/mkamsboot/mkamsboot.h b/rbutil/mkamsboot/mkamsboot.h
new file mode 100644
index 0000000..e2de46c
--- /dev/null
+++ b/rbutil/mkamsboot/mkamsboot.h
@@ -0,0 +1,124 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * mkamsboot.h - a tool for merging bootloader code into an Sansa V2
+ *               (AMS) firmware file
+ *
+ * Copyright (C) 2008 Dave Chapman
+ *
+ * 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.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef MKAMSBOOT_H
+#define MKAMSBOOT_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+
+/* load_rockbox_file()
+ *
+ * Loads a rockbox bootloader file into memory
+ *
+ * ARGUMENTS
+ *
+ * filename         :   bootloader file to load
+ * model            :   a 4 characters string representing the Sansa model
+ *                      ("fuze", "clip", "e2v2", "m2v4", or "c2v2")
+ * bootloader_size  :   set to the uncompressed bootloader size
+ * rb_packed_size   :   set to the size of compressed bootloader
+ * errstr           :   provided buffer to store an eventual error
+ * errstrsize       :   size of provided error buffer
+ *
+ * RETURN VALUE
+ *  pointer to allocated memory containing the content of compressed bootloader
+ *  or NULL in case of error (errstr will hold a description of the error)
+ */
+
+unsigned char* load_rockbox_file(
+        char* filename, int model, int* bootloader_size, int* rb_packedsize,
+        char* errstr, int errstrsize);
+
+
+/* load_of_file()
+ *
+ * Loads a Sansa AMS Original Firmware file into memory
+ *
+ * ARGUMENTS
+ *
+ * filename         :   firmware file to load
+ * bufsize          :   set to firmware file size
+ * md5sum           :   set to file md5sum, must be at least 33 bytes long
+ * model            :   set to firmware model (MODEL_XXX)
+ * fw_version       :   set to firmware format version (1 or 2)
+ * firmware_size    :   set to firmware block's size
+ * of_packed        :   pointer to allocated memory containing the compressed
+ *                      original firmware block
+ * of_packedsize    :   size of compressed original firmware block
+ * errstr           :   provided buffer to store an eventual error
+ * errstrsize       :   size of provided error buffer
+ *
+ * RETURN VALUE
+ *  pointer to allocated memory containing the content of Original Firmware
+ *  or NULL in case of error (errstr will hold a description of the error)
+ */
+
+unsigned char* load_of_file(
+        char* filename, off_t* bufsize, char* md5sum, int* model,
+        int* fw_version, int* firmware_size, unsigned char** of_packed,
+        int* of_packedsize, char* errstr, int errstrsize);
+
+
+/* patch_firmware()
+ *
+ * Patches a Sansa AMS Original Firmware file
+ *
+ * ARGUMENTS
+ *
+ * model            :   firmware model (MODEL_XXX)
+ * fw_version       :   firmware format version (1 or 2)
+ * firmware_size    :   size of uncompressed original firmware block
+ * buf              :   pointer to original firmware file
+ * len              :   size of original firmware file
+ * of_packed        :   pointer to compressed original firmware block
+ * of_packedsize    :   size of compressed original firmware block
+ * rb_packed        :   pointer to compressed rockbox bootloader
+ * rb_packed_size   :   size of compressed rockbox bootloader
+ */
+
+void patch_firmware(
+        int model, int fw_version, int firmware_size, unsigned char* buf,
+        int len, unsigned char* of_packed, int of_packedsize,
+        unsigned char* rb_packed, int rb_packedsize);
+
+
+/* total_size()
+ *
+ * Calculates the size of the new firmware block
+ *
+ * ARGUMENTS
+ *
+ * model            :   firmware model (MODEL_XXX)
+ * rb_packed_size   :   size of compressed rockbox bootloader
+ * of_packedsize    :   size of compressed original firmware block
+ *
+ * RETURN VALUE
+ *  Size of new firmware block
+*/
+
+int total_size(int model, int rb_packedsize, int of_packedsize);
+
+#endif
diff --git a/rbutil/mkamsboot/extract_fw.c b/utils/AMS/hacking/extract_fw.c
similarity index 93%
rename from rbutil/mkamsboot/extract_fw.c
rename to utils/AMS/hacking/extract_fw.c
index e91d1f8..bee6a23 100644
--- a/rbutil/mkamsboot/extract_fw.c
+++ b/utils/AMS/hacking/extract_fw.c
@@ -105,7 +105,10 @@
     close(fdin);
 
     /* Get the firmware size */
-    firmware_size = get_uint32le(&buf[0x0c]);
+    if (get_uint32le(&buf[0x04])==0x0000f000)
+        firmware_size = get_uint32le(&buf[0x10]);   /* v2 format */
+    else
+        firmware_size = get_uint32le(&buf[0x0c]);   /* v1 format */
 
     fdout = open(outfile, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666);