Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 1 | /*************************************************************************** |
| 2 | * __________ __ ___. |
| 3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| 4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| 7 | * \/ \/ \/ \/ \/ |
| 8 | * $Id$ |
| 9 | * |
| 10 | * Copyright (C) 2006 Dave Chapman |
| 11 | * |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 12 | * All files in this archive are subject to the GNU General Public License. |
| 13 | * See the file COPYING in the source tree root for full license agreement. |
| 14 | * |
| 15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| 16 | * KIND, either express or implied. |
| 17 | * |
| 18 | ****************************************************************************/ |
| 19 | |
| 20 | #include <stdio.h> |
| 21 | #include <unistd.h> |
| 22 | #include <fcntl.h> |
| 23 | #include <string.h> |
| 24 | #include <stdlib.h> |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 25 | #include <inttypes.h> |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 26 | #include <sys/types.h> |
| 27 | #include <sys/stat.h> |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 28 | |
| 29 | #include "parttypes.h" |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 30 | #include "ipodio.h" |
| 31 | |
| 32 | #define VERSION "0.5" |
| 33 | |
Dave Chapman | 75a1112 | 2006-12-14 18:41:03 +0000 | [diff] [blame^] | 34 | int verbose = 0; |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 35 | |
| 36 | /* The following string appears at the start of the firmware partition */ |
| 37 | static const char *apple_stop_sign = "{{~~ /-----\\ "\ |
| 38 | "{{~~ / \\ "\ |
| 39 | "{{~~| | "\ |
| 40 | "{{~~| S T O P | "\ |
| 41 | "{{~~| | "\ |
| 42 | "{{~~ \\ / "\ |
| 43 | "{{~~ \\-----/ "\ |
| 44 | "Copyright(C) 200"\ |
| 45 | "1 Apple Computer"\ |
| 46 | ", Inc.----------"\ |
| 47 | "----------------"\ |
| 48 | "----------------"\ |
| 49 | "----------------"\ |
| 50 | "----------------"\ |
| 51 | "----------------"\ |
| 52 | "---------------"; |
| 53 | |
| 54 | /* The maximum number of images in a firmware partition - a guess... */ |
| 55 | #define MAX_IMAGES 10 |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 56 | |
| 57 | /* Windows requires the buffer for disk I/O to be aligned in memory on a |
| 58 | multiple of the disk volume size - so we use a single global variable |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 59 | and initialise it with ipod_alloc_buf() |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 60 | */ |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 61 | |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 62 | /* Size of buffer for disk I/O - 8MB is large enough for any version |
| 63 | of the Apple firmware, but not the Nano's RSRC image. */ |
| 64 | #define BUFFER_SIZE 8*1024*1024 |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 65 | unsigned char* sectorbuf; |
| 66 | |
| 67 | char* get_parttype(int pt) |
| 68 | { |
| 69 | int i; |
| 70 | static char unknown[]="Unknown"; |
| 71 | |
| 72 | i=0; |
| 73 | while (parttypes[i].name != NULL) { |
| 74 | if (parttypes[i].type == pt) { |
| 75 | return (parttypes[i].name); |
| 76 | } |
| 77 | i++; |
| 78 | } |
| 79 | |
| 80 | return unknown; |
| 81 | } |
| 82 | |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 83 | off_t filesize(int fd) { |
| 84 | struct stat buf; |
| 85 | |
| 86 | if (fstat(fd,&buf) < 0) { |
| 87 | perror("[ERR] Checking filesize of input file"); |
| 88 | return -1; |
| 89 | } else { |
| 90 | return(buf.st_size); |
| 91 | } |
| 92 | } |
| 93 | |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 94 | /* Partition table parsing code taken from Rockbox */ |
| 95 | |
Dave Chapman | 206238d | 2006-12-13 08:59:07 +0000 | [diff] [blame] | 96 | #define MAX_SECTOR_SIZE 2048 |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 97 | #define SECTOR_SIZE 512 |
| 98 | |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 99 | struct partinfo_t { |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 100 | unsigned long start; /* first sector (LBA) */ |
| 101 | unsigned long size; /* number of sectors */ |
| 102 | unsigned char type; |
| 103 | }; |
| 104 | |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 105 | int static inline le2int(unsigned char* buf) |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 106 | { |
| 107 | int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; |
| 108 | |
| 109 | return res; |
| 110 | } |
| 111 | |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 112 | int static inline be2int(unsigned char* buf) |
| 113 | { |
| 114 | int32_t res = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; |
| 115 | |
| 116 | return res; |
| 117 | } |
| 118 | |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 119 | int static inline getint16le(char* buf) |
| 120 | { |
| 121 | int16_t res = (buf[1] << 8) | buf[0]; |
| 122 | |
| 123 | return res; |
| 124 | } |
| 125 | |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 126 | void static inline short2le(unsigned short val, unsigned char* addr) |
| 127 | { |
| 128 | addr[0] = val & 0xFF; |
| 129 | addr[1] = (val >> 8) & 0xff; |
| 130 | } |
| 131 | |
| 132 | void static inline int2le(unsigned int val, unsigned char* addr) |
| 133 | { |
| 134 | addr[0] = val & 0xFF; |
| 135 | addr[1] = (val >> 8) & 0xff; |
| 136 | addr[2] = (val >> 16) & 0xff; |
| 137 | addr[3] = (val >> 24) & 0xff; |
| 138 | } |
| 139 | |
| 140 | void int2be(unsigned int val, unsigned char* addr) |
| 141 | { |
| 142 | addr[0] = (val >> 24) & 0xff; |
| 143 | addr[1] = (val >> 16) & 0xff; |
| 144 | addr[2] = (val >> 8) & 0xff; |
| 145 | addr[3] = val & 0xFF; |
| 146 | } |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 147 | |
| 148 | |
| 149 | #define BYTES2INT32(array,pos)\ |
| 150 | ((long)array[pos] | ((long)array[pos+1] << 8 ) |\ |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 151 | ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 )) |
| 152 | |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 153 | void display_partinfo(struct partinfo_t* pinfo, int sector_size) |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 154 | { |
| 155 | int i; |
Dave Chapman | 206238d | 2006-12-13 08:59:07 +0000 | [diff] [blame] | 156 | double sectors_per_MB = (1024.0*1024.0)/sector_size; |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 157 | |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 158 | printf("[INFO] Part Start Sector End Sector Size (MB) Type\n"); |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 159 | for ( i = 0; i < 4; i++ ) { |
| 160 | if (pinfo[i].start != 0) { |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 161 | printf("[INFO] %d %10ld %10ld %10.1f %s (0x%02x)\n", |
| 162 | i, |
| 163 | pinfo[i].start, |
| 164 | pinfo[i].start+pinfo[i].size-1, |
| 165 | pinfo[i].size/sectors_per_MB, |
| 166 | get_parttype(pinfo[i].type), |
| 167 | pinfo[i].type); |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 168 | } |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 173 | int read_partinfo(HANDLE dh, int sector_size, struct partinfo_t* pinfo) |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 174 | { |
| 175 | int i; |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 176 | unsigned long count; |
| 177 | |
Dave Chapman | 8f08f29 | 2006-12-14 02:32:39 +0000 | [diff] [blame] | 178 | count = ipod_read(dh,sectorbuf,sector_size); |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 179 | |
| 180 | if (count <= 0) { |
| 181 | print_error(" Error reading from disk: "); |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 182 | return -1; |
| 183 | } |
| 184 | |
| 185 | /* check that the boot sector is initialized */ |
Dave Chapman | 132b241 | 2006-12-14 09:40:15 +0000 | [diff] [blame] | 186 | if ( (sectorbuf[510] != 0x55) || |
| 187 | (sectorbuf[511] != 0xaa)) { |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 188 | fprintf(stderr,"[ERR] Bad boot sector signature\n"); |
| 189 | return -1; |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 190 | } |
| 191 | |
Dave Chapman | 132b241 | 2006-12-14 09:40:15 +0000 | [diff] [blame] | 192 | if ((memcmp(§orbuf[71],"iPod",4) != 0) && |
| 193 | (memcmp(§orbuf[0x40],"This is your Apple iPod. You probably do not want to boot from it!",66) != 0) ) { |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 194 | fprintf(stderr,"[ERR] Drive is not an iPod, aborting\n"); |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 195 | return -1; |
| 196 | } |
| 197 | |
| 198 | /* parse partitions */ |
| 199 | for ( i = 0; i < 4; i++ ) { |
Dave Chapman | 132b241 | 2006-12-14 09:40:15 +0000 | [diff] [blame] | 200 | unsigned char* ptr = sectorbuf + 0x1be + 16*i; |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 201 | pinfo[i].type = ptr[4]; |
| 202 | pinfo[i].start = BYTES2INT32(ptr, 8); |
| 203 | pinfo[i].size = BYTES2INT32(ptr, 12); |
| 204 | |
| 205 | /* extended? */ |
| 206 | if ( pinfo[i].type == 5 ) { |
| 207 | /* not handled yet */ |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | return 0; |
| 212 | } |
| 213 | |
Dave Chapman | 8f08f29 | 2006-12-14 02:32:39 +0000 | [diff] [blame] | 214 | int read_partition(HANDLE dh, int outfile,unsigned long start_sector, |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 215 | unsigned long count, int sector_size) |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 216 | { |
| 217 | int res; |
| 218 | unsigned long n; |
| 219 | int bytesleft; |
| 220 | int chunksize; |
| 221 | |
Dave Chapman | 8f08f29 | 2006-12-14 02:32:39 +0000 | [diff] [blame] | 222 | fprintf(stderr,"[INFO] Seeking to sector %ld\n",start_sector); |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 223 | |
Dave Chapman | 8f08f29 | 2006-12-14 02:32:39 +0000 | [diff] [blame] | 224 | if (ipod_seek(dh,start_sector*sector_size) < 0) { |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 225 | return -1; |
| 226 | } |
| 227 | |
| 228 | fprintf(stderr,"[INFO] Writing %ld sectors to output file\n",count); |
| 229 | |
Dave Chapman | 206238d | 2006-12-13 08:59:07 +0000 | [diff] [blame] | 230 | bytesleft = count * sector_size; |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 231 | while (bytesleft > 0) { |
| 232 | if (bytesleft > BUFFER_SIZE) { |
| 233 | chunksize = BUFFER_SIZE; |
| 234 | } else { |
| 235 | chunksize = bytesleft; |
| 236 | } |
| 237 | |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 238 | n = ipod_read(dh, sectorbuf, chunksize); |
| 239 | |
| 240 | if (n < 0) { |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 241 | return -1; |
| 242 | } |
| 243 | |
| 244 | if (n < chunksize) { |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 245 | fprintf(stderr, |
| 246 | "[ERR] Short read in disk_read() - requested %d, got %lu\n", |
| 247 | chunksize,n); |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 248 | return -1; |
| 249 | } |
| 250 | |
| 251 | bytesleft -= n; |
| 252 | |
| 253 | res = write(outfile,sectorbuf,n); |
| 254 | |
| 255 | if (res < 0) { |
| 256 | perror("[ERR] write in disk_read"); |
| 257 | return -1; |
| 258 | } |
| 259 | |
| 260 | if (res != n) { |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 261 | fprintf(stderr, |
| 262 | "Short write - requested %lu, received %d - aborting.\n",n,res); |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 263 | return -1; |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | fprintf(stderr,"[INFO] Done.\n"); |
| 268 | return 0; |
| 269 | } |
| 270 | |
Dave Chapman | 8f08f29 | 2006-12-14 02:32:39 +0000 | [diff] [blame] | 271 | int write_partition(HANDLE dh, int infile,unsigned long start_sector, |
| 272 | int sector_size) |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 273 | { |
| 274 | unsigned long res; |
| 275 | int n; |
| 276 | int bytesread; |
| 277 | int byteswritten = 0; |
| 278 | int eof; |
| 279 | int padding = 0; |
| 280 | |
Dave Chapman | 8f08f29 | 2006-12-14 02:32:39 +0000 | [diff] [blame] | 281 | if (ipod_seek(dh, start_sector*sector_size) < 0) { |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 282 | return -1; |
| 283 | } |
| 284 | |
| 285 | fprintf(stderr,"[INFO] Writing input file to device\n"); |
| 286 | bytesread = 0; |
| 287 | eof = 0; |
| 288 | while (!eof) { |
| 289 | n = read(infile,sectorbuf,BUFFER_SIZE); |
| 290 | |
| 291 | if (n < 0) { |
| 292 | perror("[ERR] read in disk_write"); |
| 293 | return -1; |
| 294 | } |
| 295 | |
| 296 | if (n < BUFFER_SIZE) { |
| 297 | eof = 1; |
| 298 | /* We need to pad the last write to a multiple of SECTOR_SIZE */ |
Dave Chapman | 206238d | 2006-12-13 08:59:07 +0000 | [diff] [blame] | 299 | if ((n % sector_size) != 0) { |
| 300 | padding = (sector_size-(n % sector_size)); |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 301 | n += padding; |
| 302 | } |
| 303 | } |
| 304 | |
| 305 | bytesread += n; |
| 306 | |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 307 | res = ipod_write(dh, sectorbuf,n); |
| 308 | |
| 309 | if (res < 0) { |
| 310 | print_error(" Error writing to disk: "); |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 311 | fprintf(stderr,"Bytes written: %d\n",byteswritten); |
| 312 | return -1; |
| 313 | } |
| 314 | |
| 315 | if (res != n) { |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 316 | fprintf(stderr,"[ERR] Short write - requested %d, received %lu - aborting.\n",n,res); |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 317 | return -1; |
| 318 | } |
| 319 | |
| 320 | byteswritten += res; |
| 321 | } |
| 322 | |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 323 | fprintf(stderr,"[INFO] Wrote %d bytes plus %d bytes padding.\n", |
| 324 | byteswritten-padding,padding); |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 325 | return 0; |
| 326 | } |
| 327 | |
| 328 | |
| 329 | void print_usage(void) { |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 330 | #ifdef __WIN32__ |
| 331 | fprintf(stderr,"Usage: ipodpatcher DISKNO [action]\n"); |
| 332 | #else |
| 333 | fprintf(stderr,"Usage: ipodpatcher device [action]\n"); |
| 334 | #endif |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 335 | fprintf(stderr,"\n"); |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 336 | fprintf(stderr,"Where [action] is one of the following options:\n"); |
| 337 | fprintf(stderr," -l, --list\n"); |
| 338 | fprintf(stderr," -r, --read-partition bootpartition.bin\n"); |
| 339 | fprintf(stderr," -w, --write-partition bootpartition.bin\n"); |
Dave Chapman | 75a1112 | 2006-12-14 18:41:03 +0000 | [diff] [blame^] | 340 | fprintf(stderr," -rf, --read-firmware filename.ipod\n"); |
| 341 | fprintf(stderr," -wf, --write-firmware filename.ipod\n"); |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 342 | fprintf(stderr," -a, --add-bootloader filename.ipod\n"); |
| 343 | fprintf(stderr," -d, --delete-bootloader\n"); |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 344 | fprintf(stderr,"\n"); |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 345 | |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 346 | #ifdef __WIN32__ |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 347 | fprintf(stderr,"DISKNO is the number (e.g. 2) Windows has assigned to your ipod's hard disk.\n"); |
| 348 | fprintf(stderr,"The first hard disk in your computer (i.e. C:\\) will be disk0, the next disk\n"); |
| 349 | fprintf(stderr,"will be disk 1 etc. ipodpatcher will refuse to access a disk unless it\n"); |
| 350 | fprintf(stderr,"can identify it as being an ipod.\n"); |
| 351 | fprintf(stderr,"\n"); |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 352 | #else |
| 353 | fprintf(stderr,"\"device\" is the device node (e.g. /dev/sda) assigned to your ipod.\n"); |
| 354 | fprintf(stderr,"ipodpatcher will refuse to access a disk unless it can identify it as being\n"); |
| 355 | fprintf(stderr,"an ipod.\n"); |
| 356 | #endif |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 357 | } |
| 358 | |
| 359 | enum { |
| 360 | NONE, |
| 361 | SHOW_INFO, |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 362 | LIST_IMAGES, |
Dave Chapman | 75a1112 | 2006-12-14 18:41:03 +0000 | [diff] [blame^] | 363 | DELETE_BOOTLOADER, |
| 364 | ADD_BOOTLOADER, |
| 365 | READ_FIRMWARE, |
| 366 | WRITE_FIRMWARE, |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 367 | READ_PARTITION, |
| 368 | WRITE_PARTITION |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 369 | }; |
| 370 | |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 371 | char* ftypename[] = { "OSOS", "RSRC", "AUPD", "HIBE" }; |
| 372 | |
| 373 | enum firmwaretype_t { |
| 374 | FTYPE_OSOS = 0, |
| 375 | FTYPE_RSRC, |
| 376 | FTYPE_AUPD, |
| 377 | FTYPE_HIBE |
| 378 | }; |
| 379 | |
| 380 | struct ipod_directory_t { |
| 381 | enum firmwaretype_t ftype; |
| 382 | int id; |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 383 | uint32_t devOffset; /* Offset of image relative to one sector into bootpart*/ |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 384 | uint32_t len; |
| 385 | uint32_t addr; |
| 386 | uint32_t entryOffset; |
| 387 | uint32_t chksum; |
| 388 | uint32_t vers; |
| 389 | uint32_t loadAddr; |
| 390 | }; |
| 391 | |
Dave Chapman | 75a1112 | 2006-12-14 18:41:03 +0000 | [diff] [blame^] | 392 | |
| 393 | int diskmove(HANDLE dh, int start, int nimages, struct ipod_directory_t* ipod_directory, |
| 394 | int sector_size,int delta) |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 395 | { |
Dave Chapman | 75a1112 | 2006-12-14 18:41:03 +0000 | [diff] [blame^] | 396 | int src_start; |
| 397 | int src_end; |
| 398 | int dest_start; |
| 399 | int dest_end; |
| 400 | int bytesleft; |
| 401 | int chunksize; |
| 402 | int i; |
| 403 | int n; |
| 404 | |
| 405 | src_start = start + ipod_directory[1].devOffset + sector_size; |
| 406 | src_end = (start + ipod_directory[nimages-1].devOffset + sector_size + ipod_directory[nimages-1].len + (sector_size-1)) & ~(sector_size-1); |
| 407 | bytesleft = src_end - src_start; |
| 408 | dest_start = start + src_start + delta; |
| 409 | dest_end = start + src_end + delta; |
| 410 | |
| 411 | if (verbose) { |
| 412 | fprintf(stderr,"[INFO] Need to move images 2-%d forward %08x bytes\n",nimages,delta); |
| 413 | fprintf(stderr,"[VERB] src_start = %08x\n",src_start); |
| 414 | fprintf(stderr,"[VERB] src_end = %08x\n",src_end); |
| 415 | fprintf(stderr,"[VERB] dest_start = %08x\n",dest_start); |
| 416 | fprintf(stderr,"[VERB] dest_end = %08x\n",dest_end); |
| 417 | fprintf(stderr,"[VERB] bytes to copy = %08x\n",bytesleft); |
| 418 | } |
| 419 | |
| 420 | while (bytesleft > 0) { |
| 421 | if (bytesleft <= BUFFER_SIZE) { |
| 422 | chunksize = bytesleft; |
| 423 | } else { |
| 424 | chunksize = BUFFER_SIZE; |
| 425 | } |
| 426 | |
| 427 | if (verbose) { |
| 428 | fprintf(stderr,"[VERB] Copying %08x bytes from %08x to %08x\n", |
| 429 | chunksize, |
| 430 | dest_end-chunksize, |
| 431 | dest_end-chunksize+delta); |
| 432 | } |
| 433 | |
| 434 | |
| 435 | if (ipod_seek(dh,dest_end-chunksize) < 0) { |
| 436 | fprintf(stderr,"[ERR] Seek failed\n"); |
| 437 | return -1; |
| 438 | } |
| 439 | |
| 440 | if ((n = ipod_read(dh,sectorbuf,chunksize)) < 0) { |
| 441 | perror("[ERR] Write failed\n"); |
| 442 | return -1; |
| 443 | } |
| 444 | |
| 445 | if (n < chunksize) { |
| 446 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", |
| 447 | i,n); |
| 448 | return -1; |
| 449 | } |
| 450 | |
| 451 | if (ipod_seek(dh,dest_end-chunksize+delta) < 0) { |
| 452 | fprintf(stderr,"[ERR] Seek failed\n"); |
| 453 | return -1; |
| 454 | } |
| 455 | |
| 456 | if ((n = ipod_write(dh,sectorbuf,chunksize)) < 0) { |
| 457 | perror("[ERR] Write failed\n"); |
| 458 | return -1; |
| 459 | } |
| 460 | |
| 461 | if (n < chunksize) { |
| 462 | fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n" |
| 463 | ,i,n); |
| 464 | return -1; |
| 465 | } |
| 466 | |
| 467 | dest_end -= chunksize; |
| 468 | bytesleft -= chunksize; |
| 469 | } |
| 470 | |
| 471 | return 0; |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 472 | } |
| 473 | |
Dave Chapman | 75a1112 | 2006-12-14 18:41:03 +0000 | [diff] [blame^] | 474 | int add_bootloader(HANDLE dh, char* filename, int start, int sector_size, |
| 475 | int nimages, struct ipod_directory_t* ipod_directory, |
| 476 | off_t diroffset, int modelnum, char* modelname) |
| 477 | { |
| 478 | int length; |
| 479 | int i; |
| 480 | int n; |
| 481 | int infile; |
| 482 | int paddedlength; |
| 483 | int entryOffset; |
| 484 | int delta = 0; |
| 485 | unsigned long chksum=0; |
| 486 | unsigned long filechksum=0; |
| 487 | unsigned char header[8]; /* Header for .ipod file */ |
| 488 | |
| 489 | /* First check that the input file is the correct type for this ipod. */ |
| 490 | infile=open(filename,O_RDONLY); |
| 491 | if (infile < 0) { |
| 492 | fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); |
| 493 | return -1; |
| 494 | } |
| 495 | |
| 496 | n = read(infile,header,8); |
| 497 | if (n < 8) { |
| 498 | fprintf(stderr,"[ERR] Failed to read header from %s\n",filename); |
| 499 | close(infile); |
| 500 | return -1; |
| 501 | } |
| 502 | |
| 503 | if (memcmp(header+4,modelname,4)!=0) { |
| 504 | fprintf(stderr,"[ERR] Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n", |
| 505 | header[4],header[5],header[6],header[7],modelname); |
| 506 | close(infile); |
| 507 | return -1; |
| 508 | } |
| 509 | |
| 510 | filechksum = be2int(header); |
| 511 | |
| 512 | length=filesize(infile)-8; |
| 513 | paddedlength=(length+sector_size-1)&~(sector_size-1); |
| 514 | |
| 515 | /* Now read our bootloader - we need to check it before modifying the partition*/ |
| 516 | n = read(infile,sectorbuf,length); |
| 517 | if (n < 0) { |
| 518 | fprintf(stderr,"[ERR] Couldn't read input file\n"); |
| 519 | close(infile); |
| 520 | return -1; |
| 521 | } |
| 522 | |
| 523 | /* Calculate and confirm bootloader checksum */ |
| 524 | chksum = modelnum; |
| 525 | for (i = 0; i < length; i++) { |
| 526 | /* add 8 unsigned bits but keep a 32 bit sum */ |
| 527 | chksum += sectorbuf[i]; |
| 528 | } |
| 529 | |
| 530 | if (chksum == filechksum) { |
| 531 | fprintf(stderr,"[INFO] Checksum OK in %s\n",filename); |
| 532 | } else { |
| 533 | fprintf(stderr,"[ERR] Checksum in %s failed check\n",filename); |
| 534 | return -1; |
| 535 | } |
| 536 | |
| 537 | if (ipod_directory[0].entryOffset>0) { |
| 538 | /* Keep the same entryOffset */ |
| 539 | entryOffset = ipod_directory[0].entryOffset; |
| 540 | } else { |
| 541 | entryOffset = (ipod_directory[0].len+sector_size-1)&~(sector_size-1); |
| 542 | } |
| 543 | |
| 544 | if (entryOffset+paddedlength > BUFFER_SIZE) { |
| 545 | fprintf(stderr,"[ERR] Input file too big for buffer\n"); |
| 546 | close(infile); |
| 547 | return -1; |
| 548 | } |
| 549 | |
| 550 | if (verbose) { |
| 551 | fprintf(stderr,"[VERB] Original firmware begins at 0x%08x\n",ipod_directory[0].devOffset + sector_size); |
| 552 | fprintf(stderr,"[VERB] New entryOffset will be 0x%08x\n",entryOffset); |
| 553 | fprintf(stderr,"[VERB] End of bootloader will be at 0x%08x\n",entryOffset+paddedlength); |
| 554 | } |
| 555 | |
| 556 | /* Check if we have enough space */ |
| 557 | /* TODO: Check the size of the partition. */ |
| 558 | if (nimages > 1) { |
| 559 | if ((entryOffset+paddedlength) >= ipod_directory[1].devOffset) { |
| 560 | fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n"); |
| 561 | delta = entryOffset+paddedlength-ipod_directory[1].devOffset; |
| 562 | |
| 563 | if (diskmove(dh,start,nimages,ipod_directory,sector_size,delta) < 0) { |
| 564 | close(infile); |
| 565 | fprintf(stderr,"[ERR] Image movement failed.\n"); |
| 566 | return -1; |
| 567 | } |
| 568 | } |
| 569 | } |
| 570 | |
| 571 | |
| 572 | /* We have moved the partitions, now we can write our bootloader */ |
| 573 | |
| 574 | /* Firstly read the original firmware into sectorbuf */ |
| 575 | fprintf(stderr,"[INFO] Reading original firmware...\n"); |
| 576 | |
| 577 | if (ipod_seek(dh,start+sector_size+ipod_directory[0].devOffset) < 0) { |
| 578 | fprintf(stderr,"[ERR] Seek failed\n"); |
| 579 | return -1; |
| 580 | } |
| 581 | |
| 582 | if ((n = ipod_read(dh,sectorbuf,entryOffset)) < 0) { |
| 583 | perror("[ERR] Read failed\n"); |
| 584 | return -1; |
| 585 | } |
| 586 | |
| 587 | if (n < entryOffset) { |
| 588 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n" |
| 589 | ,i,n); |
| 590 | return -1; |
| 591 | } |
| 592 | |
| 593 | /* Now read our bootloader - we need to seek back to 8 bytes from start */ |
| 594 | lseek(infile,8,SEEK_SET); |
| 595 | n = read(infile,sectorbuf+entryOffset,length); |
| 596 | if (n < 0) { |
| 597 | fprintf(stderr,"[ERR] Couldn't read input file\n"); |
| 598 | close(infile); |
| 599 | return -1; |
| 600 | } |
| 601 | close(infile); |
| 602 | |
| 603 | /* Calculate new checksum for combined image */ |
| 604 | chksum = 0; |
| 605 | for (i=0;i<entryOffset + length; i++) { |
| 606 | chksum += sectorbuf[i]; |
| 607 | } |
| 608 | |
| 609 | /* Now write the combined firmware image to the disk */ |
| 610 | |
| 611 | if (ipod_seek(dh,start+sector_size+ipod_directory[0].devOffset) < 0) { |
| 612 | fprintf(stderr,"[ERR] Seek failed\n"); |
| 613 | return -1; |
| 614 | } |
| 615 | |
| 616 | if ((n = ipod_write(dh,sectorbuf,entryOffset+paddedlength)) < 0) { |
| 617 | perror("[ERR] Write failed\n"); |
| 618 | return -1; |
| 619 | } |
| 620 | |
| 621 | if (n < (entryOffset+paddedlength)) { |
| 622 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n" |
| 623 | ,i,n); |
| 624 | return -1; |
| 625 | } |
| 626 | |
| 627 | fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",entryOffset+paddedlength); |
| 628 | |
| 629 | |
| 630 | /* Read directory */ |
| 631 | if (ipod_seek(dh,start + diroffset) < 0) { return -1; } |
| 632 | |
| 633 | n=ipod_read(dh, sectorbuf, sector_size); |
| 634 | if (n < 0) { return -1; } |
| 635 | |
| 636 | /* Update entries for image 0 */ |
| 637 | int2le(entryOffset+length,sectorbuf+16); |
| 638 | int2le(entryOffset,sectorbuf+24); |
| 639 | int2le(chksum,sectorbuf+28); |
| 640 | |
| 641 | /* Update devOffset entries for other images, if we have moved them */ |
| 642 | if (delta > 0) { |
| 643 | for (i=1;i<nimages;i++) { |
| 644 | int2le(le2int(sectorbuf+i*40+12)+delta,sectorbuf+i*40+12); |
| 645 | } |
| 646 | } |
| 647 | |
| 648 | /* Write directory */ |
| 649 | if (ipod_seek(dh,start + diroffset) < 0) { return -1; } |
| 650 | n=ipod_write(dh, sectorbuf, sector_size); |
| 651 | if (n < 0) { return -1; } |
| 652 | |
| 653 | return 0; |
| 654 | } |
| 655 | |
| 656 | int delete_bootloader(HANDLE dh, int start, int sector_size, off_t diroffset, |
| 657 | struct ipod_directory_t* ipod_directory) |
| 658 | { |
| 659 | int length; |
| 660 | int i; |
| 661 | int n; |
| 662 | unsigned long chksum=0; /* 32 bit checksum - Rockbox .ipod style*/ |
| 663 | |
| 664 | /* Removing the bootloader involves adjusting the "length", |
| 665 | "chksum" and "entryOffset" values in the osos image's directory |
| 666 | entry. */ |
| 667 | |
| 668 | /* Firstly check we have a bootloader... */ |
| 669 | |
| 670 | if (ipod_directory[0].entryOffset == 0) { |
| 671 | fprintf(stderr,"[ERR] No bootloader found.\n"); |
| 672 | return -1; |
| 673 | } |
| 674 | |
| 675 | length = ipod_directory[0].entryOffset; |
| 676 | |
| 677 | /* Read the firmware so we can calculate the checksum */ |
| 678 | fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length); |
| 679 | |
| 680 | if (ipod_seek(dh,start+sector_size+ipod_directory[0].devOffset) < 0) { |
| 681 | return -1; |
| 682 | } |
| 683 | |
| 684 | i = (length+sector_size-1) & ~(sector_size-1); |
| 685 | fprintf(stderr,"[INFO] Padding read from 0x%08x to 0x%08x bytes\n", |
| 686 | length,i); |
| 687 | |
| 688 | if ((n = ipod_read(dh,sectorbuf,i)) < 0) { |
| 689 | return -1; |
| 690 | } |
| 691 | |
| 692 | if (n < i) { |
| 693 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", |
| 694 | i,n); |
| 695 | return -1; |
| 696 | } |
| 697 | |
| 698 | chksum = 0; |
| 699 | for (i = 0; i < length; i++) { |
| 700 | /* add 8 unsigned bits but keep a 32 bit sum */ |
| 701 | chksum += sectorbuf[i]; |
| 702 | } |
| 703 | |
| 704 | /* Now write back the updated directory entry */ |
| 705 | |
| 706 | fprintf(stderr,"[INFO] Updating firmware checksum\n"); |
| 707 | |
| 708 | /* Read directory */ |
| 709 | if (ipod_seek(dh,start + diroffset) < 0) { return -1; } |
| 710 | |
| 711 | n=ipod_read(dh, sectorbuf, sector_size); |
| 712 | if (n < 0) { return -1; } |
| 713 | |
| 714 | /* Update entries for image 0 */ |
| 715 | int2le(length,sectorbuf+16); |
| 716 | int2le(0,sectorbuf+24); |
| 717 | int2le(chksum,sectorbuf+28); |
| 718 | |
| 719 | /* Write directory */ |
| 720 | if (ipod_seek(dh,start + diroffset) < 0) { return -1; } |
| 721 | n=ipod_write(dh, sectorbuf, sector_size); |
| 722 | if (n < 0) { return -1; } |
| 723 | |
| 724 | return 0; |
| 725 | } |
| 726 | |
| 727 | int write_firmware(HANDLE dh, char* filename, int start, int sector_size, |
| 728 | int nimages, struct ipod_directory_t* ipod_directory, |
| 729 | off_t diroffset, int modelnum, char* modelname) |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 730 | { |
| 731 | int length; |
| 732 | int i; |
| 733 | int n; |
| 734 | int infile; |
| 735 | int newsize; |
| 736 | int bytesavailable; |
| 737 | unsigned long chksum=0; |
| 738 | unsigned long filechksum=0; |
| 739 | unsigned char header[8]; /* Header for .ipod file */ |
| 740 | |
| 741 | /* First check that the input file is the correct type for this ipod. */ |
| 742 | infile=open(filename,O_RDONLY); |
| 743 | if (infile < 0) { |
| 744 | fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); |
Dave Chapman | 75a1112 | 2006-12-14 18:41:03 +0000 | [diff] [blame^] | 745 | return -1; |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 746 | } |
| 747 | |
| 748 | n = read(infile,header,8); |
| 749 | if (n < 8) { |
| 750 | fprintf(stderr,"[ERR] Failed to read header from %s\n",filename); |
Dave Chapman | 75a1112 | 2006-12-14 18:41:03 +0000 | [diff] [blame^] | 751 | close(infile); |
| 752 | return -1; |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 753 | } |
| 754 | |
| 755 | if (memcmp(header+4,modelname,4)!=0) { |
| 756 | fprintf(stderr,"[ERR] Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n", |
| 757 | header[4],header[5],header[6],header[7],modelname); |
| 758 | close(infile); |
| 759 | return -1; |
| 760 | } |
| 761 | |
| 762 | filechksum = be2int(header); |
| 763 | |
| 764 | length=filesize(infile)-8; |
| 765 | newsize=(length+sector_size-1)&~(sector_size-1); |
| 766 | |
| 767 | fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n", |
| 768 | length,newsize); |
| 769 | |
| 770 | if (newsize > BUFFER_SIZE) { |
| 771 | fprintf(stderr,"[ERR] Input file too big for buffer\n"); |
| 772 | close(infile); |
| 773 | return -1; |
| 774 | } |
| 775 | |
| 776 | /* Check if we have enough space */ |
| 777 | /* TODO: Check the size of the partition. */ |
| 778 | if (nimages > 1) { |
| 779 | bytesavailable=ipod_directory[1].devOffset-ipod_directory[0].devOffset; |
| 780 | if (bytesavailable < newsize) { |
| 781 | fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n"); |
| 782 | |
| 783 | /* TODO: Implement image movement */ |
| 784 | fprintf(stderr,"[ERR] Image movement not yet implemented.\n"); |
| 785 | close(infile); |
| 786 | return -1; |
| 787 | } |
| 788 | } |
| 789 | |
| 790 | fprintf(stderr,"[INFO] Reading input file...\n"); |
| 791 | /* We now know we have enough space, so write it. */ |
| 792 | memset(sectorbuf+length,0,newsize-length); |
| 793 | n = read(infile,sectorbuf,length); |
| 794 | if (n < 0) { |
| 795 | fprintf(stderr,"[ERR] Couldn't read input file\n"); |
| 796 | close(infile); |
| 797 | return -1; |
| 798 | } |
| 799 | close(infile); |
| 800 | |
| 801 | chksum = modelnum; |
| 802 | for (i = 0; i < length; i++) { |
| 803 | /* add 8 unsigned bits but keep a 32 bit sum */ |
| 804 | chksum += sectorbuf[i]; |
| 805 | } |
| 806 | |
| 807 | if (chksum == filechksum) { |
| 808 | fprintf(stderr,"[INFO] Checksum OK in %s\n",filename); |
| 809 | } else { |
| 810 | fprintf(stderr,"[ERR] Checksum in %s failed check\n",filename); |
| 811 | return -1; |
| 812 | } |
| 813 | |
| 814 | if (ipod_seek(dh,start+sector_size+ipod_directory[0].devOffset) < 0) { |
| 815 | fprintf(stderr,"[ERR] Seek failed\n"); |
| 816 | return -1; |
| 817 | } |
| 818 | |
| 819 | if ((n = ipod_write(dh,sectorbuf,newsize)) < 0) { |
| 820 | perror("[ERR] Write failed\n"); |
| 821 | return -1; |
| 822 | } |
| 823 | |
| 824 | if (n < newsize) { |
| 825 | fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n" |
| 826 | ,i,n); |
| 827 | return -1; |
| 828 | } |
| 829 | fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",n); |
| 830 | |
| 831 | /* Now we need to update the "len", "entryOffset" and "chksum" fields */ |
| 832 | chksum = 0; |
| 833 | for (i = 0; i < length; i++) { |
| 834 | /* add 8 unsigned bits but keep a 32 bit sum */ |
| 835 | chksum += sectorbuf[i]; |
| 836 | } |
| 837 | |
| 838 | /* Read directory */ |
| 839 | if (ipod_seek(dh,start + diroffset) < 0) { return -1; } |
| 840 | |
| 841 | n=ipod_read(dh, sectorbuf, sector_size); |
| 842 | if (n < 0) { return -1; } |
| 843 | |
| 844 | /* Update entries for image 0 */ |
| 845 | int2le(length,sectorbuf+16); |
| 846 | int2le(0,sectorbuf+24); |
| 847 | int2le(chksum,sectorbuf+28); |
| 848 | |
| 849 | /* Write directory */ |
| 850 | if (ipod_seek(dh,start + diroffset) < 0) { return -1; } |
| 851 | n=ipod_write(dh, sectorbuf, sector_size); |
| 852 | if (n < 0) { return -1; } |
| 853 | |
| 854 | return 0; |
| 855 | } |
| 856 | |
Dave Chapman | 75a1112 | 2006-12-14 18:41:03 +0000 | [diff] [blame^] | 857 | int read_firmware(HANDLE dh, char* filename, int start, int sector_size, |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 858 | struct ipod_directory_t* ipod_directory, |
| 859 | int modelnum, char* modelname) |
| 860 | { |
| 861 | int length; |
| 862 | int i; |
| 863 | int outfile; |
| 864 | int n; |
| 865 | unsigned long chksum=0; /* 32 bit checksum - Rockbox .ipod style*/ |
| 866 | unsigned char header[8]; /* Header for .ipod file */ |
| 867 | |
| 868 | if (ipod_directory[0].entryOffset != 0) { |
| 869 | /* We have a bootloader... */ |
| 870 | length = ipod_directory[0].entryOffset; |
| 871 | } else { |
| 872 | length = ipod_directory[0].len; |
| 873 | } |
| 874 | |
| 875 | fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length); |
| 876 | |
| 877 | if (ipod_seek(dh,start+sector_size+ipod_directory[0].devOffset) < 0) { |
| 878 | return -1; |
| 879 | } |
| 880 | |
| 881 | i = (length+sector_size-1) & ~(sector_size-1); |
| 882 | fprintf(stderr,"[INFO] Padding read from 0x%08x to 0x%08x bytes\n", |
| 883 | length,i); |
| 884 | |
| 885 | if ((n = ipod_read(dh,sectorbuf,i)) < 0) { |
| 886 | return -1; |
| 887 | } |
| 888 | |
| 889 | if (n < i) { |
| 890 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", |
| 891 | i,n); |
| 892 | return -1; |
| 893 | } |
| 894 | |
| 895 | chksum = modelnum; |
| 896 | for (i = 0; i < length; i++) { |
| 897 | /* add 8 unsigned bits but keep a 32 bit sum */ |
| 898 | chksum += sectorbuf[i]; |
| 899 | } |
| 900 | |
| 901 | int2be(chksum,header); |
| 902 | memcpy(header+4,modelname,4); |
| 903 | |
Dave Chapman | cd06755 | 2006-12-14 10:16:10 +0000 | [diff] [blame] | 904 | outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666); |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 905 | if (outfile < 0) { |
| 906 | fprintf(stderr,"[ERR] Couldn't open file %s\n",filename); |
| 907 | return -1; |
| 908 | } |
| 909 | |
| 910 | write(outfile,header,8); |
Dave Chapman | cd06755 | 2006-12-14 10:16:10 +0000 | [diff] [blame] | 911 | write(outfile,sectorbuf,length); |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 912 | close(outfile); |
| 913 | |
| 914 | return 0; |
| 915 | } |
| 916 | |
| 917 | int read_directory(HANDLE dh, int start, int sector_size, |
| 918 | struct ipod_directory_t* ipod_directory, off_t* diroffset) |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 919 | { |
| 920 | int n; |
| 921 | int nimages; |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 922 | unsigned char* p; |
| 923 | |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 924 | /* Read firmware partition header (first 512 bytes of disk - but |
| 925 | let's read a whole sector) */ |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 926 | |
| 927 | if (ipod_seek(dh, start) < 0) { return -1; } |
| 928 | |
| 929 | n=ipod_read(dh, sectorbuf, sector_size); |
| 930 | if (n < 0) { return -1; } |
| 931 | |
| 932 | if (memcmp(sectorbuf,apple_stop_sign,sizeof(apple_stop_sign))!=0) { |
| 933 | fprintf(stderr,"[ERR] Firmware partition doesn't contain Apple copyright, aborting."); |
| 934 | return -1; |
| 935 | } |
| 936 | |
| 937 | if (memcmp(sectorbuf+0x100,"]ih[",4)!=0) { |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 938 | fprintf(stderr,"[ERR] Bad firmware directory\n"); |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 939 | return -1; |
| 940 | } |
| 941 | |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 942 | *diroffset=le2int(sectorbuf+0x104) + 0x200; |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 943 | |
| 944 | /* Read directory */ |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 945 | if (ipod_seek(dh,start + *diroffset) < 0) { return -1; } |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 946 | |
| 947 | n=ipod_read(dh, sectorbuf, sector_size); |
| 948 | if (n < 0) { return -1; } |
| 949 | |
| 950 | nimages=0; |
| 951 | p = sectorbuf; |
| 952 | |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 953 | while ((nimages < MAX_IMAGES) && (p < (sectorbuf + 400)) && |
| 954 | (memcmp(p,"!ATA",4)==0)) { |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 955 | p+=4; |
| 956 | if (memcmp(p,"soso",4)==0) { |
| 957 | ipod_directory[nimages].ftype=FTYPE_OSOS; |
| 958 | } else if (memcmp(p,"crsr",4)==0) { |
| 959 | ipod_directory[nimages].ftype=FTYPE_RSRC; |
| 960 | } else if (memcmp(p,"dpua",4)==0) { |
| 961 | ipod_directory[nimages].ftype=FTYPE_AUPD; |
| 962 | } else if (memcmp(p,"ebih",4)==0) { |
| 963 | ipod_directory[nimages].ftype=FTYPE_HIBE; |
| 964 | } else { |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 965 | fprintf(stderr,"[ERR] Unknown image type %c%c%c%c\n", |
| 966 | p[0],p[1],p[2],p[3]); |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 967 | } |
| 968 | p+=4; |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 969 | ipod_directory[nimages].id=le2int(p); |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 970 | p+=4; |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 971 | ipod_directory[nimages].devOffset=le2int(p); |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 972 | p+=4; |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 973 | ipod_directory[nimages].len=le2int(p); |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 974 | p+=4; |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 975 | ipod_directory[nimages].addr=le2int(p); |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 976 | p+=4; |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 977 | ipod_directory[nimages].entryOffset=le2int(p); |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 978 | p+=4; |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 979 | ipod_directory[nimages].chksum=le2int(p); |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 980 | p+=4; |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 981 | ipod_directory[nimages].vers=le2int(p); |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 982 | p+=4; |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 983 | ipod_directory[nimages].loadAddr=le2int(p); |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 984 | p+=4; |
| 985 | nimages++; |
| 986 | } |
| 987 | return nimages; |
| 988 | } |
| 989 | |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 990 | int list_images(int nimages, struct ipod_directory_t* ipod_directory, |
| 991 | int sector_size) |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 992 | { |
| 993 | int i; |
| 994 | |
Dave Chapman | 75a1112 | 2006-12-14 18:41:03 +0000 | [diff] [blame^] | 995 | if (verbose) { |
| 996 | printf(" Type id devOffset len addr entryOffset chksum vers loadAddr devOffset+len\n"); |
| 997 | for (i = 0 ; i < nimages; i++) { |
| 998 | printf("%d - %s 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",i, |
| 999 | ftypename[ipod_directory[i].ftype], |
| 1000 | ipod_directory[i].id, |
| 1001 | ipod_directory[i].devOffset, |
| 1002 | ipod_directory[i].len, |
| 1003 | ipod_directory[i].addr, |
| 1004 | ipod_directory[i].entryOffset, |
| 1005 | ipod_directory[i].chksum, |
| 1006 | ipod_directory[i].vers, |
| 1007 | ipod_directory[i].loadAddr, |
| 1008 | ipod_directory[i].devOffset+sector_size+((ipod_directory[i].len+sector_size-1)&~(sector_size-1))); |
| 1009 | } |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1010 | } |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1011 | |
| 1012 | printf("\n"); |
| 1013 | printf("Listing firmware partition contents:\n"); |
| 1014 | printf("\n"); |
| 1015 | |
| 1016 | for (i = 0 ; i < nimages; i++) { |
| 1017 | printf("Image %d:\n",i+1); |
| 1018 | switch(ipod_directory[i].ftype) { |
| 1019 | case FTYPE_OSOS: |
| 1020 | if (ipod_directory[i].entryOffset==0) { |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1021 | printf(" Main firmware - %d bytes\n", |
| 1022 | ipod_directory[i].len); |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1023 | } else { |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1024 | printf(" Main firmware - %d bytes\n", |
| 1025 | ipod_directory[i].entryOffset); |
| 1026 | printf(" Third-party bootloader - %d bytes\n", |
| 1027 | ipod_directory[i].len-ipod_directory[i].entryOffset); |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1028 | } |
| 1029 | break; |
| 1030 | default: |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1031 | printf(" %s - %d bytes\n", |
| 1032 | ftypename[ipod_directory[i].ftype], |
| 1033 | ipod_directory[i].len); |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1034 | } |
| 1035 | } |
| 1036 | printf("\n"); |
| 1037 | |
| 1038 | return 0; |
| 1039 | } |
| 1040 | |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1041 | |
| 1042 | |
| 1043 | |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 1044 | int main(int argc, char* argv[]) |
| 1045 | { |
| 1046 | int i; |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1047 | int infile, outfile; |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1048 | int ipod_version; |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1049 | unsigned int inputsize; |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1050 | struct partinfo_t pinfo[4]; /* space for 4 partitions on 1 drive */ |
| 1051 | int nimages; |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1052 | off_t diroffset; |
| 1053 | char* modelname; |
| 1054 | int modelnum; |
| 1055 | char* filename; |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1056 | struct ipod_directory_t ipod_directory[MAX_IMAGES]; |
| 1057 | int action = SHOW_INFO; |
Dave Chapman | 206238d | 2006-12-13 08:59:07 +0000 | [diff] [blame] | 1058 | int sector_size; |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1059 | char devicename[4096]; |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 1060 | HANDLE dh; |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 1061 | |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1062 | fprintf(stderr,"ipodpatcher v" VERSION " - (C) Dave Chapman 2006\n"); |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 1063 | fprintf(stderr,"This is free software; see the source for copying conditions. There is NO\n"); |
| 1064 | fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"); |
| 1065 | |
Dave Chapman | 75a1112 | 2006-12-14 18:41:03 +0000 | [diff] [blame^] | 1066 | if ((argc < 2) || (strcmp(argv[1],"-h")==0) || |
| 1067 | (strcmp(argv[1],"--help")==0)) { |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 1068 | print_usage(); |
| 1069 | return 1; |
| 1070 | } |
| 1071 | |
| 1072 | i = 1; |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1073 | devicename[0]=0; |
| 1074 | |
| 1075 | #ifdef __WIN32__ |
| 1076 | snprintf(devicename,sizeof(devicename),"\\\\.\\PhysicalDrive%s",argv[1]); |
| 1077 | #else |
| 1078 | strncpy(devicename,argv[1],sizeof(devicename)); |
| 1079 | #endif |
| 1080 | |
| 1081 | i = 2; |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 1082 | while (i < argc) { |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1083 | if ((strcmp(argv[i],"-l")==0) || (strcmp(argv[i],"--list")==0)) { |
| 1084 | action = LIST_IMAGES; |
| 1085 | i++; |
Dave Chapman | 75a1112 | 2006-12-14 18:41:03 +0000 | [diff] [blame^] | 1086 | } else if ((strcmp(argv[i],"-d")==0) || |
| 1087 | (strcmp(argv[i],"--delete-bootloader")==0)) { |
| 1088 | action = DELETE_BOOTLOADER; |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1089 | i++; |
Dave Chapman | 75a1112 | 2006-12-14 18:41:03 +0000 | [diff] [blame^] | 1090 | } else if ((strcmp(argv[i],"-a")==0) || |
| 1091 | (strcmp(argv[i],"--add-bootloader")==0)) { |
| 1092 | action = ADD_BOOTLOADER; |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1093 | i++; |
| 1094 | if (i == argc) { print_usage(); return 1; } |
| 1095 | filename=argv[i]; |
| 1096 | i++; |
| 1097 | } else if ((strcmp(argv[i],"-rf")==0) || |
Dave Chapman | 75a1112 | 2006-12-14 18:41:03 +0000 | [diff] [blame^] | 1098 | (strcmp(argv[i],"--read-firmware")==0)) { |
| 1099 | action = READ_FIRMWARE; |
| 1100 | i++; |
| 1101 | if (i == argc) { print_usage(); return 1; } |
| 1102 | filename=argv[i]; |
| 1103 | i++; |
| 1104 | } else if ((strcmp(argv[i],"-wf")==0) || |
| 1105 | (strcmp(argv[i],"--write-firmware")==0)) { |
| 1106 | action = WRITE_FIRMWARE; |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1107 | i++; |
| 1108 | if (i == argc) { print_usage(); return 1; } |
| 1109 | filename=argv[i]; |
| 1110 | i++; |
| 1111 | } else if ((strcmp(argv[i],"-r")==0) || |
| 1112 | (strcmp(argv[i],"--read-partition")==0)) { |
| 1113 | action = READ_PARTITION; |
| 1114 | i++; |
| 1115 | if (i == argc) { print_usage(); return 1; } |
| 1116 | filename=argv[i]; |
| 1117 | i++; |
| 1118 | } else if ((strcmp(argv[i],"-w")==0) || |
| 1119 | (strcmp(argv[i],"--write-partition")==0)) { |
| 1120 | action = WRITE_PARTITION; |
| 1121 | i++; |
| 1122 | if (i == argc) { print_usage(); return 1; } |
| 1123 | filename=argv[i]; |
| 1124 | i++; |
Dave Chapman | 75a1112 | 2006-12-14 18:41:03 +0000 | [diff] [blame^] | 1125 | } else if ((strcmp(argv[i],"-v")==0) || |
| 1126 | (strcmp(argv[i],"--verbose")==0)) { |
| 1127 | verbose++; |
| 1128 | i++; |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 1129 | } else { |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1130 | print_usage(); return 1; |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 1131 | } |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 1132 | } |
| 1133 | |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1134 | if (devicename[0]==0) { |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 1135 | print_usage(); |
| 1136 | return 1; |
| 1137 | } |
| 1138 | |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1139 | if (ipod_alloc_buffer(§orbuf,BUFFER_SIZE) < 0) { |
| 1140 | fprintf(stderr,"Failed to allocate memory buffer\n"); |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 1141 | } |
| 1142 | |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1143 | if (ipod_open(&dh, devicename, §or_size) < 0) { |
| 1144 | return 1; |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 1145 | } |
| 1146 | |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1147 | fprintf(stderr,"[INFO] Reading partition table from %s\n",devicename); |
Dave Chapman | 206238d | 2006-12-13 08:59:07 +0000 | [diff] [blame] | 1148 | fprintf(stderr,"[INFO] Sector size is %d bytes\n",sector_size); |
| 1149 | |
| 1150 | if (read_partinfo(dh,sector_size,pinfo) < 0) { |
| 1151 | return 2; |
| 1152 | } |
| 1153 | |
| 1154 | display_partinfo(pinfo, sector_size); |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 1155 | |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1156 | if (pinfo[0].start==0) { |
| 1157 | fprintf(stderr,"[ERR] No partition 0 on disk:\n"); |
Dave Chapman | 206238d | 2006-12-13 08:59:07 +0000 | [diff] [blame] | 1158 | display_partinfo(pinfo, sector_size); |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 1159 | return 3; |
| 1160 | } |
| 1161 | |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1162 | nimages=read_directory(dh, pinfo[0].start*sector_size, sector_size, |
| 1163 | ipod_directory, &diroffset); |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1164 | if (nimages <= 0) { |
| 1165 | fprintf(stderr,"[ERR] Failed to read firmware directory\n"); |
| 1166 | return 1; |
| 1167 | } |
| 1168 | |
| 1169 | ipod_version=(ipod_directory[0].vers>>12) & 0x0f; |
| 1170 | printf("[INFO] Ipod model: "); |
| 1171 | switch (ipod_version) { |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1172 | case 0x3: |
| 1173 | printf("3rd Generation\n"); |
| 1174 | modelnum = 7; |
| 1175 | modelname = "ip3g"; |
| 1176 | break; |
| 1177 | case 0x4: |
| 1178 | printf("1st Generation Mini\n"); |
| 1179 | modelnum = 9; |
| 1180 | modelname = "mini"; |
| 1181 | break; |
| 1182 | case 0x5: |
| 1183 | printf("4th Generation\n"); |
| 1184 | modelnum = 8; |
| 1185 | modelname = "ip4g"; |
| 1186 | break; |
| 1187 | case 0x6: |
| 1188 | printf("Photo/Color\n"); |
| 1189 | modelnum = 3; |
| 1190 | modelname = "ipco"; |
| 1191 | break; |
| 1192 | case 0x7: |
| 1193 | printf("2nd Generation Mini\n"); |
| 1194 | modelnum = 11; |
| 1195 | modelname = "mn2g"; |
| 1196 | break; |
| 1197 | case 0xc: |
| 1198 | printf("1st Generation Nano\n"); |
| 1199 | modelnum = 4; |
| 1200 | modelname = "nano"; |
| 1201 | break; |
| 1202 | case 0xb: |
| 1203 | printf("Video (aka 5th Generation)\n"); |
| 1204 | modelnum = 5; |
| 1205 | modelname = "ipvd"; |
| 1206 | break; |
| 1207 | default: |
| 1208 | printf("[ERR] Unknown firmware version (0x%08x)\n", |
| 1209 | ipod_directory[0].vers); |
| 1210 | return -1; |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1211 | } |
| 1212 | |
| 1213 | if (action==LIST_IMAGES) { |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1214 | list_images(nimages,ipod_directory,sector_size); |
Dave Chapman | 75a1112 | 2006-12-14 18:41:03 +0000 | [diff] [blame^] | 1215 | } else if (action==DELETE_BOOTLOADER) { |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1216 | if (ipod_reopen_rw(&dh, devicename) < 0) { |
| 1217 | return 5; |
| 1218 | } |
| 1219 | |
Dave Chapman | 75a1112 | 2006-12-14 18:41:03 +0000 | [diff] [blame^] | 1220 | if (ipod_directory[0].entryOffset==0) { |
| 1221 | fprintf(stderr,"[ERR] No bootloader detected.\n"); |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1222 | } else { |
Dave Chapman | 75a1112 | 2006-12-14 18:41:03 +0000 | [diff] [blame^] | 1223 | if (delete_bootloader(dh, pinfo[0].start*sector_size, sector_size, |
| 1224 | diroffset, ipod_directory)==0) { |
| 1225 | fprintf(stderr,"[INFO] Bootloader removed.\n"); |
| 1226 | } else { |
| 1227 | fprintf(stderr,"[ERR] --delete-bootloader failed.\n"); |
| 1228 | } |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1229 | } |
Dave Chapman | 75a1112 | 2006-12-14 18:41:03 +0000 | [diff] [blame^] | 1230 | } else if (action==ADD_BOOTLOADER) { |
| 1231 | if (ipod_reopen_rw(&dh, devicename) < 0) { |
| 1232 | return 5; |
| 1233 | } |
| 1234 | |
| 1235 | if (add_bootloader(dh, filename,pinfo[0].start*sector_size, |
| 1236 | sector_size, nimages, ipod_directory, diroffset, |
| 1237 | modelnum, modelname)==0) { |
| 1238 | fprintf(stderr,"[INFO] Bootloader %s written to device.\n",filename); |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1239 | } else { |
Dave Chapman | 75a1112 | 2006-12-14 18:41:03 +0000 | [diff] [blame^] | 1240 | fprintf(stderr,"[ERR] --add-bootloader failed.\n"); |
| 1241 | } |
| 1242 | } else if (action==WRITE_FIRMWARE) { |
| 1243 | if (ipod_reopen_rw(&dh, devicename) < 0) { |
| 1244 | return 5; |
| 1245 | } |
| 1246 | |
| 1247 | if (write_firmware(dh, filename,pinfo[0].start*sector_size, |
| 1248 | sector_size, nimages, ipod_directory, diroffset, |
| 1249 | modelnum, modelname)==0) { |
| 1250 | fprintf(stderr,"[INFO] Firmware %s written to device.\n",filename); |
| 1251 | } else { |
| 1252 | fprintf(stderr,"[ERR] --write-firmware failed.\n"); |
| 1253 | } |
| 1254 | } else if (action==READ_FIRMWARE) { |
| 1255 | if (read_firmware(dh, filename,pinfo[0].start*sector_size, |
| 1256 | sector_size, ipod_directory, modelnum, modelname |
| 1257 | )==0) { |
| 1258 | fprintf(stderr,"[INFO] Firmware read to file %s.\n",filename); |
| 1259 | } else { |
| 1260 | fprintf(stderr,"[ERR] --read-firmware failed.\n"); |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1261 | } |
| 1262 | } else if (action==READ_PARTITION) { |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 1263 | outfile = open(filename,O_CREAT|O_WRONLY|O_BINARY,S_IREAD|S_IWRITE); |
| 1264 | if (outfile < 0) { |
| 1265 | perror(filename); |
| 1266 | return 4; |
| 1267 | } |
| 1268 | |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1269 | if (read_partition(dh, outfile, pinfo[0].start, pinfo[0].size, |
| 1270 | sector_size) < 0) { |
| 1271 | fprintf(stderr,"[ERR] --read-partition failed.\n"); |
| 1272 | } else { |
| 1273 | fprintf(stderr,"[INFO] Partition extracted to %s.\n",filename); |
| 1274 | } |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 1275 | close(outfile); |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1276 | } else if (action==WRITE_PARTITION) { |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1277 | if (ipod_reopen_rw(&dh, devicename) < 0) { |
| 1278 | return 5; |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 1279 | } |
| 1280 | |
| 1281 | infile = open(filename,O_RDONLY|O_BINARY); |
| 1282 | if (infile < 0) { |
| 1283 | perror(filename); |
| 1284 | return 2; |
| 1285 | } |
| 1286 | |
| 1287 | /* Check filesize is <= partition size */ |
| 1288 | inputsize=filesize(infile); |
| 1289 | if (inputsize > 0) { |
Dave Chapman | c5e30e1 | 2006-12-14 01:24:05 +0000 | [diff] [blame] | 1290 | if (inputsize <= (pinfo[0].size*sector_size)) { |
| 1291 | fprintf(stderr,"[INFO] Input file is %u bytes\n",inputsize); |
| 1292 | if (write_partition(dh,infile,pinfo[0].start, |
| 1293 | sector_size) < 0) { |
| 1294 | fprintf(stderr,"[ERR] --write-partition failed.\n"); |
| 1295 | } else { |
| 1296 | fprintf(stderr,"[INFO] %s restored to partition\n",filename); |
| 1297 | } |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 1298 | } else { |
| 1299 | fprintf(stderr,"[ERR] File is too large for firmware partition, aborting.\n"); |
| 1300 | } |
| 1301 | } |
| 1302 | |
| 1303 | close(infile); |
| 1304 | } |
| 1305 | |
Dave Chapman | 4b7e1e0 | 2006-12-13 09:02:18 +0000 | [diff] [blame] | 1306 | ipod_close(dh); |
| 1307 | |
Dave Chapman | 45895df | 2006-12-13 08:57:06 +0000 | [diff] [blame] | 1308 | return 0; |
| 1309 | } |