From c876d3bbefe0dc00c27ca0c12d29da5874946962 Mon Sep 17 00:00:00 2001 From: Dominik Riebeling Date: Wed, 15 Dec 2021 21:04:28 +0100 Subject: rbutil: Merge rbutil with utils folder. rbutil uses several components from the utils folder, and can be considered part of utils too. Having it in a separate folder is an arbitrary split that doesn't help anymore these days, so merge them. This also allows other utils to easily use libtools.make without the need to navigate to a different folder. Change-Id: I3fc2f4de19e3e776553efb5dea5f779dfec0dc21 --- utils/sansapatcher/Makefile | 52 ++ utils/sansapatcher/README | 36 ++ utils/sansapatcher/main.c | 420 +++++++++++++ utils/sansapatcher/parttypes.h | 109 ++++ utils/sansapatcher/sansaio-posix.c | 157 +++++ utils/sansapatcher/sansaio-win32.c | 217 +++++++ utils/sansapatcher/sansaio.h | 88 +++ utils/sansapatcher/sansapatcher.c | 975 +++++++++++++++++++++++++++++++ utils/sansapatcher/sansapatcher.h | 67 +++ utils/sansapatcher/sansapatcher.manifest | 13 + utils/sansapatcher/sansapatcher.pro | 38 ++ utils/sansapatcher/sansapatcher.rc | 1 + 12 files changed, 2173 insertions(+) create mode 100644 utils/sansapatcher/Makefile create mode 100644 utils/sansapatcher/README create mode 100644 utils/sansapatcher/main.c create mode 100644 utils/sansapatcher/parttypes.h create mode 100644 utils/sansapatcher/sansaio-posix.c create mode 100644 utils/sansapatcher/sansaio-win32.c create mode 100644 utils/sansapatcher/sansaio.h create mode 100644 utils/sansapatcher/sansapatcher.c create mode 100644 utils/sansapatcher/sansapatcher.h create mode 100644 utils/sansapatcher/sansapatcher.manifest create mode 100644 utils/sansapatcher/sansapatcher.pro create mode 100644 utils/sansapatcher/sansapatcher.rc (limited to 'utils/sansapatcher') diff --git a/utils/sansapatcher/Makefile b/utils/sansapatcher/Makefile new file mode 100644 index 0000000000..85c36c6d14 --- /dev/null +++ b/utils/sansapatcher/Makefile @@ -0,0 +1,52 @@ +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ + +TARGET_DIR ?= $(shell pwd)/ +CFLAGS += -Wall -W -D_LARGEFILE64_SOURCE + +OUTPUT = sansapatcher + +# inputs +LIBSOURCES := sansapatcher.c sansaio-posix.c sansaio-win32.c +SOURCES := main.c +# additional link dependencies for the standalone executable +EXTRADEPS := + +# Releases of sansapatcher are created with "make RELEASE=1". This +# enables BOOTOBJS and uses the VERSION string defined in main.c +ifdef RELEASE +CFLAGS += -DRELEASE +BOOTOBJS=1 +endif + +ifdef WITH_BOOTOBJS +BOOTSRC = bootimg_c200.c bootimg_e200.c +SOURCES += $(BOOTSRC) +CFLAGS += -DWITH_BOOTOBJS +endif + +include ../libtools.make + +# find out if we need to link the manifest resource. +# Since libtools.make sets up BINARY we check it for the file extension .exe. +ifeq ($(findstring exe,$(BINARY)),exe) +$(BINARY): $(OBJDIR)sansapatcher-rc.o +endif + +$(OBJDIR)main.o: $(BOOTSRC) +$(OBJDIR)sansapatcher-rc.o: sansapatcher.rc sansapatcher.manifest + @echo WINDRES $(notdir $<) + $(SILENT)$(CROSS)$(WINDRES) -i sansapatcher.rc -o $@ + +bootimg_c200.c: firmware.mi4 $(BIN2C) + @echo BIN2C $< + $(SILENT)$(BIN2C) $< $* + +bootimg_e200.c: PP5022.mi4 $(BIN2C) + @echo BIN2C $< $* + $(SILENT)$(BIN2C) $< $* + diff --git a/utils/sansapatcher/README b/utils/sansapatcher/README new file mode 100644 index 0000000000..c62f676110 --- /dev/null +++ b/utils/sansapatcher/README @@ -0,0 +1,36 @@ +sansapatcher +------------ + +To compile sansapatcher, you need both the C200 and E200 Rockbox +bootloaders. The latest bootloaders can always be found here: + +http://download.rockbox.org/bootloader/sandisk-sansa/e200/PP5022.mi4 +http://download.rockbox.org/bootloader/sandisk-sansa/c200/firmware.mi4 + +Place both these files in the sansapatcher source directory, and type "make". + + +Building your own bootloaders +----------------------------- + +If you would like to compile the bootloaders yourself, they are the output of +running the "Bootloader" build for the E200 and C200 targets. + +NOTE: Unless you know what you are doing, it is recommended that you + use the official pre-built binary bootloaders linked to above. + Bootloaders compiled from current Rockbox SVN are untested and + may contain bugs preventing you from starting the device (or + worse...). + +In the Rockbox source directory, do: + +mkdir build-e200-bootloader +cd build-e200-bootloader +../tools/configure +[Select E200, then B for bootloader] +make + +This will create PP5022.mi4 which you should copy to the sansapatcher +build directory. + +A similar process for the C200 will create firmware.mi4. diff --git a/utils/sansapatcher/main.c b/utils/sansapatcher/main.c new file mode 100644 index 0000000000..d12e33c22b --- /dev/null +++ b/utils/sansapatcher/main.c @@ -0,0 +1,420 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006-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 +#include +#include +#include +#include +#include +#include +#include + +#include "sansapatcher.h" +#include "sansaio.h" +#include "parttypes.h" +#ifdef WITH_BOOTOBJS +#include "bootimg_c200.h" +#include "bootimg_e200.h" +#endif + +#ifndef VERSION +#define VERSION "0.8 with v6.0 bootloaders" +#endif + +enum { + NONE, + INSTALL, + INTERACTIVE, + SHOW_INFO, + LIST_IMAGES, + DELETE_BOOTLOADER, + ADD_BOOTLOADER, + READ_FIRMWARE, + WRITE_FIRMWARE, + READ_PARTITION, + WRITE_PARTITION, + UPDATE_OF, + UPDATE_PPBL +}; + +static 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"); + fprintf(stderr," --install\n"); + 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," -of --update-original-firmware filename.mi4\n"); + fprintf(stderr," -bl --update-ppbl filename.bin\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 or C200.\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 or C200.\n"); +#endif +} + +static const char* get_parttype(int pt) +{ + int i; + static const 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; +} + +static 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[]) +{ + char yesno[4]; + int i; + int n; + char* filename; + int action = SHOW_INFO; + struct sansa_t sansa; + int res = 0; + unsigned char* buf = NULL; + unsigned int len; + + 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 SANSA_OK; + } + + if (sansa_alloc_buffer(&sansa, BUFFER_SIZE) < 0) { + fprintf(stderr,"Failed to allocate memory buffer\n"); + return SANSA_INTERNAL_ERROR; + } + + if ((argc > 1) && (strcmp(argv[1],"--scan")==0)) { + if (sansa_scan(&sansa) == 0) + fprintf(stderr,"[ERR] No E200s or C200s found.\n"); + return SANSA_NOT_FOUND; + } + + /* 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 C200/E200s */ + n = sansa_scan(&sansa); + if (n==0) { + fprintf(stderr,"[ERR] No E200s or C200s 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 Sansa'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 Sansas found, aborting\n",n); + fprintf(stderr,"[ERR] Please connect only one Sansa 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 n > 1 ? SANSA_MULTIPLE_DEVICES : SANSA_NOT_FOUND; + } + + i = 1; + } + +#ifdef WITH_BOOTOBJS + action = INTERACTIVE; +#endif + + while (i < argc) { + if ((strcmp(argv[i],"-l")==0) || (strcmp(argv[i],"--list")==0)) { + action = LIST_IMAGES; + i++; + } else if (strcmp(argv[i],"--install")==0) { + action = INSTALL; + i++; + } 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; + i++; + if (i == argc) { print_usage(); return SANSA_WRONG_ARGUMENTS; } + filename=argv[i]; + i++; + } else if ((strcmp(argv[i],"-of")==0) || + (strcmp(argv[i],"--update-original-firmware")==0)) { + action = UPDATE_OF; + i++; + if (i == argc) { print_usage(); return SANSA_WRONG_ARGUMENTS; } + filename=argv[i]; + i++; + } else if ((strcmp(argv[i],"-bl")==0) || + (strcmp(argv[i],"--update-ppbl")==0)) { + action = UPDATE_PPBL; + i++; + if (i == argc) { print_usage(); return SANSA_WRONG_ARGUMENTS; } + 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 SANSA_WRONG_ARGUMENTS; } + filename=argv[i]; + i++; + } + } + + if (sansa.diskname[0]==0) { + print_usage(); + return SANSA_WRONG_ARGUMENTS; + } + + if (sansa_open(&sansa, 0) < 0) { + return SANSA_ACCESS_DENIED; + } + + fprintf(stderr,"[INFO] Reading partition table from %s\n",sansa.diskname); + fprintf(stderr,"[INFO] Sector size is %d bytes\n",sansa.sector_size); + + if (sansa_read_partinfo(&sansa,0) < 0) { + return SANSA_PARTITION_ERROR; + } + + display_partinfo(&sansa); + + i = is_sansa(&sansa); + if (i < 0) { + fprintf(stderr,"[ERR] Disk is not an E200 or C200 (%d), aborting.\n",i); + return SANSA_WRONG_TYPE; + } + + 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/wiki/SansaE200Install\n"); + printf("[ERR] ************************************************************************\n"); + res = SANSA_OLD_INSTALL; + } else { + if (action==LIST_IMAGES) { + sansa_list_images(&sansa); +#ifdef WITH_BOOTOBJS + } else if (action==INTERACTIVE) { + + printf("Enter i to install the Rockbox bootloader, u to uninstall\n or c to cancel and do nothing (i/u/c) :"); + + if (fgets(yesno,4,stdin)) { + if (yesno[0]=='i') { + if (sansa_reopen_rw(&sansa) < 0) { + res = SANSA_CANNOT_REOPEN; + } + if (strcmp(sansa.targetname,"c200") == 0) { + len = LEN_bootimg_c200; + buf = bootimg_c200; + } else { + len = LEN_bootimg_e200; + buf = bootimg_e200; + } + if (sansa_add_bootloader(&sansa, buf, len)==0) { + fprintf(stderr,"[INFO] Bootloader installed successfully.\n"); + } else { + fprintf(stderr,"[ERR] --install failed.\n"); + res = SANSA_INSTALL_FAILED; + } + } else if (yesno[0]=='u') { + if (sansa_reopen_rw(&sansa) < 0) { + res = SANSA_CANNOT_REOPEN; + } + + if (sansa_delete_bootloader(&sansa)==0) { + fprintf(stderr,"[INFO] Bootloader removed.\n"); + } else { + fprintf(stderr,"[ERR] Bootloader removal failed.\n"); + res = SANSA_UNINSTALL_FAILED; + } + } + } +#endif + } else if (action==READ_FIRMWARE) { + if (sansa_read_firmware(&sansa, filename)==0) { + fprintf(stderr,"[INFO] Firmware read to file %s.\n",filename); + } else { + fprintf(stderr,"[ERR] --read-firmware failed.\n"); + } +#ifdef WITH_BOOTOBJS + } else if (action==INSTALL) { + if (sansa_reopen_rw(&sansa) < 0) { + return SANSA_CANNOT_REOPEN; + } + + if (strcmp(sansa.targetname,"c200") == 0) { + len = LEN_bootimg_c200; + buf = bootimg_c200; + } else { + len = LEN_bootimg_e200; + buf = bootimg_e200; + } + + if (sansa_add_bootloader(&sansa, buf, len)==0) { + fprintf(stderr,"[INFO] Bootloader installed successfully.\n"); + } else { + fprintf(stderr,"[ERR] --install failed.\n"); + } +#endif + } else if (action==ADD_BOOTLOADER) { + if (sansa_reopen_rw(&sansa) < 0) { + return SANSA_CANNOT_REOPEN; + } + + len = sansa_read_bootloader(&sansa, filename, &buf); + if (len > 0) { + if (sansa_add_bootloader(&sansa, buf, len)==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 SANSA_CANNOT_REOPEN; + } + + if (sansa_delete_bootloader(&sansa)==0) { + fprintf(stderr,"[INFO] Bootloader removed successfully.\n"); + } else { + fprintf(stderr,"[ERR] --delete-bootloader failed.\n"); + } + } else if (action==UPDATE_OF) { + if (sansa_reopen_rw(&sansa) < 0) { + return SANSA_CANNOT_REOPEN; + } + + if (sansa_update_of(&sansa, filename)==0) { + fprintf(stderr,"[INFO] OF updated successfully.\n"); + } else { + fprintf(stderr,"[ERR] --update-original-firmware failed.\n"); + } + } else if (action==UPDATE_PPBL) { + printf("[WARN] PPBL installation will overwrite your bootloader. This will lead to a\n"); + printf(" Sansa that won't boot if the bootloader file is invalid. Only continue if\n"); + printf(" you're sure you know what you're doing.\n"); + printf(" Continue (y/n)? "); + + if (fgets(yesno,4,stdin)) { + if (yesno[0]=='y') { + if (sansa_reopen_rw(&sansa) < 0) { + return SANSA_CANNOT_REOPEN; + } + + if (sansa_update_ppbl(&sansa, filename)==0) { + fprintf(stderr,"[INFO] PPBL updated successfully.\n"); + } else { + fprintf(stderr,"[ERR] --update-ppbl failed.\n"); + } + } + } + } + } + + sansa_close(&sansa); + sansa_dealloc_buffer(&sansa); + +#ifdef WITH_BOOTOBJS + if (action==INTERACTIVE) { + printf("Press ENTER to exit sansapatcher :"); + fgets(yesno,4,stdin); + } +#endif + + return res; +} diff --git a/utils/sansapatcher/parttypes.h b/utils/sansapatcher/parttypes.h new file mode 100644 index 0000000000..f8de303553 --- /dev/null +++ b/utils/sansapatcher/parttypes.h @@ -0,0 +1,109 @@ +/* DOS partition types - taken from fdisk */ + +struct parttype { + unsigned char type; + char *name; +}; + +struct parttype parttypes[] = { + {0x00, "Empty"}, + {0x01, "FAT12"}, + {0x02, "XENIX root"}, + {0x03, "XENIX usr"}, + {0x04, "FAT16 <32M"}, + {0x05, "Extended"}, /* DOS 3.3+ extended partition */ + {0x06, "FAT16"}, /* DOS 16-bit >=32M */ + {0x07, "HPFS/NTFS"}, /* OS/2 IFS, eg, HPFS or NTFS or QNX */ + {0x08, "AIX"}, /* AIX boot (AIX -- PS/2 port) or SplitDrive */ + {0x09, "AIX bootable"}, /* AIX data or Coherent */ + {0x0a, "OS/2 Boot Manager"},/* OS/2 Boot Manager */ + {0x0b, "W95 FAT32"}, + {0x0c, "W95 FAT32 (LBA)"},/* LBA really is `Extended Int 13h' */ + {0x0e, "W95 FAT16 (LBA)"}, + {0x0f, "W95 Ext'd (LBA)"}, + {0x10, "OPUS"}, + {0x11, "Hidden FAT12"}, + {0x12, "Compaq diagnostics"}, + {0x14, "Hidden FAT16 <32M"}, + {0x16, "Hidden FAT16"}, + {0x17, "Hidden HPFS/NTFS"}, + {0x18, "AST SmartSleep"}, + {0x1b, "Hidden W95 FAT32"}, + {0x1c, "Hidden W95 FAT32 (LBA)"}, + {0x1e, "Hidden W95 FAT16 (LBA)"}, + {0x24, "NEC DOS"}, + {0x39, "Plan 9"}, + {0x3c, "PartitionMagic recovery"}, + {0x40, "Venix 80286"}, + {0x41, "PPC PReP Boot"}, + {0x42, "SFS"}, + {0x4d, "QNX4.x"}, + {0x4e, "QNX4.x 2nd part"}, + {0x4f, "QNX4.x 3rd part"}, + {0x50, "OnTrack DM"}, + {0x51, "OnTrack DM6 Aux1"}, /* (or Novell) */ + {0x52, "CP/M"}, /* CP/M or Microport SysV/AT */ + {0x53, "OnTrack DM6 Aux3"}, + {0x54, "OnTrackDM6"}, + {0x55, "EZ-Drive"}, + {0x56, "Golden Bow"}, + {0x5c, "Priam Edisk"}, + {0x61, "SpeedStor"}, + {0x63, "GNU HURD or SysV"}, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */ + {0x64, "Novell Netware 286"}, + {0x65, "Novell Netware 386"}, + {0x70, "DiskSecure Multi-Boot"}, + {0x75, "PC/IX"}, + {0x80, "Old Minix"}, /* Minix 1.4a and earlier */ + {0x81, "Minix / old Linux"},/* Minix 1.4b and later */ + {0x82, "Linux swap / Solaris"}, + {0x83, "Linux"}, + {0x84, "OS/2 hidden C: drive"}, + {0x85, "Linux extended"}, + {0x86, "NTFS volume set"}, + {0x87, "NTFS volume set"}, + {0x88, "Linux plaintext"}, + {0x8e, "Linux LVM"}, + {0x93, "Amoeba"}, + {0x94, "Amoeba BBT"}, /* (bad block table) */ + {0x9f, "BSD/OS"}, /* BSDI */ + {0xa0, "IBM Thinkpad hibernation"}, + {0xa5, "FreeBSD"}, /* various BSD flavours */ + {0xa6, "OpenBSD"}, + {0xa7, "NeXTSTEP"}, + {0xa8, "Darwin UFS"}, + {0xa9, "NetBSD"}, + {0xab, "Darwin boot"}, + {0xb7, "BSDI fs"}, + {0xb8, "BSDI swap"}, + {0xbb, "Boot Wizard hidden"}, + {0xbe, "Solaris boot"}, + {0xbf, "Solaris"}, + {0xc1, "DRDOS/sec (FAT-12)"}, + {0xc4, "DRDOS/sec (FAT-16 < 32M)"}, + {0xc6, "DRDOS/sec (FAT-16)"}, + {0xc7, "Syrinx"}, + {0xda, "Non-FS data"}, + {0xdb, "CP/M / CTOS / ..."},/* CP/M or Concurrent CP/M or + Concurrent DOS or CTOS */ + {0xde, "Dell Utility"}, /* Dell PowerEdge Server utilities */ + {0xdf, "BootIt"}, /* BootIt EMBRM */ + {0xe1, "DOS access"}, /* DOS access or SpeedStor 12-bit FAT + extended partition */ + {0xe3, "DOS R/O"}, /* DOS R/O or SpeedStor */ + {0xe4, "SpeedStor"}, /* SpeedStor 16-bit FAT extended + partition < 1024 cyl. */ + {0xeb, "BeOS fs"}, + {0xee, "EFI GPT"}, /* Intel EFI GUID Partition Table */ + {0xef, "EFI (FAT-12/16/32)"},/* Intel EFI System Partition */ + {0xf0, "Linux/PA-RISC boot"},/* Linux/PA-RISC boot loader */ + {0xf1, "SpeedStor"}, + {0xf4, "SpeedStor"}, /* SpeedStor large partition */ + {0xf2, "DOS secondary"}, /* DOS 3.3+ secondary */ + {0xfd, "Linux raid autodetect"},/* New (2.2.x) raid partition with + autodetect using persistent + superblock */ + {0xfe, "LANstep"}, /* SpeedStor >1024 cyl. or LANstep */ + {0xff, "BBT"}, /* Xenix Bad Block Table */ + { 0, 0 } +}; diff --git a/utils/sansapatcher/sansaio-posix.c b/utils/sansapatcher/sansaio-posix.c new file mode 100644 index 0000000000..44c4dcc95c --- /dev/null +++ b/utils/sansapatcher/sansaio-posix.c @@ -0,0 +1,157 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006-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. + * + ****************************************************************************/ + +#if !defined(_WIN32) /* all non-Windows platforms supported are POSIX. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(linux) || defined (__linux) +#include +#define SANSA_SECTORSIZE_IOCTL BLKSSZGET +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ + || defined(__bsdi__) || defined(__DragonFly__) +#include +#define SANSA_SECTORSIZE_IOCTL DIOCGSECTORSIZE +#elif defined(__APPLE__) && defined(__MACH__) +#include +#define SANSA_SECTORSIZE_IOCTL DKIOCGETBLOCKSIZE +#else + #error No sector-size detection implemented for this platform +#endif + +#include "sansaio.h" + +#if defined(__APPLE__) && defined(__MACH__) +static int sansa_unmount(struct sansa_t* sansa) +{ + char cmd[4096]; + int res; + + sprintf(cmd, "/usr/sbin/diskutil unmount \"%ss1\"",sansa->diskname); + fprintf(stderr,"[INFO] "); + res = system(cmd); + + if (res==0) { + return 0; + } else { + perror("Unmount failed"); + return -1; + } +} +#endif + + +void sansa_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); + if(errno == EACCES) return -2; + else 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) +{ +#if defined(__APPLE__) && defined(__MACH__) + if (sansa_unmount(sansa) < 0) + return -1; +#endif + + 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(struct sansa_t *sansa, int bufsize) +{ + sansa->sectorbuf=malloc(bufsize); + if (sansa->sectorbuf == NULL) { + return -1; + } + return 0; +} + +int sansa_dealloc_buffer(struct sansa_t* sansa) +{ + if (sansa->sectorbuf == NULL) { + return -1; + } + free(sansa->sectorbuf); + sansa->sectorbuf = NULL; + 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, int nbytes) +{ + return write(sansa->dh, sansa->sectorbuf, nbytes); +} +#endif + diff --git a/utils/sansapatcher/sansaio-win32.c b/utils/sansapatcher/sansaio-win32.c new file mode 100644 index 0000000000..ee6a8cd93d --- /dev/null +++ b/utils/sansapatcher/sansaio-win32.c @@ -0,0 +1,217 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * 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/ + * + * 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. + * + ****************************************************************************/ + +#if defined(_WIN32) +#include +#include +#include +#include +#include +#include +#include +#ifdef __WIN32__ +#include +#include +#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 sansa_print_error(char* msg) +{ + LPSTR pMsgBuf = NULL; + + printf(msg); + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 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 = CreateFileA(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) sansa_print_error(" Error opening disk: "); + if(GetLastError() == ERROR_ACCESS_DENIED) + return -2; + else + return -1; + } + + if (!lock_volume(sansa->dh)) { + if (!silent) sansa_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) sansa_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 = CreateFileA(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) { + sansa_print_error(" Error opening disk: "); + return -1; + } + + if (!lock_volume(sansa->dh)) { + sansa_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(struct sansa_t* sansa, int bufsize) +{ + /* The ReadFile function requires a memory buffer aligned to a multiple of + the disk sector size. */ + sansa->sectorbuf = (unsigned char*)VirtualAlloc(NULL, bufsize, MEM_COMMIT, PAGE_READWRITE); + if (sansa->sectorbuf == NULL) { + sansa_print_error(" Error allocating a buffer: "); + return -1; + } + return 0; +} + +int sansa_dealloc_buffer(struct sansa_t* sansa) +{ + if (sansa->sectorbuf == NULL) { + return -1; + } + if(!VirtualFree(sansa->sectorbuf, 0, MEM_RELEASE)) { + sansa_print_error(" Error releasing buffer "); + return -1; + } + sansa->sectorbuf = NULL; + 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) { + sansa_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)) { + sansa_print_error(" Error reading from disk: "); + return -1; + } + + return count; +} + +int sansa_write(struct sansa_t* sansa, int nbytes) +{ + unsigned long count; + + if (!WriteFile(sansa->dh, sansa->sectorbuf, nbytes, &count, NULL)) { + sansa_print_error(" Error writing to disk: "); + return -1; + } + + return count; +} +#endif + diff --git a/utils/sansapatcher/sansaio.h b/utils/sansapatcher/sansaio.h new file mode 100644 index 0000000000..61e2f1d1b2 --- /dev/null +++ b/utils/sansapatcher/sansaio.h @@ -0,0 +1,88 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006-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. + * + ****************************************************************************/ + +#ifndef __SANSAIO_H +#define __SANSAIO_H + +#include +#if !defined(_MSC_VER) +#include /* not available on MSVC */ +#endif + +#if defined(__WIN32__) || defined(_WIN32) +#include +#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 + +#ifdef __cplusplus +extern "C" { +#endif + +struct sansa_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; + unsigned char* sectorbuf; + char diskname[4096]; + int sector_size; + struct sansa_partinfo_t pinfo[4]; + int hasoldbootloader; + char* targetname; /* "e200" or "c200" */ + loff_t start; /* Offset in bytes of firmware partition from start of disk */ +}; + +void sansa_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, int nbytes); +int sansa_alloc_buffer(struct sansa_t* sansa, int bufsize); +int sansa_dealloc_buffer(struct sansa_t* sansa); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/utils/sansapatcher/sansapatcher.c b/utils/sansapatcher/sansapatcher.c new file mode 100644 index 0000000000..e3b105dcca --- /dev/null +++ b/utils/sansapatcher/sansapatcher.c @@ -0,0 +1,975 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006-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 +#include +#include +#include +#include +#include +#include +#include + +#include "sansaio.h" +#include "sansapatcher.h" + +/* The offset of the MI4 image header in the firmware partition */ +#define PPMI_OFFSET 0x80000 +#define NVPARAMS_OFFSET 0x780000 +#define NVPARAMS_SIZE (0x80000-0x200) + +int sansa_verbose = 0; + +/* 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(). +*/ + +static 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 + +static inline int32_t le2int(const unsigned char* buf) +{ + int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + + return res; +} + +static inline uint32_t le2uint(const unsigned char* buf) +{ + uint32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + + return res; +} + +static inline void int2le(unsigned int val, unsigned char* addr) +{ + addr[0] = val & 0xFF; + addr[1] = (val >> 8) & 0xff; + addr[2] = (val >> 16) & 0xff; + addr[3] = (val >> 24) & 0xff; +} + +#define BYTES2INT32(array,pos)\ + ((long)array[pos] | ((long)array[pos+1] << 8 ) |\ + ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 )) + +int sansa_read_partinfo(struct sansa_t* sansa, int silent) +{ + int i; + unsigned long count; + + count = sansa_read(sansa,sansa->sectorbuf, sansa->sector_size); + + if (count <= 0) { + sansa_print_error(" Error reading from disk: "); + return -1; + } + + if ((sansa->sectorbuf[510] == 0x55) && (sansa->sectorbuf[511] == 0xaa)) { + /* parse partitions */ + for ( i = 0; i < 4; i++ ) { + unsigned char* ptr = sansa->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 ((sansa->sectorbuf[0] == 'E') && (sansa->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; +} + +/* NOTE: memmem implementation copied from glibc-2.2.4 - it's a GNU + extension and is not universally. In addition, early versions of + memmem had a serious bug - the meaning of needle and haystack were + reversed. */ + +/* Copyright (C) 1991,92,93,94,96,97,98,2000 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* Return the first occurrence of NEEDLE in HAYSTACK. */ +static void * +sansa_memmem (haystack, haystack_len, needle, needle_len) + const void *haystack; + size_t haystack_len; + const void *needle; + size_t needle_len; +{ + const char *begin; + const char *const last_possible + = (const char *) haystack + haystack_len - needle_len; + + if (needle_len == 0) + /* The first occurrence of the empty string is deemed to occur at + the beginning of the string. */ + return (void *) haystack; + + /* Sanity check, otherwise the loop might search through the whole + memory. */ + if (__builtin_expect (haystack_len < needle_len, 0)) + return NULL; + + for (begin = (const char *) haystack; begin <= last_possible; ++begin) + if (begin[0] == ((const char *) needle)[0] && + !memcmp ((const void *) &begin[1], + (const void *) ((const char *) needle + 1), + needle_len - 1)) + return (void *) begin; + + return NULL; +} + +/* + * 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 (const 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 and C200 firmwares: */ +#define NUM_KEYS ((int)(sizeof(keys)/sizeof(keys[0]))) +static const uint32_t keys[][4] = { + { 0xe494e96e, 0x3ee32966, 0x6f48512b, 0xa93fbb42 }, /* "sansa" */ + { 0xd7b10538, 0xc662945b, 0x1b3fce68, 0xf389c0e6 }, /* "sansa_gh" */ + { 0x1d29ddc0, 0x2579c2cd, 0xce339e1a, 0x75465dfe }, /* sansa 103 */ + + { 0x2a7968de, 0x15127979, 0x142e60a7, 0xe49c1893 }, /* c200 1.00.03 */ + { 0xbf2d06fa, 0xf0e23d59, 0x29738132, 0xe2d04ca7 }, /* c200 1.00.04 and up*/ + { 0xa913d139, 0xf842f398, 0x3e03f1a6, 0x060ee012 }, /* c200 1.01.05 and up*/ + { 0x0fe92902, 0xe8cc0f89, 0x6ff568ba, 0x1eff5161 }, /* c200 1.01.07 */ +}; + +/* + +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. +*/ + +static void tea_decrypt(uint32_t* v0, uint32_t* v1, const 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 + */ + +static void tea_decrypt_buf(const unsigned char* src, unsigned char* dest, + size_t n, const uint32_t * initial_key) +{ + uint32_t v0, v1; + unsigned int i; + uint32_t key[4]; + + memcpy(key, initial_key, sizeof(key)); + 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(const 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,const 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); + + /* Add a dummy DSA signature */ + memset(buf+0x1c,0,40); + buf[0x2f] = 1; + + return 0; +} + +static 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 or 0x0c); + 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_sansa(struct sansa_t* sansa) +{ + struct mi4header_t mi4header; + int ppmi_length; + int ppbl_length; + + /* Check partition layout */ + if (((sansa->pinfo[0].type != 0x06) && + (sansa->pinfo[0].type != 0x0b) && + (sansa->pinfo[0].type != 0x0c) && + (sansa->pinfo[0].type != 0x0e)) || + (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, sansa->sectorbuf, 0x200) < 0) { + return -2; + } + if (memcmp(sansa->sectorbuf,"PPBL",4)!=0) { + /* No bootloader header, abort */ + return -4; + } + ppbl_length = (le2int(sansa->sectorbuf+4) + 0x1ff) & ~0x1ff; + + /* Sanity/safety check - the bootloader can't be larger than PPMI_OFFSET */ + if (ppbl_length > PPMI_OFFSET) + { + return -5; + } + + /* Load Sansa bootloader and check for "Sansa C200" magic string */ + if (sansa_seek_and_read(sansa, sansa->start + 0x200, sansa->sectorbuf, ppbl_length) < 0) { + fprintf(stderr,"[ERR] Seek and read to 0x%08"PRIx64" in is_sansa failed.\n", + sansa->start+0x200); + return -6; + } + if (sansa_memmem(sansa->sectorbuf, ppbl_length, "Sansa C200", 10) != NULL) { + /* C200 */ + sansa->targetname="c200"; + } else { + /* E200 */ + sansa->targetname="e200"; + } + + /* Check Main firmware header */ + if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET, sansa->sectorbuf, 0x200) < 0) { + fprintf(stderr,"[ERR] Seek to 0x%"PRIx64" in is_sansa failed.\n", + sansa->start+PPMI_OFFSET); + return -5; + } + if (memcmp(sansa->sectorbuf,"PPMI",4)!=0) { + /* No bootloader header, abort */ + return -7; + } + ppmi_length = le2int(sansa->sectorbuf+4); + + /* Check main mi4 file header */ + if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET+0x200, sansa->sectorbuf, 0x200) < 0) { + fprintf(stderr,"[ERR] Seek to 0x%"PRIx64" in is_sansa failed.\n", + sansa->start+PPMI_OFFSET+0x200); + return -5; + } + + if (get_mi4header(sansa->sectorbuf,&mi4header) < 0) { + fprintf(stderr,"[ERR] Invalid mi4header\n"); + return -6; + } + + /* Some sanity checks: + + 1) Main MI4 image without RBBL and < 100000 bytes -> old install + 2) Main MI4 image with RBBL but no second image -> old install + */ + + sansa->hasoldbootloader = 0; + if (memcmp(sansa->sectorbuf+0x1f8,"RBBL",4)==0) { + /* Look for an original firmware after the first image */ + if (sansa_seek_and_read(sansa, + sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, + sansa->sectorbuf, 512) < 0) { + return -7; + } + + if (get_mi4header(sansa->sectorbuf,&mi4header)!=0) { + fprintf(stderr,"[ERR] No original firmware found\n"); + sansa->hasoldbootloader = 1; + } + } else if (mi4header.mi4size < 100000) { + fprintf(stderr,"[ERR] Old bootloader found\n"); + sansa->hasoldbootloader = 1; + } + + return 0; +} + +int sansa_scan(struct sansa_t* sansa) +{ + int i; + int n = 0; + char last_disk[4096]; + int denied = 0; + int result; + + 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 ((result = sansa_open(sansa, 1)) < 0) { + if(result == -2) { + denied++; + } + sansa_close(sansa); + continue; + } + + if (sansa_read_partinfo(sansa,1) < 0) { + sansa_close(sansa); + continue; + } + + if (is_sansa(sansa) < 0) { + sansa_close(sansa); + continue; + } + +#ifdef __WIN32__ + printf("[INFO] %s found - disk device %d\n",sansa->targetname, i); +#else + printf("[INFO] %s found - %s\n",sansa->targetname, sansa->diskname); +#endif + n++; + strcpy(last_disk,sansa->diskname); + sansa_close(sansa); + } + + if (n==1) { + /* Remember the disk name */ + strcpy(sansa->diskname,last_disk); + } + else if (n == 0 && denied) { + printf("[ERR] FATAL: Permission denied on %d device(s) and no sansa detected.\n", denied); +#ifdef __WIN32__ + printf("[ERR] You need to run this program with administrator priviledges!\n"); +#else + printf("[ERR] You need permissions for raw disc access for this program to work!\n"); +#endif + } + + return (n == 0 && denied) ? -1 : n; +} + +/* Prepare original firmware for writing to the firmware partition by decrypting + and updating the header */ +static int prepare_original_firmware(struct sansa_t* sansa, unsigned char* buf, struct mi4header_t* mi4header) +{ + unsigned char* tmpbuf; + int i; + int key_found; + + 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 = (le2uint(tmpbuf+mi4header->length-mi4header->plaintext-4) == 0xaa55aa55); + } + + if (key_found) { + 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); + + /* Add Rockbox-specific header */ + memcpy(buf+0x1f8,"RBOF",4); + memcpy(buf+0x1fc,sansa->targetname,4); + + return 0; +} + +static int load_original_firmware(struct sansa_t* sansa, unsigned char* buf, struct mi4header_t* mi4header) +{ + int ppmi_length; + int n; + + /* 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; + } + } + return prepare_original_firmware(sansa, buf, mi4header); +} + +int sansa_read_firmware(struct sansa_t* sansa, const char* filename) +{ + int res; + int outfile; + struct mi4header_t mi4header; + + res = load_original_firmware(sansa,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; + } + + res = write(outfile,sansa->sectorbuf,mi4header.mi4size); + if (res != (int)mi4header.mi4size) { + fprintf(stderr,"[ERR] Write error - %d\n", res); + return -1; + } + close(outfile); + + return 0; +} + +unsigned int sansa_read_bootloader(struct sansa_t* sansa, const char* filename, unsigned char** bl_buffer) +{ + /* Step 1 - read bootloader into RAM. */ + int infile; + unsigned int n; + unsigned int len; + infile=open(filename,O_RDONLY|O_BINARY); + if (infile < 0) { + fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); + return 0; + } + + len = filesize(infile); + + unsigned char* b = malloc(len); + if (b == NULL) { + fprintf(stderr,"[ERR] Could not allocate memory for bootloader\n"); + close(infile); + return 0; + } + + n = read(infile,b,len); + close(infile); + if (n < len) { + fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n" + ,len,n); + return 0; + } + + if (memcmp(b+0x1f8,"RBBL",4)!=0) { + fprintf(stderr,"[ERR] %s is not a Rockbox bootloader, aborting.\n", + filename); + return 0; + } + if (memcmp(b+0x1fc,sansa->targetname,4)!=0) { + fprintf(stderr,"[ERR] %s is not a Rockbox bootloader for %s, aborting.\n", + filename, sansa->targetname); + return 0; + } + *bl_buffer = b; + return len; +} + +int sansa_add_bootloader(struct sansa_t* sansa, const unsigned char* bootloader, const unsigned int bl_length) +{ + int res; + struct mi4header_t mi4header; + int length; + int n; + + /* Create PPMI header */ + memset(sansa->sectorbuf,0,0x200); + memcpy(sansa->sectorbuf,"PPMI",4); + int2le(bl_length, sansa->sectorbuf+4); + int2le(0x00020000, sansa->sectorbuf+8); + + /* copy bootloader to sansa->sectorbuf+0x200 */ + memcpy(sansa->sectorbuf+0x200,bootloader,bl_length); + + /* Load original firmware from Sansa to the space after the bootloader */ + res = load_original_firmware(sansa,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%08"PRIx64" in add_bootloader failed.\n", + sansa->start+PPMI_OFFSET); + return -5; + } + + length = 0x200 + bl_length + mi4header.mi4size; + + n=sansa_write(sansa, length); + if (n < length) { + fprintf(stderr,"[ERR] Short write in add_bootloader\n"); + return -6; + } + + return 0; +} + +int sansa_delete_bootloader(struct sansa_t* sansa) +{ + int res; + struct mi4header_t mi4header; + int n; + int length; + + /* Load original firmware from Sansa to sansa->sectorbuf+0x200 */ + res = load_original_firmware(sansa,sansa->sectorbuf+0x200,&mi4header); + if (res < 0) + return res; + + /* Create PPMI header */ + memset(sansa->sectorbuf,0,0x200); + memcpy(sansa->sectorbuf,"PPMI",4); + int2le(mi4header.mi4size, sansa->sectorbuf+4); + int2le(0x00020000, sansa->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%08"PRIx64" in add_bootloader failed.\n", + sansa->start+PPMI_OFFSET); + return -5; + } + + length = 0x200 + mi4header.mi4size; + + n=sansa_write(sansa, length); + if (n < length) { + fprintf(stderr,"[ERR] Short write in delete_bootloader\n"); + return -6; + } + + return 0; +} + +/** List number of MI4 images on the player, return number. + */ +int sansa_list_images(struct sansa_t* sansa) +{ + struct mi4header_t mi4header; + loff_t ppmi_length; + int num = 0; + + /* Check Main firmware header */ + if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET, sansa->sectorbuf, 0x200) < 0) { + return 0; + } + + ppmi_length = le2int(sansa->sectorbuf+4); + + printf("[INFO] Image 1 - %"PRIu64" bytes\n",ppmi_length); + num = 1; + + /* Look for an original firmware after the first image */ + if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, sansa->sectorbuf, 512) < 0) { + return 0; + } + + if (get_mi4header(sansa->sectorbuf,&mi4header)==0) { + printf("[INFO] Image 2 - %d bytes\n",mi4header.mi4size); + num = 2; + } + return num; +} + +int sansa_update_of(struct sansa_t* sansa, const char* filename) +{ + int n; + int infile = -1; /* Prevent an erroneous "may be used uninitialised" gcc warning */ + int of_length = 0; /* Keep gcc happy when building for rbutil */ + int ppmi_length; + struct mi4header_t mi4header; + unsigned char buf[512]; + + /* Step 1 - check we have an OF on the Sansa to upgrade. We expect the + Rockbox bootloader to be installed and the OF to be after it on disk. */ + + /* Read 512 bytes from PPMI_OFFSET - the PPMI 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); + + /* 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 don't have a valid MI4 file after a bootloader, so do nothing. */ + fprintf(stderr,"[ERR] No original firmware found at 0x%08"PRIx64"\n", + sansa->start+PPMI_OFFSET+0x200+ppmi_length); + return -1; + } + + /* Step 2 - read OF 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; + } + + of_length = filesize(infile); + + /* Load original firmware from file */ + memset(sansa->sectorbuf,0,0x200); + n = read(infile,sansa->sectorbuf,of_length); + close(infile); + if (n < of_length) { + fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n" + , of_length, n); + return -1; + } + + /* Check we have a valid MI4 file. */ + if (get_mi4header(sansa->sectorbuf,&mi4header)!=0) { + fprintf(stderr,"[ERR] %s is not a valid mi4 file\n",filename); + return -1; + } + + /* Decrypt and build the header */ + if(prepare_original_firmware(sansa, sansa->sectorbuf, &mi4header)!=0){ + fprintf(stderr,"[ERR] Unable to build decrypted mi4 from %s\n" + ,filename); + return -1; + } + + /* Step 3 - write the OF to the Sansa */ + if (sansa_seek(sansa, sansa->start+PPMI_OFFSET+0x200+ppmi_length) < 0) { + fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in sansa_update_of failed.\n", + sansa->start+PPMI_OFFSET+0x200+ppmi_length); + return -1; + } + + n=sansa_write(sansa, of_length); + if (n < of_length) { + fprintf(stderr,"[ERR] Short write in sansa_update_of\n"); + return -1; + } + + /* Step 4 - zero out the nvparams section - we have to do this or we end up + with multiple copies of the nvparams data and don't know which one to + work with for the database rebuild disabling trick in our bootloader */ + if (strcmp(sansa->targetname,"e200") == 0) { + printf("[INFO] Resetting Original Firmware settings\n"); + if (sansa_seek(sansa, sansa->start+NVPARAMS_OFFSET+0x200) < 0) { + fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in sansa_update_of failed.\n", + sansa->start+NVPARAMS_OFFSET+0x200); + return -1; + } + + memset(sansa->sectorbuf,0,NVPARAMS_SIZE); + n=sansa_write(sansa, NVPARAMS_SIZE); + if (n < NVPARAMS_SIZE) { + fprintf(stderr,"[ERR] Short write in sansa_update_of\n"); + return -1; + } + } + + return 0; +} + +/* Update the PPBL (bootloader) image in the hidden firmware partition */ +int sansa_update_ppbl(struct sansa_t* sansa, const char* filename) +{ + int n; + int infile = -1; /* Prevent an erroneous "may be used uninitialised" gcc warning */ + int ppbl_length = 0; /* Keep gcc happy when building for rbutil */ + + /* 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; + } + + ppbl_length = filesize(infile); + + n = read(infile,sansa->sectorbuf+0x200,ppbl_length); + close(infile); + if (n < ppbl_length) { + fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", ppbl_length, n); + return -1; + } + + /* Step 2 - Build the header */ + memset(sansa->sectorbuf,0,0x200); + memcpy(sansa->sectorbuf,"PPBL",4); + int2le(ppbl_length, sansa->sectorbuf+4); + int2le(0x00010000, sansa->sectorbuf+8); + + /* Step 3 - write the bootloader to the Sansa */ + if (sansa_seek(sansa, sansa->start) < 0) { + fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in sansa_update_ppbl failed.\n", sansa->start); + return -1; + } + + n=sansa_write(sansa, ppbl_length + 0x200); + if (n < (ppbl_length+0x200)) { + fprintf(stderr,"[ERR] Short write in sansa_update_ppbl\n"); + return -1; + } + + return 0; +} + diff --git a/utils/sansapatcher/sansapatcher.h b/utils/sansapatcher/sansapatcher.h new file mode 100644 index 0000000000..259143e38a --- /dev/null +++ b/utils/sansapatcher/sansapatcher.h @@ -0,0 +1,67 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006-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. + * + ****************************************************************************/ + +#ifndef _SANSAPATCHER_H +#define _SANSAPATCHER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sansaio.h" + +/* exit codes */ +#define SANSA_OK 0 +#define SANSA_WRONG_ARGUMENTS 1 +#define SANSA_OPEN_INFILE_FAILED 2 +#define SANSA_PARTITION_ERROR 3 +#define SANSA_CANNOT_REOPEN 5 +#define SANSA_INSTALL_FAILED 6 +#define SANSA_UNINSTALL_FAILED 7 +#define SANSA_ACCESS_DENIED 10 +#define SANSA_NOT_FOUND 11 +#define SANSA_WRONG_DEVICE_COUNT 12 +#define SANSA_MULTIPLE_DEVICES 15 +#define SANSA_WRONG_TYPE 16 +#define SANSA_OLD_INSTALL 17 +#define SANSA_INTERNAL_ERROR 20 + +extern int sansa_verbose; +/* 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 + +int sansa_read_partinfo(struct sansa_t* sansa, int silent); +int is_sansa(struct sansa_t* sansa); +int sansa_scan(struct sansa_t* sansa); +int sansa_read_firmware(struct sansa_t* sansa, const char* filename); +unsigned int sansa_read_bootloader(struct sansa_t* sansa, const char* filename, unsigned char** bl_buffer); +int sansa_add_bootloader(struct sansa_t* sansa, const unsigned char* buf, unsigned int len); +int sansa_delete_bootloader(struct sansa_t* sansa); +int sansa_update_of(struct sansa_t* sansa,const char* filename); +int sansa_update_ppbl(struct sansa_t* sansa,const char* filename); +int sansa_list_images(struct sansa_t* sansa); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/utils/sansapatcher/sansapatcher.manifest b/utils/sansapatcher/sansapatcher.manifest new file mode 100644 index 0000000000..71bb153688 --- /dev/null +++ b/utils/sansapatcher/sansapatcher.manifest @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/utils/sansapatcher/sansapatcher.pro b/utils/sansapatcher/sansapatcher.pro new file mode 100644 index 0000000000..f8308e6283 --- /dev/null +++ b/utils/sansapatcher/sansapatcher.pro @@ -0,0 +1,38 @@ +# +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# +# 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. +# + +TEMPLATE = app +TARGET = sansapatcher +QT -= core + +SOURCES += \ + sansaio-posix.c \ + sansaio-win32.c \ + sansapatcher.c \ + main.c + +HEADERS += \ + parttypes.h \ + sansaio.h \ + sansapatcher.h \ + +RC_FILE = sansapatcher.rc + +DEFINES += _LARGEFILE64_SOURCE + +unix { + target.path = /usr/local/bin + INSTALLS += target +} diff --git a/utils/sansapatcher/sansapatcher.rc b/utils/sansapatcher/sansapatcher.rc new file mode 100644 index 0000000000..26bb8fe730 --- /dev/null +++ b/utils/sansapatcher/sansapatcher.rc @@ -0,0 +1 @@ +1 24 MOVEABLE PURE "sansapatcher.manifest" -- cgit v1.2.3