blob: 87b181b3226823ed8306972fec83899847d85a90 [file] [log] [blame]
Dave Chapman45895df2006-12-13 08:57:06 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 Dave Chapman
11 *
Dave Chapman45895df2006-12-13 08:57:06 +000012 * 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 Chapman4b7e1e02006-12-13 09:02:18 +000025#include <inttypes.h>
Dave Chapman45895df2006-12-13 08:57:06 +000026#include <sys/types.h>
27#include <sys/stat.h>
Dave Chapman45895df2006-12-13 08:57:06 +000028
29#include "parttypes.h"
Dave Chapman4b7e1e02006-12-13 09:02:18 +000030#include "ipodio.h"
31
32#define VERSION "0.5"
33
Dave Chapman75a11122006-12-14 18:41:03 +000034int verbose = 0;
Dave Chapman4b7e1e02006-12-13 09:02:18 +000035
36/* The following string appears at the start of the firmware partition */
37static 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 Chapman45895df2006-12-13 08:57:06 +000056
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 Chapman4b7e1e02006-12-13 09:02:18 +000059 and initialise it with ipod_alloc_buf()
Dave Chapman45895df2006-12-13 08:57:06 +000060*/
Dave Chapman4b7e1e02006-12-13 09:02:18 +000061
Dave Chapmanc5e30e12006-12-14 01:24:05 +000062/* 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 Chapman45895df2006-12-13 08:57:06 +000065unsigned char* sectorbuf;
66
67char* 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 Chapman45895df2006-12-13 08:57:06 +000083off_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 Chapman45895df2006-12-13 08:57:06 +000094/* Partition table parsing code taken from Rockbox */
95
Dave Chapman206238d2006-12-13 08:59:07 +000096#define MAX_SECTOR_SIZE 2048
Dave Chapman45895df2006-12-13 08:57:06 +000097#define SECTOR_SIZE 512
98
Dave Chapman4b7e1e02006-12-13 09:02:18 +000099struct partinfo_t {
Dave Chapman45895df2006-12-13 08:57:06 +0000100 unsigned long start; /* first sector (LBA) */
101 unsigned long size; /* number of sectors */
102 unsigned char type;
103};
104
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000105int static inline le2int(unsigned char* buf)
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000106{
107 int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
108
109 return res;
110}
111
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000112int 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 Chapman4b7e1e02006-12-13 09:02:18 +0000119int static inline getint16le(char* buf)
120{
121 int16_t res = (buf[1] << 8) | buf[0];
122
123 return res;
124}
125
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000126void static inline short2le(unsigned short val, unsigned char* addr)
127{
128 addr[0] = val & 0xFF;
129 addr[1] = (val >> 8) & 0xff;
130}
131
132void 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
140void 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 Chapman4b7e1e02006-12-13 09:02:18 +0000147
148
149#define BYTES2INT32(array,pos)\
150 ((long)array[pos] | ((long)array[pos+1] << 8 ) |\
Dave Chapman45895df2006-12-13 08:57:06 +0000151 ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 ))
152
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000153void display_partinfo(struct partinfo_t* pinfo, int sector_size)
Dave Chapman45895df2006-12-13 08:57:06 +0000154{
155 int i;
Dave Chapman206238d2006-12-13 08:59:07 +0000156 double sectors_per_MB = (1024.0*1024.0)/sector_size;
Dave Chapman45895df2006-12-13 08:57:06 +0000157
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000158 printf("[INFO] Part Start Sector End Sector Size (MB) Type\n");
Dave Chapman45895df2006-12-13 08:57:06 +0000159 for ( i = 0; i < 4; i++ ) {
160 if (pinfo[i].start != 0) {
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000161 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 Chapman45895df2006-12-13 08:57:06 +0000168 }
169 }
170}
171
172
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000173int read_partinfo(HANDLE dh, int sector_size, struct partinfo_t* pinfo)
Dave Chapman45895df2006-12-13 08:57:06 +0000174{
175 int i;
Dave Chapman45895df2006-12-13 08:57:06 +0000176 unsigned long count;
177
Dave Chapman8f08f292006-12-14 02:32:39 +0000178 count = ipod_read(dh,sectorbuf,sector_size);
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000179
180 if (count <= 0) {
181 print_error(" Error reading from disk: ");
Dave Chapman45895df2006-12-13 08:57:06 +0000182 return -1;
183 }
184
185 /* check that the boot sector is initialized */
Dave Chapman132b2412006-12-14 09:40:15 +0000186 if ( (sectorbuf[510] != 0x55) ||
187 (sectorbuf[511] != 0xaa)) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000188 fprintf(stderr,"[ERR] Bad boot sector signature\n");
189 return -1;
Dave Chapman45895df2006-12-13 08:57:06 +0000190 }
191
Dave Chapman132b2412006-12-14 09:40:15 +0000192 if ((memcmp(&sectorbuf[71],"iPod",4) != 0) &&
193 (memcmp(&sectorbuf[0x40],"This is your Apple iPod. You probably do not want to boot from it!",66) != 0) ) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000194 fprintf(stderr,"[ERR] Drive is not an iPod, aborting\n");
Dave Chapman45895df2006-12-13 08:57:06 +0000195 return -1;
196 }
197
198 /* parse partitions */
199 for ( i = 0; i < 4; i++ ) {
Dave Chapman132b2412006-12-14 09:40:15 +0000200 unsigned char* ptr = sectorbuf + 0x1be + 16*i;
Dave Chapman45895df2006-12-13 08:57:06 +0000201 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 Chapman8f08f292006-12-14 02:32:39 +0000214int read_partition(HANDLE dh, int outfile,unsigned long start_sector,
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000215 unsigned long count, int sector_size)
Dave Chapman45895df2006-12-13 08:57:06 +0000216{
217 int res;
218 unsigned long n;
219 int bytesleft;
220 int chunksize;
221
Dave Chapman8f08f292006-12-14 02:32:39 +0000222 fprintf(stderr,"[INFO] Seeking to sector %ld\n",start_sector);
Dave Chapman45895df2006-12-13 08:57:06 +0000223
Dave Chapman8f08f292006-12-14 02:32:39 +0000224 if (ipod_seek(dh,start_sector*sector_size) < 0) {
Dave Chapman45895df2006-12-13 08:57:06 +0000225 return -1;
226 }
227
228 fprintf(stderr,"[INFO] Writing %ld sectors to output file\n",count);
229
Dave Chapman206238d2006-12-13 08:59:07 +0000230 bytesleft = count * sector_size;
Dave Chapman45895df2006-12-13 08:57:06 +0000231 while (bytesleft > 0) {
232 if (bytesleft > BUFFER_SIZE) {
233 chunksize = BUFFER_SIZE;
234 } else {
235 chunksize = bytesleft;
236 }
237
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000238 n = ipod_read(dh, sectorbuf, chunksize);
239
240 if (n < 0) {
Dave Chapman45895df2006-12-13 08:57:06 +0000241 return -1;
242 }
243
244 if (n < chunksize) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000245 fprintf(stderr,
246 "[ERR] Short read in disk_read() - requested %d, got %lu\n",
247 chunksize,n);
Dave Chapman45895df2006-12-13 08:57:06 +0000248 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 Chapmanc5e30e12006-12-14 01:24:05 +0000261 fprintf(stderr,
262 "Short write - requested %lu, received %d - aborting.\n",n,res);
Dave Chapman45895df2006-12-13 08:57:06 +0000263 return -1;
264 }
265 }
266
267 fprintf(stderr,"[INFO] Done.\n");
268 return 0;
269}
270
Dave Chapman8f08f292006-12-14 02:32:39 +0000271int write_partition(HANDLE dh, int infile,unsigned long start_sector,
272 int sector_size)
Dave Chapman45895df2006-12-13 08:57:06 +0000273{
274 unsigned long res;
275 int n;
276 int bytesread;
277 int byteswritten = 0;
278 int eof;
279 int padding = 0;
280
Dave Chapman8f08f292006-12-14 02:32:39 +0000281 if (ipod_seek(dh, start_sector*sector_size) < 0) {
Dave Chapman45895df2006-12-13 08:57:06 +0000282 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 Chapman206238d2006-12-13 08:59:07 +0000299 if ((n % sector_size) != 0) {
300 padding = (sector_size-(n % sector_size));
Dave Chapman45895df2006-12-13 08:57:06 +0000301 n += padding;
302 }
303 }
304
305 bytesread += n;
306
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000307 res = ipod_write(dh, sectorbuf,n);
308
309 if (res < 0) {
310 print_error(" Error writing to disk: ");
Dave Chapman45895df2006-12-13 08:57:06 +0000311 fprintf(stderr,"Bytes written: %d\n",byteswritten);
312 return -1;
313 }
314
315 if (res != n) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000316 fprintf(stderr,"[ERR] Short write - requested %d, received %lu - aborting.\n",n,res);
Dave Chapman45895df2006-12-13 08:57:06 +0000317 return -1;
318 }
319
320 byteswritten += res;
321 }
322
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000323 fprintf(stderr,"[INFO] Wrote %d bytes plus %d bytes padding.\n",
324 byteswritten-padding,padding);
Dave Chapman45895df2006-12-13 08:57:06 +0000325 return 0;
326}
327
328
329void print_usage(void) {
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000330#ifdef __WIN32__
331 fprintf(stderr,"Usage: ipodpatcher DISKNO [action]\n");
332#else
333 fprintf(stderr,"Usage: ipodpatcher device [action]\n");
334#endif
Dave Chapman45895df2006-12-13 08:57:06 +0000335 fprintf(stderr,"\n");
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000336 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 Chapman75a11122006-12-14 18:41:03 +0000340 fprintf(stderr," -rf, --read-firmware filename.ipod\n");
341 fprintf(stderr," -wf, --write-firmware filename.ipod\n");
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000342 fprintf(stderr," -a, --add-bootloader filename.ipod\n");
343 fprintf(stderr," -d, --delete-bootloader\n");
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000344 fprintf(stderr,"\n");
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000345
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000346#ifdef __WIN32__
Dave Chapman45895df2006-12-13 08:57:06 +0000347 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 Chapman4b7e1e02006-12-13 09:02:18 +0000352#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 Chapman45895df2006-12-13 08:57:06 +0000357}
358
359enum {
360 NONE,
361 SHOW_INFO,
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000362 LIST_IMAGES,
Dave Chapman75a11122006-12-14 18:41:03 +0000363 DELETE_BOOTLOADER,
364 ADD_BOOTLOADER,
365 READ_FIRMWARE,
366 WRITE_FIRMWARE,
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000367 READ_PARTITION,
368 WRITE_PARTITION
Dave Chapman45895df2006-12-13 08:57:06 +0000369};
370
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000371char* ftypename[] = { "OSOS", "RSRC", "AUPD", "HIBE" };
372
373enum firmwaretype_t {
374 FTYPE_OSOS = 0,
375 FTYPE_RSRC,
376 FTYPE_AUPD,
377 FTYPE_HIBE
378};
379
380struct ipod_directory_t {
381 enum firmwaretype_t ftype;
382 int id;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000383 uint32_t devOffset; /* Offset of image relative to one sector into bootpart*/
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000384 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 Chapman75a11122006-12-14 18:41:03 +0000392
393int diskmove(HANDLE dh, int start, int nimages, struct ipod_directory_t* ipod_directory,
394 int sector_size,int delta)
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000395{
Dave Chapman75a11122006-12-14 18:41:03 +0000396 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 Chapmanc5e30e12006-12-14 01:24:05 +0000472}
473
Dave Chapman75a11122006-12-14 18:41:03 +0000474int 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
656int 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
727int 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 Chapmanc5e30e12006-12-14 01:24:05 +0000730{
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 Chapman75a11122006-12-14 18:41:03 +0000745 return -1;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000746 }
747
748 n = read(infile,header,8);
749 if (n < 8) {
750 fprintf(stderr,"[ERR] Failed to read header from %s\n",filename);
Dave Chapman75a11122006-12-14 18:41:03 +0000751 close(infile);
752 return -1;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000753 }
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 Chapman75a11122006-12-14 18:41:03 +0000857int read_firmware(HANDLE dh, char* filename, int start, int sector_size,
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000858 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 Chapmancd067552006-12-14 10:16:10 +0000904 outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000905 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 Chapmancd067552006-12-14 10:16:10 +0000911 write(outfile,sectorbuf,length);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000912 close(outfile);
913
914 return 0;
915}
916
917int read_directory(HANDLE dh, int start, int sector_size,
918 struct ipod_directory_t* ipod_directory, off_t* diroffset)
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000919{
920 int n;
921 int nimages;
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000922 unsigned char* p;
923
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000924 /* Read firmware partition header (first 512 bytes of disk - but
925 let's read a whole sector) */
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000926
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 Chapmanc5e30e12006-12-14 01:24:05 +0000938 fprintf(stderr,"[ERR] Bad firmware directory\n");
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000939 return -1;
940 }
941
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000942 *diroffset=le2int(sectorbuf+0x104) + 0x200;
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000943
944 /* Read directory */
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000945 if (ipod_seek(dh,start + *diroffset) < 0) { return -1; }
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000946
947 n=ipod_read(dh, sectorbuf, sector_size);
948 if (n < 0) { return -1; }
949
950 nimages=0;
951 p = sectorbuf;
952
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000953 while ((nimages < MAX_IMAGES) && (p < (sectorbuf + 400)) &&
954 (memcmp(p,"!ATA",4)==0)) {
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000955 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 Chapmanc5e30e12006-12-14 01:24:05 +0000965 fprintf(stderr,"[ERR] Unknown image type %c%c%c%c\n",
966 p[0],p[1],p[2],p[3]);
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000967 }
968 p+=4;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000969 ipod_directory[nimages].id=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000970 p+=4;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000971 ipod_directory[nimages].devOffset=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000972 p+=4;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000973 ipod_directory[nimages].len=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000974 p+=4;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000975 ipod_directory[nimages].addr=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000976 p+=4;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000977 ipod_directory[nimages].entryOffset=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000978 p+=4;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000979 ipod_directory[nimages].chksum=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000980 p+=4;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000981 ipod_directory[nimages].vers=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000982 p+=4;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000983 ipod_directory[nimages].loadAddr=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000984 p+=4;
985 nimages++;
986 }
987 return nimages;
988}
989
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000990int list_images(int nimages, struct ipod_directory_t* ipod_directory,
991 int sector_size)
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000992{
993 int i;
994
Dave Chapman75a11122006-12-14 18:41:03 +0000995 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 Chapman4b7e1e02006-12-13 09:02:18 +00001010 }
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001011
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 Chapmanc5e30e12006-12-14 01:24:05 +00001021 printf(" Main firmware - %d bytes\n",
1022 ipod_directory[i].len);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001023 } else {
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001024 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 Chapman4b7e1e02006-12-13 09:02:18 +00001028 }
1029 break;
1030 default:
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001031 printf(" %s - %d bytes\n",
1032 ftypename[ipod_directory[i].ftype],
1033 ipod_directory[i].len);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001034 }
1035 }
1036 printf("\n");
1037
1038 return 0;
1039}
1040
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001041
1042
1043
Dave Chapman45895df2006-12-13 08:57:06 +00001044int main(int argc, char* argv[])
1045{
1046 int i;
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001047 int infile, outfile;
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001048 int ipod_version;
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001049 unsigned int inputsize;
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001050 struct partinfo_t pinfo[4]; /* space for 4 partitions on 1 drive */
1051 int nimages;
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001052 off_t diroffset;
1053 char* modelname;
1054 int modelnum;
1055 char* filename;
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001056 struct ipod_directory_t ipod_directory[MAX_IMAGES];
1057 int action = SHOW_INFO;
Dave Chapman206238d2006-12-13 08:59:07 +00001058 int sector_size;
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001059 char devicename[4096];
Dave Chapman45895df2006-12-13 08:57:06 +00001060 HANDLE dh;
Dave Chapman45895df2006-12-13 08:57:06 +00001061
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001062 fprintf(stderr,"ipodpatcher v" VERSION " - (C) Dave Chapman 2006\n");
Dave Chapman45895df2006-12-13 08:57:06 +00001063 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 Chapman75a11122006-12-14 18:41:03 +00001066 if ((argc < 2) || (strcmp(argv[1],"-h")==0) ||
1067 (strcmp(argv[1],"--help")==0)) {
Dave Chapman45895df2006-12-13 08:57:06 +00001068 print_usage();
1069 return 1;
1070 }
1071
1072 i = 1;
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001073 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 Chapman45895df2006-12-13 08:57:06 +00001082 while (i < argc) {
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001083 if ((strcmp(argv[i],"-l")==0) || (strcmp(argv[i],"--list")==0)) {
1084 action = LIST_IMAGES;
1085 i++;
Dave Chapman75a11122006-12-14 18:41:03 +00001086 } else if ((strcmp(argv[i],"-d")==0) ||
1087 (strcmp(argv[i],"--delete-bootloader")==0)) {
1088 action = DELETE_BOOTLOADER;
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001089 i++;
Dave Chapman75a11122006-12-14 18:41:03 +00001090 } else if ((strcmp(argv[i],"-a")==0) ||
1091 (strcmp(argv[i],"--add-bootloader")==0)) {
1092 action = ADD_BOOTLOADER;
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001093 i++;
1094 if (i == argc) { print_usage(); return 1; }
1095 filename=argv[i];
1096 i++;
1097 } else if ((strcmp(argv[i],"-rf")==0) ||
Dave Chapman75a11122006-12-14 18:41:03 +00001098 (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 Chapmanc5e30e12006-12-14 01:24:05 +00001107 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 Chapman75a11122006-12-14 18:41:03 +00001125 } else if ((strcmp(argv[i],"-v")==0) ||
1126 (strcmp(argv[i],"--verbose")==0)) {
1127 verbose++;
1128 i++;
Dave Chapman45895df2006-12-13 08:57:06 +00001129 } else {
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001130 print_usage(); return 1;
Dave Chapman45895df2006-12-13 08:57:06 +00001131 }
Dave Chapman45895df2006-12-13 08:57:06 +00001132 }
1133
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001134 if (devicename[0]==0) {
Dave Chapman45895df2006-12-13 08:57:06 +00001135 print_usage();
1136 return 1;
1137 }
1138
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001139 if (ipod_alloc_buffer(&sectorbuf,BUFFER_SIZE) < 0) {
1140 fprintf(stderr,"Failed to allocate memory buffer\n");
Dave Chapman45895df2006-12-13 08:57:06 +00001141 }
1142
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001143 if (ipod_open(&dh, devicename, &sector_size) < 0) {
1144 return 1;
Dave Chapman45895df2006-12-13 08:57:06 +00001145 }
1146
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001147 fprintf(stderr,"[INFO] Reading partition table from %s\n",devicename);
Dave Chapman206238d2006-12-13 08:59:07 +00001148 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 Chapman45895df2006-12-13 08:57:06 +00001155
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001156 if (pinfo[0].start==0) {
1157 fprintf(stderr,"[ERR] No partition 0 on disk:\n");
Dave Chapman206238d2006-12-13 08:59:07 +00001158 display_partinfo(pinfo, sector_size);
Dave Chapman45895df2006-12-13 08:57:06 +00001159 return 3;
1160 }
1161
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001162 nimages=read_directory(dh, pinfo[0].start*sector_size, sector_size,
1163 ipod_directory, &diroffset);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001164 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 Chapmanc5e30e12006-12-14 01:24:05 +00001172 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 Chapman4b7e1e02006-12-13 09:02:18 +00001211 }
1212
1213 if (action==LIST_IMAGES) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001214 list_images(nimages,ipod_directory,sector_size);
Dave Chapman75a11122006-12-14 18:41:03 +00001215 } else if (action==DELETE_BOOTLOADER) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001216 if (ipod_reopen_rw(&dh, devicename) < 0) {
1217 return 5;
1218 }
1219
Dave Chapman75a11122006-12-14 18:41:03 +00001220 if (ipod_directory[0].entryOffset==0) {
1221 fprintf(stderr,"[ERR] No bootloader detected.\n");
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001222 } else {
Dave Chapman75a11122006-12-14 18:41:03 +00001223 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 Chapmanc5e30e12006-12-14 01:24:05 +00001229 }
Dave Chapman75a11122006-12-14 18:41:03 +00001230 } 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 Chapmanc5e30e12006-12-14 01:24:05 +00001239 } else {
Dave Chapman75a11122006-12-14 18:41:03 +00001240 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 Chapmanc5e30e12006-12-14 01:24:05 +00001261 }
1262 } else if (action==READ_PARTITION) {
Dave Chapman45895df2006-12-13 08:57:06 +00001263 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 Chapmanc5e30e12006-12-14 01:24:05 +00001269 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 Chapman45895df2006-12-13 08:57:06 +00001275 close(outfile);
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001276 } else if (action==WRITE_PARTITION) {
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001277 if (ipod_reopen_rw(&dh, devicename) < 0) {
1278 return 5;
Dave Chapman45895df2006-12-13 08:57:06 +00001279 }
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 Chapmanc5e30e12006-12-14 01:24:05 +00001290 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 Chapman45895df2006-12-13 08:57:06 +00001298 } else {
1299 fprintf(stderr,"[ERR] File is too large for firmware partition, aborting.\n");
1300 }
1301 }
1302
1303 close(infile);
1304 }
1305
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001306 ipod_close(dh);
1307
Dave Chapman45895df2006-12-13 08:57:06 +00001308 return 0;
1309}