summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Chapman <dave@dchapman.com>2007-03-15 22:55:36 +0000
committerDave Chapman <dave@dchapman.com>2007-03-15 22:55:36 +0000
commite17043ead72180ffa18a3b926cbabe09b5e3f903 (patch)
tree02f708ccb7a417c3f6d1a99bd9fdd3ea2941d6c5
parenta42070df8566baa4a95fc04f2205a1e0aa240e00 (diff)
downloadrockbox-e17043ead72180ffa18a3b926cbabe09b5e3f903.tar.gz
rockbox-e17043ead72180ffa18a3b926cbabe09b5e3f903.zip
Initial commit of sansapatcher - an installation tool for the Sansa E200.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12792 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--rbutil/sansapatcher/Makefile44
-rw-r--r--rbutil/sansapatcher/main.c293
-rw-r--r--rbutil/sansapatcher/sansaio-posix.c115
-rw-r--r--rbutil/sansapatcher/sansaio-win32.c196
-rw-r--r--rbutil/sansapatcher/sansaio.h74
-rw-r--r--rbutil/sansapatcher/sansapatcher.c702
-rw-r--r--rbutil/sansapatcher/sansapatcher.h45
7 files changed, 1469 insertions, 0 deletions
diff --git a/rbutil/sansapatcher/Makefile b/rbutil/sansapatcher/Makefile
new file mode 100644
index 0000000000..17b3fb0047
--- /dev/null
+++ b/rbutil/sansapatcher/Makefile
@@ -0,0 +1,44 @@
+CFLAGS=-Wall -D_LARGEFILE64_SOURCE
+
+ifeq ($(findstring CYGWIN,$(shell uname)),CYGWIN)
+OUTPUT=sansapatcher.exe
+CROSS=
+CFLAGS+=-mno-cygwin
+else
+OUTPUT=sansapatcher
+CROSS=i586-mingw32msvc-
+endif
+
+NATIVECC = gcc
+CC = $(CROSS)gcc
+
+all: $(OUTPUT)
+
+sansapatcher: main.c sansapatcher.c sansaio-posix.c parttypes.h bootimg.c
+ gcc $(CFLAGS) -o sansapatcher main.c sansapatcher.c sansaio-posix.c bootimg.c
+ strip sansapatcher
+
+sansapatcher.exe: main.c sansapatcher.c sansaio-win32.c parttypes.h bootimg.c
+ $(CC) $(CFLAGS) -o sansapatcher.exe main.c sansapatcher.c sansaio-win32.c bootimg.c
+ $(CROSS)strip sansapatcher.exe
+
+sansapatcher-mac: sansapatcher-i386 sansapatcher-ppc
+ lipo -create sansapatcher-ppc sansapatcher-i386 -output sansapatcher-mac
+
+sansapatcher-i386: main.c sansapatcher.c sansaio-posix.c parttypes.h bootimg.c
+ gcc -isysroot /Developer/SDKs/MacOSX10.4u.sdk -o bin/i386/program -arch i386 $(CFLAGS) -o sansapatcher-i386 main.c sansapatcher.c sansaio-posix.c bootimg.c
+ strip sansapatcher-i386
+
+sansapatcher-ppc: main.c sansapatcher.c sansaio-posix.c parttypes.h bootimg.c
+ gcc -arch ppc $(CFLAGS) -o sansapatcher-ppc main.c sansapatcher.c sansaio-posix.c bootimg.c
+ strip sansapatcher-ppc
+
+#mi42c: mi42c.c
+# $(NATIVECC) $(CFLAGS) -o mi42c mi42c.c
+
+#bootimg.c: PP5022.mi4 mi42c
+# ./mi42c PP5022.mi4 bootimg
+
+clean:
+ rm -f sansapatcher.exe sansapatcher-mac sansapatcher-i386 sansapatcher-ppc sansapatcher mi42c *~
+#bootimg.c bootimg.h
diff --git a/rbutil/sansapatcher/main.c b/rbutil/sansapatcher/main.c
new file mode 100644
index 0000000000..4e1e2dc120
--- /dev/null
+++ b/rbutil/sansapatcher/main.c
@@ -0,0 +1,293 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id: ipodpatcher.c 12237 2007-02-08 21:31:38Z dave $
+ *
+ * Copyright (C) 2006-2007 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 <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "sansapatcher.h"
+#include "sansaio.h"
+
+#define VERSION "0.1"
+
+int verbose = 0;
+
+enum {
+ NONE,
+#ifdef WITH_BOOTOBJS
+ INSTALL,
+#endif
+ INTERACTIVE,
+ SHOW_INFO,
+ LIST_IMAGES,
+ DELETE_BOOTLOADER,
+ ADD_BOOTLOADER,
+ READ_FIRMWARE,
+ WRITE_FIRMWARE,
+ READ_PARTITION,
+ WRITE_PARTITION
+};
+
+void print_usage(void)
+{
+ fprintf(stderr,"Usage: sansapatcher --scan\n");
+#ifdef __WIN32__
+ fprintf(stderr," or sansapatcher [DISKNO] [action]\n");
+#else
+ fprintf(stderr," or sansapatcher [device] [action]\n");
+#endif
+ fprintf(stderr,"\n");
+ fprintf(stderr,"Where [action] is one of the following options:\n");
+#ifdef WITH_BOOTOBJS
+ fprintf(stderr," --install\n");
+#endif
+ fprintf(stderr," -l, --list\n");
+ fprintf(stderr," -rf, --read-firmware filename.mi4\n");
+ fprintf(stderr," -a, --add-bootloader filename.mi4\n");
+ fprintf(stderr," -d, --delete-bootloader\n");
+ fprintf(stderr,"\n");
+
+#ifdef __WIN32__
+ fprintf(stderr,"DISKNO is the number (e.g. 2) Windows has assigned to your sansa's hard disk.\n");
+ fprintf(stderr,"The first hard disk in your computer (i.e. C:\\) will be disk 0, the next disk\n");
+ fprintf(stderr,"will be disk 1 etc. sansapatcher will refuse to access a disk unless it\n");
+ fprintf(stderr,"can identify it as being an E200.\n");
+ fprintf(stderr,"\n");
+#else
+#if defined(linux) || defined (__linux)
+ fprintf(stderr,"\"device\" is the device node (e.g. /dev/sda) assigned to your sansa.\n");
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+ fprintf(stderr,"\"device\" is the device node (e.g. /dev/da1) assigned to your sansa.\n");
+#elif defined(__APPLE__) && defined(__MACH__)
+ fprintf(stderr,"\"device\" is the device node (e.g. /dev/disk1) assigned to your sansa.\n");
+#endif
+ fprintf(stderr,"sansapatcher will refuse to access a disk unless it can identify it as being\n");
+ fprintf(stderr,"an E200.\n");
+#endif
+}
+
+void display_partinfo(struct sansa_t* sansa)
+{
+ int i;
+ double sectors_per_MB = (1024.0*1024.0)/sansa->sector_size;
+
+ printf("[INFO] Part Start Sector End Sector Size (MB) Type\n");
+ for ( i = 0; i < 4; i++ ) {
+ if (sansa->pinfo[i].start != 0) {
+ printf("[INFO] %d %10ld %10ld %10.1f %s (0x%02x)\n",
+ i,
+ sansa->pinfo[i].start,
+ sansa->pinfo[i].start+sansa->pinfo[i].size-1,
+ sansa->pinfo[i].size/sectors_per_MB,
+ get_parttype(sansa->pinfo[i].type),
+ sansa->pinfo[i].type);
+ }
+ }
+}
+
+
+int main(int argc, char* argv[])
+{
+#ifdef WITH_BOOTOBJS
+ char yesno[4];
+#endif
+ int i;
+ int n;
+ char* filename;
+ int action = SHOW_INFO;
+ int type;
+ struct sansa_t sansa;
+
+ fprintf(stderr,"sansapatcher v" VERSION " - (C) Dave Chapman 2006-2007\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 > 1) && ((strcmp(argv[1],"-h")==0) || (strcmp(argv[1],"--help")==0))) {
+ print_usage();
+ return 1;
+ }
+
+ if (sansa_alloc_buffer(&sectorbuf,BUFFER_SIZE) < 0) {
+ fprintf(stderr,"Failed to allocate memory buffer\n");
+ }
+
+ if ((argc > 1) && (strcmp(argv[1],"--scan")==0)) {
+ if (sansa_scan(&sansa) == 0)
+ fprintf(stderr,"[ERR] No E200s found.\n");
+ return 0;
+ }
+
+ /* If the first parameter doesn't start with -, then we interpret it as a device */
+ if ((argc > 1) && (argv[1][0] != '-')) {
+ sansa.diskname[0]=0;
+#ifdef __WIN32__
+ snprintf(sansa.diskname,sizeof(sansa.diskname),"\\\\.\\PhysicalDrive%s",argv[1]);
+#else
+ strncpy(sansa.diskname,argv[1],sizeof(sansa.diskname));
+#endif
+ i = 2;
+ } else {
+ /* Autoscan for E200s */
+ n = sansa_scan(&sansa);
+ if (n==0) {
+ fprintf(stderr,"[ERR] No E200s found, aborting\n");
+ fprintf(stderr,"[ERR] Please connect your sansa and ensure it is in UMS mode\n");
+#if defined(__APPLE__) && defined(__MACH__)
+ fprintf(stderr,"[ERR] Also ensure that your E200's main partition is not mounted.\n");
+#elif !defined(__WIN32__)
+ if (geteuid()!=0) {
+ fprintf(stderr,"[ERR] You may also need to run sansapatcher as root.\n");
+ }
+#endif
+ fprintf(stderr,"[ERR] Please refer to the Rockbox manual if you continue to have problems.\n");
+ } else if (n > 1) {
+ fprintf(stderr,"[ERR] %d E200s found, aborting\n",n);
+ fprintf(stderr,"[ERR] Please connect only one E200 and re-run sansapatcher.\n");
+ }
+
+ if (n != 1) {
+#ifdef WITH_BOOTOBJS
+ if (argc==1) {
+ printf("\nPress ENTER to exit sansapatcher :");
+ fgets(yesno,4,stdin);
+ }
+#endif
+ return 0;
+ }
+
+ i = 1;
+ }
+
+#ifdef WITH_BOOTOBJS
+ action = INTERACTIVE;
+#else
+ action = NONE;
+#endif
+
+ while (i < argc) {
+ if ((strcmp(argv[i],"-l")==0) || (strcmp(argv[i],"--list")==0)) {
+ action = LIST_IMAGES;
+ i++;
+#ifdef WITH_BOOTOBJS
+ } else if (strcmp(argv[i],"--install")==0) {
+ action = INSTALL;
+ i++;
+#endif
+ } else if ((strcmp(argv[i],"-d")==0) ||
+ (strcmp(argv[i],"--delete-bootloader")==0)) {
+ action = DELETE_BOOTLOADER;
+ i++;
+ } else if ((strcmp(argv[i],"-a")==0) ||
+ (strcmp(argv[i],"--add-bootloader")==0)) {
+ action = ADD_BOOTLOADER;
+ type = FILETYPE_MI4;
+ i++;
+ if (i == argc) { print_usage(); return 1; }
+ filename=argv[i];
+ i++;
+ } else if ((strcmp(argv[i],"-rf")==0) ||
+ (strcmp(argv[i],"--read-firmware")==0)) {
+ action = READ_FIRMWARE;
+ i++;
+ if (i == argc) { print_usage(); return 1; }
+ filename=argv[i];
+ i++;
+ }
+ }
+
+ if (sansa.diskname[0]==0) {
+ print_usage();
+ return 1;
+ }
+
+ if (sansa_open(&sansa, 0) < 0) {
+ return 1;
+ }
+
+ fprintf(stderr,"[INFO] Reading partition table from %s\n",sansa.diskname);
+ fprintf(stderr,"[INFO] Sector size is %d bytes\n",sansa.sector_size);
+
+ if (read_partinfo(&sansa,0) < 0) {
+ return 2;
+ }
+
+ display_partinfo(&sansa);
+
+ i = is_e200(&sansa);
+ if (i < 0) {
+ fprintf(stderr,"[ERR] Disk is not an E200 (%d), aborting.\n",i);
+ return 3;
+ }
+
+ if (sansa.hasoldbootloader) {
+ printf("[ERR] ************************************************************************\n");
+ printf("[ERR] *** OLD ROCKBOX INSTALLATION DETECTED, ABORTING.\n");
+ printf("[ERR] *** You must reinstall the original Sansa firmware before running\n");
+ printf("[ERR] *** sansapatcher for the first time.\n");
+ printf("[ERR] *** See http://www.rockbox.org/twiki/bin/view/Main/SansaE200Install\n");
+ printf("[ERR] ************************************************************************\n");
+ return 4;
+ }
+
+ if (action==LIST_IMAGES) {
+ list_images(&sansa);
+ } else if (action==READ_FIRMWARE) {
+ if (read_firmware(&sansa, filename)==0) {
+ fprintf(stderr,"[INFO] Firmware read to file %s.\n",filename);
+ } else {
+ fprintf(stderr,"[ERR] --read-firmware failed.\n");
+ }
+ } else if (action==ADD_BOOTLOADER) {
+ if (sansa_reopen_rw(&sansa) < 0) {
+ return 5;
+ }
+
+ if (add_bootloader(&sansa, filename, type)==0) {
+ fprintf(stderr,"[INFO] Bootloader %s written to device.\n",filename);
+ } else {
+ fprintf(stderr,"[ERR] --add-bootloader failed.\n");
+ }
+ } else if (action==DELETE_BOOTLOADER) {
+ if (sansa_reopen_rw(&sansa) < 0) {
+ return 5;
+ }
+
+ if (delete_bootloader(&sansa)==0) {
+ fprintf(stderr,"[INFO] Bootloader removed successfully.\n");
+ } else {
+ fprintf(stderr,"[ERR] --delete-bootloader failed.\n");
+ }
+ }
+
+ sansa_close(&sansa);
+
+#ifdef WITH_BOOTOBJS
+ if (action==INTERACTIVE) {
+ printf("Press ENTER to exit sansapatcher :");
+ fgets(yesno,4,stdin);
+ }
+#endif
+
+ return 0;
+}
diff --git a/rbutil/sansapatcher/sansaio-posix.c b/rbutil/sansapatcher/sansaio-posix.c
new file mode 100644
index 0000000000..a8d529c20b
--- /dev/null
+++ b/rbutil/sansapatcher/sansaio-posix.c
@@ -0,0 +1,115 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id: ipodio-posix.c 12263 2007-02-10 19:49:43Z dave $
+ *
+ * Copyright (C) 2006-2007 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 <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#if defined(linux) || defined (__linux)
+#include <sys/mount.h>
+#define SANSA_SECTORSIZE_IOCTL BLKSSZGET
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \
+ || defined(__bsdi__) || defined(__DragonFly__)
+#include <sys/disk.h>
+#define SANSA_SECTORSIZE_IOCTL DIOCGSECTORSIZE
+#elif defined(__APPLE__) && defined(__MACH__)
+#include <sys/disk.h>
+#define SANSA_SECTORSIZE_IOCTL DKIOCGETBLOCKSIZE
+#else
+ #error No sector-size detection implemented for this platform
+#endif
+
+#include "sansaio.h"
+
+void print_error(char* msg)
+{
+ perror(msg);
+}
+
+int sansa_open(struct sansa_t* sansa, int silent)
+{
+ sansa->dh=open(sansa->diskname,O_RDONLY);
+ if (sansa->dh < 0) {
+ if (!silent) perror(sansa->diskname);
+ return -1;
+ }
+
+ if(ioctl(sansa->dh,SANSA_SECTORSIZE_IOCTL,&sansa->sector_size) < 0) {
+ sansa->sector_size=512;
+ if (!silent) {
+ fprintf(stderr,"[ERR] ioctl() call to get sector size failed, defaulting to %d\n"
+ ,sansa->sector_size);
+ }
+ }
+ return 0;
+}
+
+
+int sansa_reopen_rw(struct sansa_t* sansa)
+{
+ close(sansa->dh);
+ sansa->dh=open(sansa->diskname,O_RDWR);
+ if (sansa->dh < 0) {
+ perror(sansa->diskname);
+ return -1;
+ }
+ return 0;
+}
+
+int sansa_close(struct sansa_t* sansa)
+{
+ close(sansa->dh);
+ return 0;
+}
+
+int sansa_alloc_buffer(unsigned char** sectorbuf, int bufsize)
+{
+ *sectorbuf=malloc(bufsize);
+ if (*sectorbuf == NULL) {
+ return -1;
+ }
+ return 0;
+}
+
+int sansa_seek(struct sansa_t* sansa, loff_t pos)
+{
+ off_t res;
+
+ res = lseek64(sansa->dh, pos, SEEK_SET);
+
+ if (res == -1) {
+ return -1;
+ }
+ return 0;
+}
+
+int sansa_read(struct sansa_t* sansa, unsigned char* buf, int nbytes)
+{
+ return read(sansa->dh, buf, nbytes);
+}
+
+int sansa_write(struct sansa_t* sansa, unsigned char* buf, int nbytes)
+{
+ return write(sansa->dh, buf, nbytes);
+}
diff --git a/rbutil/sansapatcher/sansaio-win32.c b/rbutil/sansapatcher/sansaio-win32.c
new file mode 100644
index 0000000000..315b379a28
--- /dev/null
+++ b/rbutil/sansapatcher/sansaio-win32.c
@@ -0,0 +1,196 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id: ipodio-win32.c 12263 2007-02-10 19:49:43Z dave $
+ *
+ * Copyright (C) 2006-2007 Dave Chapman
+ *
+ * error(), lock_volume() and unlock_volume() functions and inspiration taken
+ * from:
+ * RawDisk - Direct Disk Read/Write Access for NT/2000/XP
+ * Copyright (c) 2003 Jan Kiszka
+ * http://www.stud.uni-hannover.de/user/73174/RawDisk/
+ *
+ * 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 <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef __WIN32__
+#include <windows.h>
+#include <winioctl.h>
+#endif
+
+#include "sansaio.h"
+
+static int lock_volume(HANDLE hDisk)
+{
+ DWORD dummy;
+
+ return DeviceIoControl(hDisk, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0,
+ &dummy, NULL);
+}
+
+static int unlock_volume(HANDLE hDisk)
+{
+ DWORD dummy;
+
+ return DeviceIoControl(hDisk, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0,
+ &dummy, NULL);
+}
+
+void print_error(char* msg)
+{
+ char* pMsgBuf;
+
+ printf(msg);
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&pMsgBuf,
+ 0, NULL);
+ printf(pMsgBuf);
+ LocalFree(pMsgBuf);
+}
+
+int sansa_open(struct sansa_t* sansa, int silent)
+{
+ DISK_GEOMETRY_EX diskgeometry_ex;
+ DISK_GEOMETRY diskgeometry;
+ unsigned long n;
+
+ sansa->dh = CreateFile(sansa->diskname, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+ FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL);
+
+ if (sansa->dh == INVALID_HANDLE_VALUE) {
+ if (!silent) print_error(" Error opening disk: ");
+ return -1;
+ }
+
+ if (!lock_volume(sansa->dh)) {
+ if (!silent) print_error(" Error locking disk: ");
+ return -1;
+ }
+
+ if (!DeviceIoControl(sansa->dh,
+ IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
+ NULL,
+ 0,
+ &diskgeometry_ex,
+ sizeof(diskgeometry_ex),
+ &n,
+ NULL)) {
+ if (!DeviceIoControl(sansa->dh,
+ IOCTL_DISK_GET_DRIVE_GEOMETRY,
+ NULL,
+ 0,
+ &diskgeometry,
+ sizeof(diskgeometry),
+ &n,
+ NULL)) {
+ if (!silent) print_error(" Error reading disk geometry: ");
+ return -1;
+ } else {
+ sansa->sector_size=diskgeometry.BytesPerSector;
+ }
+ } else {
+ sansa->sector_size=diskgeometry_ex.Geometry.BytesPerSector;
+ }
+
+ return 0;
+}
+
+int sansa_reopen_rw(struct sansa_t* sansa)
+{
+ /* Close existing file and re-open for writing */
+ unlock_volume(sansa->dh);
+ CloseHandle(sansa->dh);
+
+ sansa->dh = CreateFile(sansa->diskname, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+ FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL);
+
+ if (sansa->dh == INVALID_HANDLE_VALUE) {
+ print_error(" Error opening disk: ");
+ return -1;
+ }
+
+ if (!lock_volume(sansa->dh)) {
+ print_error(" Error locking disk: ");
+ return -1;
+ }
+
+ return 0;
+}
+
+int sansa_close(struct sansa_t* sansa)
+{
+ unlock_volume(sansa->dh);
+ CloseHandle(sansa->dh);
+ return 0;
+}
+
+int sansa_alloc_buffer(unsigned char** sectorbuf, int bufsize)
+{
+ /* The ReadFile function requires a memory buffer aligned to a multiple of
+ the disk sector size. */
+ *sectorbuf = (unsigned char*)VirtualAlloc(NULL, bufsize, MEM_COMMIT, PAGE_READWRITE);
+ if (*sectorbuf == NULL) {
+ print_error(" Error allocating a buffer: ");
+ return -1;
+ }
+ return 0;
+}
+
+int sansa_seek(struct sansa_t* sansa, loff_t pos)
+{
+ LARGE_INTEGER li;
+
+ li.QuadPart = pos;
+
+ li.LowPart = SetFilePointer (sansa->dh, li.LowPart, &li.HighPart, FILE_BEGIN);
+
+ if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
+ print_error(" Seek error ");
+ return -1;
+ }
+ return 0;
+}
+
+int sansa_read(struct sansa_t* sansa, unsigned char* buf, int nbytes)
+{
+ unsigned long count;
+
+ if (!ReadFile(sansa->dh, buf, nbytes, &count, NULL)) {
+ print_error(" Error reading from disk: ");
+ return -1;
+ }
+
+ return count;
+}
+
+int sansa_write(struct sansa_t* sansa, unsigned char* buf, int nbytes)
+{
+ unsigned long count;
+
+ if (!WriteFile(sansa->dh, buf, nbytes, &count, NULL)) {
+ print_error(" Error writing to disk: ");
+ return -1;
+ }
+
+ return count;
+}
diff --git a/rbutil/sansapatcher/sansaio.h b/rbutil/sansapatcher/sansaio.h
new file mode 100644
index 0000000000..246400c51c
--- /dev/null
+++ b/rbutil/sansapatcher/sansaio.h
@@ -0,0 +1,74 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id: ipodio.h 12339 2007-02-16 20:45:00Z dave $
+ *
+ * Copyright (C) 2006-2007 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 __SANSAIO_H
+#define __SANSAIO_H
+
+#include <stdint.h>
+#include <unistd.h>
+
+#ifdef __WIN32__
+#include <windows.h>
+#define loff_t int64_t
+#else
+#define HANDLE int
+#define O_BINARY 0
+
+/* Only Linux seems to need lseek64 and loff_t */
+#if !defined(linux) && defined (__linux)
+#define loff_t off_t
+#define lseek64 lseek
+#endif
+
+#endif
+
+struct partinfo_t {
+ unsigned long start; /* first sector (LBA) */
+ unsigned long size; /* number of sectors */
+ int type;
+};
+
+struct mi4header_t {
+ uint32_t version;
+ uint32_t length;
+ uint32_t crc32;
+ uint32_t enctype;
+ uint32_t mi4size;
+ uint32_t plaintext;
+};
+
+struct sansa_t {
+ HANDLE dh;
+ char diskname[4096];
+ int sector_size;
+ struct partinfo_t pinfo[4];
+ int hasoldbootloader;
+ loff_t start; /* Offset in bytes of firmware partition from start of disk */
+};
+
+void print_error(char* msg);
+int sansa_open(struct sansa_t* sansa, int silent);
+int sansa_reopen_rw(struct sansa_t* sansa);
+int sansa_close(struct sansa_t* sansa);
+int sansa_seek(struct sansa_t* sansa, loff_t pos);
+int sansa_read(struct sansa_t* sansa, unsigned char* buf, int nbytes);
+int sansa_write(struct sansa_t* sansa, unsigned char* buf, int nbytes);
+int sansa_alloc_buffer(unsigned char** sectorbuf, int bufsize);
+
+#endif
diff --git a/rbutil/sansapatcher/sansapatcher.c b/rbutil/sansapatcher/sansapatcher.c
new file mode 100644
index 0000000000..190cf09452
--- /dev/null
+++ b/rbutil/sansapatcher/sansapatcher.c
@@ -0,0 +1,702 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id: ipodpatcher.c 12264 2007-02-10 20:09:23Z dave $
+ *
+ * Copyright (C) 2006-2007 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 <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "parttypes.h"
+#include "sansaio.h"
+#include "sansapatcher.h"
+#include "bootimg.h"
+
+/* The offset of the MI4 image header in the firmware partition */
+#define PPMI_OFFSET 0x80000
+
+extern int verbose;
+
+/* Windows requires the buffer for disk I/O to be aligned in memory on a
+ multiple of the disk volume size - so we use a single global variable
+ and initialise it with sansa_alloc_buf() in main().
+*/
+
+unsigned char* sectorbuf;
+
+char* get_parttype(int pt)
+{
+ int i;
+ static char unknown[]="Unknown";
+
+ if (pt == -1) {
+ return "HFS/HFS+";
+ }
+
+ i=0;
+ while (parttypes[i].name != NULL) {
+ if (parttypes[i].type == pt) {
+ return (parttypes[i].name);
+ }
+ i++;
+ }
+
+ return unknown;
+}
+
+off_t filesize(int fd) {
+ struct stat buf;
+
+ if (fstat(fd,&buf) < 0) {
+ perror("[ERR] Checking filesize of input file");
+ return -1;
+ } else {
+ return(buf.st_size);
+ }
+}
+
+/* Partition table parsing code taken from Rockbox */
+
+#define MAX_SECTOR_SIZE 2048
+#define SECTOR_SIZE 512
+
+unsigned short static inline le2ushort(unsigned char* buf)
+{
+ unsigned short res = (buf[1] << 8) | buf[0];
+
+ return res;
+}
+
+int static inline le2int(unsigned char* buf)
+{
+ int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+
+ return res;
+}
+
+int static inline be2int(unsigned char* buf)
+{
+ int32_t res = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+
+ return res;
+}
+
+int static inline getint16le(char* buf)
+{
+ int16_t res = (buf[1] << 8) | buf[0];
+
+ return res;
+}
+
+void static inline short2le(unsigned short val, unsigned char* addr)
+{
+ addr[0] = val & 0xFF;
+ addr[1] = (val >> 8) & 0xff;
+}
+
+void static inline 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;
+}
+
+void int2be(unsigned int val, unsigned char* addr)
+{
+ addr[0] = (val >> 24) & 0xff;
+ addr[1] = (val >> 16) & 0xff;
+ addr[2] = (val >> 8) & 0xff;
+ addr[3] = val & 0xFF;
+}
+
+
+#define BYTES2INT32(array,pos)\
+ ((long)array[pos] | ((long)array[pos+1] << 8 ) |\
+ ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 ))
+
+int read_partinfo(struct sansa_t* sansa, int silent)
+{
+ int i;
+ unsigned long count;
+
+ count = sansa_read(sansa,sectorbuf, sansa->sector_size);
+
+ if (count <= 0) {
+ print_error(" Error reading from disk: ");
+ return -1;
+ }
+
+ if ((sectorbuf[510] == 0x55) && (sectorbuf[511] == 0xaa)) {
+ /* parse partitions */
+ for ( i = 0; i < 4; i++ ) {
+ unsigned char* ptr = sectorbuf + 0x1be + 16*i;
+ sansa->pinfo[i].type = ptr[4];
+ sansa->pinfo[i].start = BYTES2INT32(ptr, 8);
+ sansa->pinfo[i].size = BYTES2INT32(ptr, 12);
+
+ /* extended? */
+ if ( sansa->pinfo[i].type == 5 ) {
+ /* not handled yet */
+ }
+ }
+ } else if ((sectorbuf[0] == 'E') && (sectorbuf[1] == 'R')) {
+ if (!silent) fprintf(stderr,"[ERR] Bad boot sector signature\n");
+ return -1;
+ }
+
+ /* Calculate the starting position of the firmware partition */
+ sansa->start = (loff_t)sansa->pinfo[1].start*(loff_t)sansa->sector_size;
+ return 0;
+}
+
+
+/*
+ * 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.
+ *
+ */
+
+/* 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;
+ }
+}
+
+/* Known keys for Sansa E200 firmwares: */
+#define NUM_KEYS 2
+static uint32_t keys[][4] = {
+ { 0xe494e96e, 0x3ee32966, 0x6f48512b, 0xa93fbb42 }, /* "sansa" */
+ { 0xd7b10538, 0xc662945b, 0x1b3fce68, 0xf389c0e6 }, /* "sansa_gh" */
+};
+
+/*
+
+tea_decrypt() from http://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm
+
+"Following is an adaptation of the reference encryption and decryption
+routines in C, released into the public domain by David Wheeler and
+Roger Needham:"
+
+*/
+
+/* NOTE: The mi4 version of TEA uses a different initial value to sum compared
+ to the reference implementation and the main loop is 8 iterations, not
+ 32.
+*/
+
+void tea_decrypt(uint32_t* v0, uint32_t* v1, uint32_t* k) {
+ uint32_t sum=0xF1BBCDC8, i; /* set up */
+ uint32_t delta=0x9E3779B9; /* a key schedule constant */
+ uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
+ for(i=0; i<8; i++) { /* basic cycle start */
+ *v1 -= ((*v0<<4) + k2) ^ (*v0 + sum) ^ ((*v0>>5) + k3);
+ *v0 -= ((*v1<<4) + k0) ^ (*v1 + sum) ^ ((*v1>>5) + k1);
+ sum -= delta; /* end cycle */
+ }
+}
+
+/* mi4 files are encrypted in 64-bit blocks (two little-endian 32-bit
+ integers) and the key is incremented after each block
+ */
+
+void tea_decrypt_buf(unsigned char* src, unsigned char* dest, size_t n, uint32_t * key)
+{
+ uint32_t v0, v1;
+ int i;
+
+ for (i = 0; i < (n / 8); i++) {
+ v0 = le2int(src);
+ v1 = le2int(src+4);
+
+ tea_decrypt(&v0, &v1, key);
+
+ int2le(v0, dest);
+ int2le(v1, dest+4);
+
+ src += 8;
+ dest += 8;
+
+ /* Now increment the key */
+ key[0]++;
+ if (key[0]==0) {
+ key[1]++;
+ if (key[1]==0) {
+ key[2]++;
+ if (key[2]==0) {
+ key[3]++;
+ }
+ }
+ }
+ }
+}
+
+static int get_mi4header(unsigned char* buf,struct mi4header_t* mi4header)
+{
+ if (memcmp(buf,"PPOS",4)!=0)
+ return -1;
+
+ mi4header->version = le2int(buf+0x04);
+ mi4header->length = le2int(buf+0x08);
+ mi4header->crc32 = le2int(buf+0x0c);
+ mi4header->enctype = le2int(buf+0x10);
+ mi4header->mi4size = le2int(buf+0x14);
+ mi4header->plaintext = le2int(buf+0x18);
+
+ return 0;
+}
+
+static int set_mi4header(unsigned char* buf,struct mi4header_t* mi4header)
+{
+ if (memcmp(buf,"PPOS",4)!=0)
+ return -1;
+
+ int2le(mi4header->version ,buf+0x04);
+ int2le(mi4header->length ,buf+0x08);
+ int2le(mi4header->crc32 ,buf+0x0c);
+ int2le(mi4header->enctype ,buf+0x10);
+ int2le(mi4header->mi4size ,buf+0x14);
+ int2le(mi4header->plaintext ,buf+0x18);
+
+ return 0;
+}
+
+int sansa_seek_and_read(struct sansa_t* sansa, loff_t pos, unsigned char* buf, int nbytes)
+{
+ int n;
+
+ if (sansa_seek(sansa, pos) < 0) {
+ return -1;
+ }
+
+ if ((n = sansa_read(sansa,buf,nbytes)) < 0) {
+ return -1;
+ }
+
+ if (n < nbytes) {
+ fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n",
+ nbytes,n);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* We identify an E200 based on the following criteria:
+
+ 1) Exactly two partitions;
+ 2) First partition is type "W95 FAT32" (0x0b);
+ 3) Second partition is type "OS/2 hidden C: drive" (0x84);
+ 4) The "PPBL" string appears at offset 0 in the 2nd partition;
+ 5) The "PPMI" string appears at offset PPMI_OFFSET in the 2nd partition.
+*/
+
+int is_e200(struct sansa_t* sansa)
+{
+ struct mi4header_t mi4header;
+
+ /* Check partition layout */
+
+ if ((sansa->pinfo[0].type != 0x0b) || (sansa->pinfo[1].type != 0x84) ||
+ (sansa->pinfo[2].type != 0x00) || (sansa->pinfo[3].type != 0x00)) {
+ /* Bad partition layout, abort */
+ return -1;
+ }
+
+ /* Check Bootloader header */
+ if (sansa_seek_and_read(sansa, sansa->start, sectorbuf, 0x200) < 0) {
+ return -2;
+ }
+ if (memcmp(sectorbuf,"PPBL",4)!=0) {
+ /* No bootloader header, abort */
+ return -4;
+ }
+
+ /* Check Main firmware header */
+ if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET, sectorbuf, 0x200) < 0) {
+ fprintf(stderr,"[ERR] Seek to 0x%08x in is_e200 failed.\n",
+ (unsigned int)(sansa->start+PPMI_OFFSET));
+ return -5;
+ }
+ if (memcmp(sectorbuf,"PPMI",4)!=0) {
+ /* No bootloader header, abort */
+ return -7;
+ }
+
+ /* Check main mi4 file header */
+ if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET+0x200, sectorbuf, 0x200) < 0) {
+ fprintf(stderr,"[ERR] Seek to 0x%08x in is_e200 failed.\n",
+ (unsigned int)(sansa->start+PPMI_OFFSET+0x200));
+ return -5;
+ }
+
+ if (get_mi4header(sectorbuf,&mi4header) < 0) {
+ fprintf(stderr,"[ERR] Invalid mi4header\n");
+ return -6;
+ }
+
+ if ((mi4header.mi4size < 100000) &&
+ (memcmp(sectorbuf+0x1f8,"RBBL",4)!=0)) {
+ sansa->hasoldbootloader = 1;
+ } else {
+ sansa->hasoldbootloader = 0;
+ }
+
+ return 0;
+}
+
+int sansa_scan(struct sansa_t* sansa)
+{
+ int i;
+ int n = 0;
+ char last_disk[4096];
+
+ printf("[INFO] Scanning disk devices...\n");
+
+ for (i = 0; i <= 25 ; i++) {
+#ifdef __WIN32__
+ sprintf(sansa->diskname,"\\\\.\\PhysicalDrive%d",i);
+#elif defined(linux) || defined (__linux)
+ sprintf(sansa->diskname,"/dev/sd%c",'a'+i);
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \
+ || defined(__bsdi__) || defined(__DragonFly__)
+ sprintf(sansa->diskname,"/dev/da%d",i);
+#elif defined(__APPLE__) && defined(__MACH__)
+ sprintf(sansa->diskname,"/dev/disk%d",i);
+#else
+ #error No disk paths defined for this platform
+#endif
+ if (sansa_open(sansa, 1) < 0) {
+ continue;
+ }
+
+ if (read_partinfo(sansa,1) < 0) {
+ continue;
+ }
+
+ if (is_e200(sansa) < 0) {
+ continue;
+ }
+
+#ifdef __WIN32__
+ printf("[INFO] E200 found - disk device %d\n",i);
+#else
+ printf("[INFO] E200 found - %s\n",sansa->diskname);
+#endif
+ n++;
+ strcpy(last_disk,sansa->diskname);
+ sansa_close(sansa);
+ }
+
+ if (n==1) {
+ /* Remember the disk name */
+ strcpy(sansa->diskname,last_disk);
+ }
+ return n;
+}
+
+int load_original_firmware(struct sansa_t* sansa, unsigned char* buf, struct mi4header_t* mi4header)
+{
+ int ppmi_length;
+ int n;
+ unsigned char* tmpbuf;
+ int i;
+ int key_found;
+
+ /* Read 512 bytes from PPMI_OFFSET - the PPMI header plus the mi4 header */
+ if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET, buf, 512) < 0) {
+ return -1;
+ }
+
+ /* No need to check PPMI magic - it's done during init to confirm
+ this is an E200 */
+ ppmi_length = le2int(buf+4);
+
+ /* Firstly look for an original firmware after the first image */
+ if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, buf, 512) < 0) {
+ return -1;
+ }
+
+ if (get_mi4header(buf,mi4header)==0) {
+ /* We have a valid MI4 file after a bootloader, so we use this. */
+ if ((n = sansa_seek_and_read(sansa,
+ sansa->start + PPMI_OFFSET + 0x200 + ppmi_length,
+ buf, mi4header->mi4size)) < 0) {
+ return -1;
+ }
+ } else {
+ /* No valid MI4 file, so read the first image. */
+ if ((n = sansa_seek_and_read(sansa,
+ sansa->start + PPMI_OFFSET + 0x200,
+ buf, ppmi_length)) < 0) {
+ return -1;
+ }
+ }
+
+ get_mi4header(buf,mi4header);
+
+#if 0
+ printf("mi4header->version =0x%08x\n",mi4header->version);
+ printf("mi4header->length =0x%08x\n",mi4header->length);
+ printf("mi4header->crc32 =0x%08x\n",mi4header->crc32);
+ printf("mi4header->enctype =0x%08x\n",mi4header->enctype);
+ printf("mi4header->mi4size =0x%08x\n",mi4header->mi4size);
+ printf("mi4header->plaintext =0x%08x\n",mi4header->plaintext);
+#endif
+
+ /* Decrypt anything that needs decrypting. */
+ if (mi4header->plaintext < mi4header->mi4size - 0x200) {
+ /* TODO: Check different keys */
+ tmpbuf=malloc(mi4header->mi4size-(mi4header->plaintext+0x200));
+ if (tmpbuf==NULL) {
+ fprintf(stderr,"[ERR] Can not allocate memory\n");
+ return -1;
+ }
+
+ key_found=0;
+ for (i=0; i < NUM_KEYS && !key_found ; i++) {
+ tea_decrypt_buf(buf+(mi4header->plaintext+0x200),
+ tmpbuf,
+ mi4header->mi4size-(mi4header->plaintext+0x200),
+ keys[i]);
+ key_found = (le2int(tmpbuf+mi4header->length-mi4header->plaintext-4) == 0xaa55aa55);
+ }
+
+ if (key_found) {
+printf("Key found - %d\n",i);
+ memcpy(buf+(mi4header->plaintext+0x200),tmpbuf,mi4header->mi4size-(mi4header->plaintext+0x200));
+ free(tmpbuf);
+ } else {
+ fprintf(stderr,"[ERR] Failed to decrypt image, aborting\n");
+ free(tmpbuf);
+ return -1;
+ }
+ }
+
+ /* Increase plaintext value to full file */
+ mi4header->plaintext = mi4header->mi4size - 0x200;
+
+ /* Update CRC checksum */
+ chksum_crc32gentab ();
+ mi4header->crc32 = chksum_crc32(buf+0x200,mi4header->mi4size-0x200);
+
+ set_mi4header(buf,mi4header);
+
+ return 0;
+}
+
+int read_firmware(struct sansa_t* sansa, char* filename)
+{
+ int res;
+ int outfile;
+ struct mi4header_t mi4header;
+
+ res = load_original_firmware(sansa,sectorbuf,&mi4header);
+ if (res < 0)
+ return res;
+
+ outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666);
+ if (outfile < 0) {
+ fprintf(stderr,"[ERR] Couldn't open file %s\n",filename);
+ return -1;
+ }
+
+ write(outfile,sectorbuf,mi4header.mi4size);
+ close(outfile);
+
+ return 0;
+}
+
+
+int add_bootloader(struct sansa_t* sansa, char* filename, int type)
+{
+ int res;
+ int infile;
+ int bl_length;
+ struct mi4header_t mi4header;
+ int n;
+ int length;
+
+ /* Step 1 - read bootloader into RAM. */
+ infile=open(filename,O_RDONLY|O_BINARY);
+ if (infile < 0) {
+ fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename);
+ return -1;
+ }
+
+ bl_length = filesize(infile);
+
+ /* Create PPMI header */
+ memset(sectorbuf,0,0x200);
+ memcpy(sectorbuf,"PPMI",4);
+ int2le(bl_length, sectorbuf+4);
+ int2le(0x00020000, sectorbuf+8);
+
+ /* Read bootloader into sectorbuf+0x200 */
+ n = read(infile,sectorbuf+0x200,bl_length);
+ if (n < bl_length) {
+ fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n"
+ ,bl_length,n);
+ return -1;
+ }
+
+ /* Load original firmware from Sansa to the space after the bootloader */
+ res = load_original_firmware(sansa,sectorbuf+0x200+bl_length,&mi4header);
+ if (res < 0)
+ return res;
+
+ /* Now write the whole thing back to the Sansa */
+
+ if (sansa_seek(sansa, sansa->start+PPMI_OFFSET) < 0) {
+ fprintf(stderr,"[ERR] Seek to 0x%08x in add_bootloader failed.\n",
+ (unsigned int)(sansa->start+PPMI_OFFSET));
+ return -5;
+ }
+
+ length = 0x200 + bl_length + mi4header.mi4size;
+
+ n=sansa_write(sansa, sectorbuf, length);
+ if (n < length) {
+ fprintf(stderr,"[ERR] Short write in add_bootloader\n");
+ return -6;
+ }
+
+ return 0;
+}
+
+int delete_bootloader(struct sansa_t* sansa)
+{
+ int res;
+ struct mi4header_t mi4header;
+ int n;
+ int length;
+
+ /* Load original firmware from Sansa to sectorbuf+0x200 */
+ res = load_original_firmware(sansa,sectorbuf+0x200,&mi4header);
+ if (res < 0)
+ return res;
+
+ /* Create PPMI header */
+ memset(sectorbuf,0,0x200);
+ memcpy(sectorbuf,"PPMI",4);
+ int2le(mi4header.mi4size, sectorbuf+4);
+ int2le(0x00020000, sectorbuf+8);
+
+ /* Now write the whole thing back to the Sansa */
+
+ if (sansa_seek(sansa, sansa->start+PPMI_OFFSET) < 0) {
+ fprintf(stderr,"[ERR] Seek to 0x%08x in add_bootloader failed.\n",
+ (unsigned int)(sansa->start+PPMI_OFFSET));
+ return -5;
+ }
+
+ length = 0x200 + mi4header.mi4size;
+
+ n=sansa_write(sansa, sectorbuf, length);
+ if (n < length) {
+ fprintf(stderr,"[ERR] Short write in delete_bootloader\n");
+ return -6;
+ }
+
+ return 0;
+}
+
+void list_images(struct sansa_t* sansa)
+{
+ struct mi4header_t mi4header;
+ loff_t ppmi_length;
+
+ /* Check Main firmware header */
+ if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET, sectorbuf, 0x200) < 0) {
+ return;
+ }
+
+ ppmi_length = le2int(sectorbuf+4);
+
+ printf("[INFO] Image 1 - %llu bytes\n",ppmi_length);
+
+ /* Look for an original firmware after the first image */
+ if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, sectorbuf, 512) < 0) {
+ return;
+ }
+
+ if (get_mi4header(sectorbuf,&mi4header)==0) {
+ printf("[INFO] Image 2 - %d bytes\n",mi4header.mi4size);
+ }
+}
diff --git a/rbutil/sansapatcher/sansapatcher.h b/rbutil/sansapatcher/sansapatcher.h
new file mode 100644
index 0000000000..7a2345f34a
--- /dev/null
+++ b/rbutil/sansapatcher/sansapatcher.h
@@ -0,0 +1,45 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id: ipodpatcher.c 12237 2007-02-08 21:31:38Z dave $
+ *
+ * Copyright (C) 2006-2007 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 _IPODPATCHER_H
+#define _IPODPATCHER_H
+
+#include "sansaio.h"
+
+/* Size of buffer for disk I/O - 8MB is large enough for any version
+ of the Apple firmware, but not the Nano's RSRC image. */
+#define BUFFER_SIZE 8*1024*1024
+extern unsigned char* sectorbuf;
+
+#define FILETYPE_MI4 0
+#ifdef WITH_BOOTOBJS
+ #define FILETYPE_INTERNAL 1
+#endif
+
+char* get_parttype(int pt);
+int read_partinfo(struct sansa_t* sansa, int silent);
+off_t filesize(int fd);
+int is_e200(struct sansa_t* sansa);
+int sansa_scan(struct sansa_t* sansa);
+int read_firmware(struct sansa_t* sansa, char* filename);
+int add_bootloader(struct sansa_t* sansa, char* filename, int type);
+int delete_bootloader(struct sansa_t* sansa);
+void list_images(struct sansa_t* sansa);
+
+#endif