blob: 2177320f2aea32ece88e399ca02b8a89ead8650a [file] [log] [blame]
Dave Chapman45895df2006-12-13 08:57:06 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
Dave Chapmane332f4c2007-02-05 01:20:20 +000010 * Copyright (C) 2006-2007 Dave Chapman
Dave Chapman45895df2006-12-13 08:57:06 +000011 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
Dave Chapman45895df2006-12-13 08:57:06 +000016 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include <stdio.h>
23#include <unistd.h>
24#include <fcntl.h>
25#include <string.h>
26#include <stdlib.h>
Dave Chapman4b7e1e02006-12-13 09:02:18 +000027#include <inttypes.h>
Dave Chapman6e797152007-09-08 23:27:49 +000028#include <stdbool.h>
Dave Chapman45895df2006-12-13 08:57:06 +000029#include <sys/types.h>
30#include <sys/stat.h>
Dave Chapman45895df2006-12-13 08:57:06 +000031
32#include "parttypes.h"
Dave Chapman4b7e1e02006-12-13 09:02:18 +000033#include "ipodio.h"
Dave Chapman79a1fb12007-02-08 23:57:41 +000034#include "ipodpatcher.h"
Dave Chapman4b7e1e02006-12-13 09:02:18 +000035
Dave Chapmanbdc27ff2007-02-08 18:05:50 +000036#ifdef WITH_BOOTOBJS
Dave Chapman6a20def2007-07-26 20:21:11 +000037#include "ipod1g2g.h"
Dave Chapmanbdc27ff2007-02-08 18:05:50 +000038#include "ipod3g.h"
39#include "ipod4g.h"
40#include "ipodmini.h"
41#include "ipodmini2g.h"
42#include "ipodcolor.h"
43#include "ipodnano.h"
44#include "ipodvideo.h"
45#endif
46
Dave Chapman6e797152007-09-08 23:27:49 +000047#ifndef RBUTIL
48#include "arc4.h"
49#endif
50
Dominik Riebelingd131a312008-06-17 17:52:13 +000051int ipod_verbose = 0;
Dave Chapman4b7e1e02006-12-13 09:02:18 +000052
Dominik Riebelingd131a312008-06-17 17:52:13 +000053unsigned char* ipod_sectorbuf;
Dave Chapman4b7e1e02006-12-13 09:02:18 +000054
55/* The following string appears at the start of the firmware partition */
56static const char *apple_stop_sign = "{{~~ /-----\\ "\
57 "{{~~ / \\ "\
58 "{{~~| | "\
59 "{{~~| S T O P | "\
60 "{{~~| | "\
61 "{{~~ \\ / "\
62 "{{~~ \\-----/ "\
63 "Copyright(C) 200"\
64 "1 Apple Computer"\
65 ", Inc.----------"\
66 "----------------"\
67 "----------------"\
68 "----------------"\
69 "----------------"\
70 "----------------"\
71 "---------------";
72
Dave Chapman45895df2006-12-13 08:57:06 +000073/* Windows requires the buffer for disk I/O to be aligned in memory on a
74 multiple of the disk volume size - so we use a single global variable
Dave Chapman4b7e1e02006-12-13 09:02:18 +000075 and initialise it with ipod_alloc_buf()
Dave Chapman45895df2006-12-13 08:57:06 +000076*/
Dave Chapman4b7e1e02006-12-13 09:02:18 +000077
Dave Chapman45895df2006-12-13 08:57:06 +000078char* get_parttype(int pt)
79{
80 int i;
81 static char unknown[]="Unknown";
82
Dave Chapman2cc80f52007-07-29 21:19:14 +000083 if (pt == PARTTYPE_HFS) {
Dave Chapman6e641e82007-02-05 18:29:39 +000084 return "HFS/HFS+";
85 }
86
Dave Chapman45895df2006-12-13 08:57:06 +000087 i=0;
88 while (parttypes[i].name != NULL) {
89 if (parttypes[i].type == pt) {
90 return (parttypes[i].name);
91 }
92 i++;
93 }
94
95 return unknown;
96}
97
Dave Chapman45895df2006-12-13 08:57:06 +000098off_t filesize(int fd) {
99 struct stat buf;
100
101 if (fstat(fd,&buf) < 0) {
102 perror("[ERR] Checking filesize of input file");
103 return -1;
104 } else {
105 return(buf.st_size);
106 }
107}
108
Dave Chapman45895df2006-12-13 08:57:06 +0000109/* Partition table parsing code taken from Rockbox */
110
Dave Chapman206238d2006-12-13 08:59:07 +0000111#define MAX_SECTOR_SIZE 2048
Dave Chapman45895df2006-12-13 08:57:06 +0000112#define SECTOR_SIZE 512
113
Dave Chapman2cc80f52007-07-29 21:19:14 +0000114static inline unsigned short le2ushort(unsigned char* buf)
Dave Chapman31aa4522007-02-04 11:42:11 +0000115{
116 unsigned short res = (buf[1] << 8) | buf[0];
117
118 return res;
119}
Dave Chapman45895df2006-12-13 08:57:06 +0000120
Dave Chapman2cc80f52007-07-29 21:19:14 +0000121static inline int le2int(unsigned char* buf)
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000122{
123 int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
124
125 return res;
126}
127
Dave Chapman2cc80f52007-07-29 21:19:14 +0000128static inline int be2int(unsigned char* buf)
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000129{
130 int32_t res = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
131
132 return res;
133}
134
Dave Chapman2cc80f52007-07-29 21:19:14 +0000135static inline int getint16le(char* buf)
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000136{
137 int16_t res = (buf[1] << 8) | buf[0];
138
139 return res;
140}
141
Dave Chapman2cc80f52007-07-29 21:19:14 +0000142static inline void short2le(unsigned short val, unsigned char* addr)
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000143{
144 addr[0] = val & 0xFF;
145 addr[1] = (val >> 8) & 0xff;
146}
147
Dave Chapman2cc80f52007-07-29 21:19:14 +0000148static inline void int2le(unsigned int val, unsigned char* addr)
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000149{
150 addr[0] = val & 0xFF;
151 addr[1] = (val >> 8) & 0xff;
152 addr[2] = (val >> 16) & 0xff;
153 addr[3] = (val >> 24) & 0xff;
154}
155
Dave Chapman2cc80f52007-07-29 21:19:14 +0000156static inline void int2be(unsigned int val, unsigned char* addr)
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000157{
158 addr[0] = (val >> 24) & 0xff;
159 addr[1] = (val >> 16) & 0xff;
160 addr[2] = (val >> 8) & 0xff;
161 addr[3] = val & 0xFF;
162}
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000163
164
165#define BYTES2INT32(array,pos)\
166 ((long)array[pos] | ((long)array[pos+1] << 8 ) |\
Dave Chapman45895df2006-12-13 08:57:06 +0000167 ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 ))
168
Dave Chapman31aa4522007-02-04 11:42:11 +0000169int read_partinfo(struct ipod_t* ipod, int silent)
Dave Chapman45895df2006-12-13 08:57:06 +0000170{
171 int i;
Dave Chapman45895df2006-12-13 08:57:06 +0000172 unsigned long count;
173
Dominik Riebelingd131a312008-06-17 17:52:13 +0000174 count = ipod_read(ipod,ipod_sectorbuf, ipod->sector_size);
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000175
176 if (count <= 0) {
177 print_error(" Error reading from disk: ");
Dave Chapman45895df2006-12-13 08:57:06 +0000178 return -1;
179 }
180
Dave Chapman35735c662007-07-27 20:51:36 +0000181 memset(ipod->pinfo, 0, sizeof(ipod->pinfo));
182
Dominik Riebelingd131a312008-06-17 17:52:13 +0000183 if ((ipod_sectorbuf[510] == 0x55) && (ipod_sectorbuf[511] == 0xaa)) {
Dave Chapman6e641e82007-02-05 18:29:39 +0000184 /* DOS partition table */
Dave Chapman6e641e82007-02-05 18:29:39 +0000185 ipod->macpod = 0;
186 /* parse partitions */
187 for ( i = 0; i < 4; i++ ) {
Dominik Riebelingd131a312008-06-17 17:52:13 +0000188 unsigned char* ptr = ipod_sectorbuf + 0x1be + 16*i;
Dave Chapman6e641e82007-02-05 18:29:39 +0000189 ipod->pinfo[i].type = ptr[4];
190 ipod->pinfo[i].start = BYTES2INT32(ptr, 8);
191 ipod->pinfo[i].size = BYTES2INT32(ptr, 12);
192
193 /* extended? */
194 if ( ipod->pinfo[i].type == 5 ) {
195 /* not handled yet */
196 }
197 }
Dominik Riebelingd131a312008-06-17 17:52:13 +0000198 } else if ((ipod_sectorbuf[0] == 'E') && (ipod_sectorbuf[1] == 'R')) {
Dave Chapman6e641e82007-02-05 18:29:39 +0000199 /* Apple Partition Map */
200
201 /* APM parsing code based on the check_mac_partitions() function in
202 ipodloader2 - written by Thomas Tempelmann and released
203 under the GPL. */
204
205 int blkNo = 1;
206 int partBlkCount = 1;
Dominik Riebelingd131a312008-06-17 17:52:13 +0000207 int partBlkSizMul = ipod_sectorbuf[2] / 2;
Dave Chapman6e641e82007-02-05 18:29:39 +0000208
209 int pmMapBlkCnt; /* # of blks in partition map */
210 int pmPyPartStart; /* physical start blk of partition */
211 int pmPartBlkCnt; /* # of blks in this partition */
212 int i = 0;
213
214 ipod->macpod = 1;
215
216 memset(ipod->pinfo,0,sizeof(ipod->pinfo));
217
218 while (blkNo <= partBlkCount) {
219 if (ipod_seek(ipod, blkNo * partBlkSizMul * 512) < 0) {
220 fprintf(stderr,"[ERR] Seek failed whilst reading APM\n");
221 return -1;
222 }
223
Dominik Riebelingd131a312008-06-17 17:52:13 +0000224 count = ipod_read(ipod, ipod_sectorbuf, ipod->sector_size);
Dave Chapman6e641e82007-02-05 18:29:39 +0000225
226 if (count <= 0) {
227 print_error(" Error reading from disk: ");
228 return -1;
229 }
230
231 /* see if it's a partition entry */
Dominik Riebelingd131a312008-06-17 17:52:13 +0000232 if ((ipod_sectorbuf[0] != 'P') || (ipod_sectorbuf[1] != 'M')) {
Dave Chapman6e641e82007-02-05 18:29:39 +0000233 /* end of partition table -> leave the loop */
234 break;
235 }
236
237 /* Extract the interesting entries */
Dominik Riebelingd131a312008-06-17 17:52:13 +0000238 pmMapBlkCnt = be2int(ipod_sectorbuf + 4);
239 pmPyPartStart = be2int(ipod_sectorbuf + 8);
240 pmPartBlkCnt = be2int(ipod_sectorbuf + 12);
Dave Chapman6e641e82007-02-05 18:29:39 +0000241
242 /* update the number of part map blocks */
243 partBlkCount = pmMapBlkCnt;
244
Dominik Riebelingd131a312008-06-17 17:52:13 +0000245 if (strncmp((char*)(ipod_sectorbuf + 48), "Apple_MDFW", 32)==0) {
Dave Chapman6e641e82007-02-05 18:29:39 +0000246 /* A Firmware partition */
247 ipod->pinfo[i].start = pmPyPartStart;
248 ipod->pinfo[i].size = pmPartBlkCnt;
249 ipod->pinfo[i].type = 0;
250 i++;
Dominik Riebelingd131a312008-06-17 17:52:13 +0000251 } else if (strncmp((char*)(ipod_sectorbuf + 48), "Apple_HFS", 32)==0) {
Dave Chapman6e641e82007-02-05 18:29:39 +0000252 /* A HFS partition */
253 ipod->pinfo[i].start = pmPyPartStart;
254 ipod->pinfo[i].size = pmPartBlkCnt;
Dave Chapman2cc80f52007-07-29 21:19:14 +0000255 ipod->pinfo[i].type = PARTTYPE_HFS;
Dave Chapman6e641e82007-02-05 18:29:39 +0000256 i++;
257 }
258
259 blkNo++; /* read next partition map entry */
260 }
261 } else {
Dave Chapman57b84b62006-12-17 23:00:15 +0000262 if (!silent) fprintf(stderr,"[ERR] Bad boot sector signature\n");
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000263 return -1;
Dave Chapman45895df2006-12-13 08:57:06 +0000264 }
265
Dave Chapman4df0c772007-05-22 19:07:45 +0000266 /* Check that the partition table looks like an ipod:
267 1) Partition 1 is of type 0 (Empty) but isn't empty.
Dave Chapman0fba85f2007-06-10 22:47:51 +0000268 2) Partition 2 is of type 0xb or 0xc (winpod) or -1 (macpod)
Dave Chapman4df0c772007-05-22 19:07:45 +0000269 */
270 if ((ipod->pinfo[0].type != 0) || (ipod->pinfo[0].size == 0) ||
Dave Chapman0fba85f2007-06-10 22:47:51 +0000271 ((ipod->pinfo[1].type != 0xb) && (ipod->pinfo[1].type != 0xc) &&
Dave Chapman2cc80f52007-07-29 21:19:14 +0000272 (ipod->pinfo[1].type != PARTTYPE_HFS))) {
Dave Chapman4df0c772007-05-22 19:07:45 +0000273 if (!silent) fprintf(stderr,"[ERR] Partition layout is not an ipod\n");
274 return -1;
275 }
276
Dave Chapman31aa4522007-02-04 11:42:11 +0000277 ipod->start = ipod->pinfo[0].start*ipod->sector_size;
Dave Chapman45895df2006-12-13 08:57:06 +0000278 return 0;
279}
280
Dave Chapman31aa4522007-02-04 11:42:11 +0000281int read_partition(struct ipod_t* ipod, int outfile)
Dave Chapman45895df2006-12-13 08:57:06 +0000282{
283 int res;
Dave Chapman2cc80f52007-07-29 21:19:14 +0000284 ssize_t n;
Dave Chapman45895df2006-12-13 08:57:06 +0000285 int bytesleft;
286 int chunksize;
Dave Chapman31aa4522007-02-04 11:42:11 +0000287 int count = ipod->pinfo[0].size;
Dave Chapman45895df2006-12-13 08:57:06 +0000288
Dave Chapman31aa4522007-02-04 11:42:11 +0000289 if (ipod_seek(ipod, ipod->start) < 0) {
Dave Chapman45895df2006-12-13 08:57:06 +0000290 return -1;
291 }
292
Dave Chapman31aa4522007-02-04 11:42:11 +0000293 fprintf(stderr,"[INFO] Writing %d sectors to output file\n",count);
Dave Chapman45895df2006-12-13 08:57:06 +0000294
Dave Chapman31aa4522007-02-04 11:42:11 +0000295 bytesleft = count * ipod->sector_size;
Dave Chapman45895df2006-12-13 08:57:06 +0000296 while (bytesleft > 0) {
297 if (bytesleft > BUFFER_SIZE) {
298 chunksize = BUFFER_SIZE;
299 } else {
300 chunksize = bytesleft;
301 }
302
Dominik Riebelingd131a312008-06-17 17:52:13 +0000303 n = ipod_read(ipod, ipod_sectorbuf, chunksize);
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000304
305 if (n < 0) {
Dave Chapman45895df2006-12-13 08:57:06 +0000306 return -1;
307 }
308
309 if (n < chunksize) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000310 fprintf(stderr,
Dave Chapman2cc80f52007-07-29 21:19:14 +0000311 "[ERR] Short read in disk_read() - requested %d, got %d\n",
Dave Chapman0be30362007-07-29 21:25:09 +0000312 chunksize,(int)n);
Dave Chapman45895df2006-12-13 08:57:06 +0000313 return -1;
314 }
315
316 bytesleft -= n;
317
Dominik Riebelingd131a312008-06-17 17:52:13 +0000318 res = write(outfile,ipod_sectorbuf,n);
Dave Chapman45895df2006-12-13 08:57:06 +0000319
320 if (res < 0) {
321 perror("[ERR] write in disk_read");
322 return -1;
323 }
324
325 if (res != n) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000326 fprintf(stderr,
Dave Chapman0be30362007-07-29 21:25:09 +0000327 "Short write - requested %d, received %d - aborting.\n",(int)n,res);
Dave Chapman45895df2006-12-13 08:57:06 +0000328 return -1;
329 }
330 }
331
332 fprintf(stderr,"[INFO] Done.\n");
333 return 0;
334}
335
Dave Chapman31aa4522007-02-04 11:42:11 +0000336int write_partition(struct ipod_t* ipod, int infile)
Dave Chapman45895df2006-12-13 08:57:06 +0000337{
Dave Chapman2cc80f52007-07-29 21:19:14 +0000338 ssize_t res;
Dave Chapman45895df2006-12-13 08:57:06 +0000339 int n;
340 int bytesread;
341 int byteswritten = 0;
342 int eof;
343 int padding = 0;
344
Dave Chapman31aa4522007-02-04 11:42:11 +0000345 if (ipod_seek(ipod, ipod->start) < 0) {
Dave Chapman45895df2006-12-13 08:57:06 +0000346 return -1;
347 }
348
349 fprintf(stderr,"[INFO] Writing input file to device\n");
350 bytesread = 0;
351 eof = 0;
352 while (!eof) {
Dominik Riebelingd131a312008-06-17 17:52:13 +0000353 n = read(infile,ipod_sectorbuf,BUFFER_SIZE);
Dave Chapman45895df2006-12-13 08:57:06 +0000354
355 if (n < 0) {
356 perror("[ERR] read in disk_write");
357 return -1;
358 }
359
360 if (n < BUFFER_SIZE) {
361 eof = 1;
362 /* We need to pad the last write to a multiple of SECTOR_SIZE */
Dave Chapman31aa4522007-02-04 11:42:11 +0000363 if ((n % ipod->sector_size) != 0) {
364 padding = (ipod->sector_size-(n % ipod->sector_size));
Dave Chapman45895df2006-12-13 08:57:06 +0000365 n += padding;
366 }
367 }
368
369 bytesread += n;
370
Dominik Riebelingd131a312008-06-17 17:52:13 +0000371 res = ipod_write(ipod, ipod_sectorbuf, n);
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000372
373 if (res < 0) {
374 print_error(" Error writing to disk: ");
Dave Chapman45895df2006-12-13 08:57:06 +0000375 fprintf(stderr,"Bytes written: %d\n",byteswritten);
376 return -1;
377 }
378
379 if (res != n) {
Dave Chapman0be30362007-07-29 21:25:09 +0000380 fprintf(stderr,"[ERR] Short write - requested %d, received %d - aborting.\n",n,(int)res);
Dave Chapman45895df2006-12-13 08:57:06 +0000381 return -1;
382 }
383
384 byteswritten += res;
385 }
386
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000387 fprintf(stderr,"[INFO] Wrote %d bytes plus %d bytes padding.\n",
388 byteswritten-padding,padding);
Dave Chapman45895df2006-12-13 08:57:06 +0000389 return 0;
390}
391
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000392char* ftypename[] = { "OSOS", "RSRC", "AUPD", "HIBE" };
393
Dave Chapman31aa4522007-02-04 11:42:11 +0000394int diskmove(struct ipod_t* ipod, 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;
Dave Chapman75a11122006-12-14 18:41:03 +0000398 int bytesleft;
399 int chunksize;
Dave Chapman75a11122006-12-14 18:41:03 +0000400 int n;
401
Dave Chapman6e797152007-09-08 23:27:49 +0000402 src_start = ipod->ipod_directory[1].devOffset;
Dave Chapman31aa4522007-02-04 11:42:11 +0000403 src_end = (ipod->ipod_directory[ipod->nimages-1].devOffset + ipod->sector_size +
404 ipod->ipod_directory[ipod->nimages-1].len +
405 (ipod->sector_size-1)) & ~(ipod->sector_size-1);
Dave Chapman75a11122006-12-14 18:41:03 +0000406 bytesleft = src_end - src_start;
Dave Chapman75a11122006-12-14 18:41:03 +0000407
Dominik Riebelingd131a312008-06-17 17:52:13 +0000408 if (ipod_verbose) {
Dave Chapman31aa4522007-02-04 11:42:11 +0000409 fprintf(stderr,"[INFO] Need to move images 2-%d forward %08x bytes\n", ipod->nimages,delta);
Dave Chapman75a11122006-12-14 18:41:03 +0000410 fprintf(stderr,"[VERB] src_start = %08x\n",src_start);
411 fprintf(stderr,"[VERB] src_end = %08x\n",src_end);
Dave Chapman408c65b2006-12-15 09:55:21 +0000412 fprintf(stderr,"[VERB] dest_start = %08x\n",src_start+delta);
413 fprintf(stderr,"[VERB] dest_end = %08x\n",src_end+delta);
Dave Chapman75a11122006-12-14 18:41:03 +0000414 fprintf(stderr,"[VERB] bytes to copy = %08x\n",bytesleft);
415 }
416
417 while (bytesleft > 0) {
418 if (bytesleft <= BUFFER_SIZE) {
419 chunksize = bytesleft;
420 } else {
421 chunksize = BUFFER_SIZE;
422 }
423
Dominik Riebelingd131a312008-06-17 17:52:13 +0000424 if (ipod_verbose) {
Dave Chapman408c65b2006-12-15 09:55:21 +0000425 fprintf(stderr,"[VERB] Copying %08x bytes from %08x to %08x (absolute %08x to %08x)\n",
Dave Chapman75a11122006-12-14 18:41:03 +0000426 chunksize,
Dave Chapman408c65b2006-12-15 09:55:21 +0000427 src_end-chunksize,
428 src_end-chunksize+delta,
Dave Chapman31aa4522007-02-04 11:42:11 +0000429 (unsigned int)(ipod->start+src_end-chunksize),
430 (unsigned int)(ipod->start+src_end-chunksize+delta));
Dave Chapman75a11122006-12-14 18:41:03 +0000431 }
432
433
Dave Chapman31aa4522007-02-04 11:42:11 +0000434 if (ipod_seek(ipod, ipod->start+src_end-chunksize) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000435 fprintf(stderr,"[ERR] Seek failed\n");
436 return -1;
437 }
438
Dominik Riebelingd131a312008-06-17 17:52:13 +0000439 if ((n = ipod_read(ipod,ipod_sectorbuf,chunksize)) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000440 perror("[ERR] Write failed\n");
441 return -1;
442 }
443
444 if (n < chunksize) {
445 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n",
Dave Chapman3b1119b2007-07-29 20:43:42 +0000446 chunksize,n);
Dave Chapman75a11122006-12-14 18:41:03 +0000447 return -1;
448 }
449
Dave Chapman31aa4522007-02-04 11:42:11 +0000450 if (ipod_seek(ipod, ipod->start+src_end-chunksize+delta) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000451 fprintf(stderr,"[ERR] Seek failed\n");
452 return -1;
453 }
454
Dominik Riebelingd131a312008-06-17 17:52:13 +0000455 if ((n = ipod_write(ipod,ipod_sectorbuf,chunksize)) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000456 perror("[ERR] Write failed\n");
457 return -1;
458 }
459
460 if (n < chunksize) {
461 fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n"
Dave Chapman3b1119b2007-07-29 20:43:42 +0000462 ,chunksize,n);
Dave Chapman75a11122006-12-14 18:41:03 +0000463 return -1;
464 }
465
Dave Chapman408c65b2006-12-15 09:55:21 +0000466 src_end -= chunksize;
Dave Chapman75a11122006-12-14 18:41:03 +0000467 bytesleft -= chunksize;
468 }
469
470 return 0;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000471}
472
Dave Chapman31aa4522007-02-04 11:42:11 +0000473int add_bootloader(struct ipod_t* ipod, char* filename, int type)
Dave Chapman75a11122006-12-14 18:41:03 +0000474{
475 int length;
476 int i;
Dave Chapmand7729772006-12-15 15:52:08 +0000477 int x;
Dave Chapman75a11122006-12-14 18:41:03 +0000478 int n;
479 int infile;
480 int paddedlength;
481 int entryOffset;
482 int delta = 0;
483 unsigned long chksum=0;
484 unsigned long filechksum=0;
485 unsigned char header[8]; /* Header for .ipod file */
Dave Chapmaneca52222007-02-08 21:31:38 +0000486 unsigned char* bootloader_buf;
Dave Chapman75a11122006-12-14 18:41:03 +0000487
Dave Chapmanbdc27ff2007-02-08 18:05:50 +0000488 /* Calculate the position in the OSOS image where our bootloader will go. */
Dave Chapman31aa4522007-02-04 11:42:11 +0000489 if (ipod->ipod_directory[0].entryOffset>0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000490 /* Keep the same entryOffset */
Dave Chapman31aa4522007-02-04 11:42:11 +0000491 entryOffset = ipod->ipod_directory[0].entryOffset;
Dave Chapman75a11122006-12-14 18:41:03 +0000492 } else {
Dave Chapman31aa4522007-02-04 11:42:11 +0000493 entryOffset = (ipod->ipod_directory[0].len+ipod->sector_size-1)&~(ipod->sector_size-1);
Dave Chapman75a11122006-12-14 18:41:03 +0000494 }
495
Dave Chapmanbdc27ff2007-02-08 18:05:50 +0000496#ifdef WITH_BOOTOBJS
497 if (type == FILETYPE_INTERNAL) {
498 fprintf(stderr,"[INFO] Using internal bootloader - %d bytes\n",ipod->bootloader_len);
Dominik Riebelingd131a312008-06-17 17:52:13 +0000499 memcpy(ipod_sectorbuf+entryOffset,ipod->bootloader,ipod->bootloader_len);
Dave Chapmanbdc27ff2007-02-08 18:05:50 +0000500 length = ipod->bootloader_len;
501 paddedlength=(ipod->bootloader_len+ipod->sector_size-1)&~(ipod->sector_size-1);
502 }
503 else
504#endif
505 {
506 infile=open(filename,O_RDONLY);
507 if (infile < 0) {
508 fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename);
509 return -1;
510 }
511
512 if (type==FILETYPE_DOT_IPOD) {
513 /* First check that the input file is the correct type for this ipod. */
514 n = read(infile,header,8);
515 if (n < 8) {
516 fprintf(stderr,"[ERR] Failed to read header from %s\n",filename);
517 close(infile);
518 return -1;
519 }
520
521 if (memcmp(header+4, ipod->modelname,4)!=0) {
522 fprintf(stderr,"[ERR] Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n",
523 header[4],header[5],header[6],header[7], ipod->modelname);
524 close(infile);
525 return -1;
526 }
527
528 filechksum = be2int(header);
529
530 length=filesize(infile)-8;
531 } else {
532 length=filesize(infile);
533 }
534 paddedlength=(length+ipod->sector_size-1)&~(ipod->sector_size-1);
Dave Chapmaneca52222007-02-08 21:31:38 +0000535
536 bootloader_buf = malloc(length);
537 if (bootloader_buf == NULL) {
Dave Chapman6e314e52007-06-08 15:24:47 +0000538 fprintf(stderr,"[ERR] Can not allocate memory for bootloader\n");
Dave Chapman09b247f2007-06-08 15:30:14 +0000539 return -1;
Dave Chapmaneca52222007-02-08 21:31:38 +0000540 }
Dave Chapmanbdc27ff2007-02-08 18:05:50 +0000541 /* Now read our bootloader - we need to check it before modifying the partition*/
Dave Chapmaneca52222007-02-08 21:31:38 +0000542 n = read(infile,bootloader_buf,length);
Dave Chapmanbdc27ff2007-02-08 18:05:50 +0000543 close(infile);
544
545 if (n < 0) {
546 fprintf(stderr,"[ERR] Couldn't read input file\n");
547 return -1;
548 }
549
550 if (type==FILETYPE_DOT_IPOD) {
551 /* Calculate and confirm bootloader checksum */
552 chksum = ipod->modelnum;
Dave Chapmaneca52222007-02-08 21:31:38 +0000553 for (i = 0; i < length; i++) {
Dave Chapmanbdc27ff2007-02-08 18:05:50 +0000554 /* add 8 unsigned bits but keep a 32 bit sum */
Dave Chapmaneca52222007-02-08 21:31:38 +0000555 chksum += bootloader_buf[i];
Dave Chapmanbdc27ff2007-02-08 18:05:50 +0000556 }
557
558 if (chksum == filechksum) {
559 fprintf(stderr,"[INFO] Checksum OK in %s\n",filename);
560 } else {
561 fprintf(stderr,"[ERR] Checksum in %s failed check\n",filename);
562 return -1;
563 }
564 }
565 }
566
Dave Chapman75a11122006-12-14 18:41:03 +0000567 if (entryOffset+paddedlength > BUFFER_SIZE) {
568 fprintf(stderr,"[ERR] Input file too big for buffer\n");
Dave Chapman75a11122006-12-14 18:41:03 +0000569 return -1;
570 }
571
Dominik Riebelingd131a312008-06-17 17:52:13 +0000572 if (ipod_verbose) {
Dave Chapman31aa4522007-02-04 11:42:11 +0000573 fprintf(stderr,"[VERB] Original firmware begins at 0x%08x\n", ipod->ipod_directory[0].devOffset + ipod->sector_size);
Dave Chapman75a11122006-12-14 18:41:03 +0000574 fprintf(stderr,"[VERB] New entryOffset will be 0x%08x\n",entryOffset);
575 fprintf(stderr,"[VERB] End of bootloader will be at 0x%08x\n",entryOffset+paddedlength);
576 }
577
578 /* Check if we have enough space */
579 /* TODO: Check the size of the partition. */
Dave Chapman31aa4522007-02-04 11:42:11 +0000580 if (ipod->nimages > 1) {
Dave Chapmanbdc27ff2007-02-08 18:05:50 +0000581 if ((ipod->ipod_directory[0].devOffset+entryOffset+paddedlength) >
Dave Chapman33e15152007-02-04 13:04:23 +0000582 ipod->ipod_directory[1].devOffset) {
Dave Chapman75a11122006-12-14 18:41:03 +0000583 fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n");
Dave Chapman31aa4522007-02-04 11:42:11 +0000584 delta = ipod->ipod_directory[0].devOffset + entryOffset+paddedlength
Dave Chapman6e797152007-09-08 23:27:49 +0000585 - ipod->ipod_directory[1].devOffset + ipod->sector_size;
Dave Chapman75a11122006-12-14 18:41:03 +0000586
Dave Chapman31aa4522007-02-04 11:42:11 +0000587 if (diskmove(ipod, delta) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000588 fprintf(stderr,"[ERR] Image movement failed.\n");
589 return -1;
590 }
591 }
592 }
593
594
595 /* We have moved the partitions, now we can write our bootloader */
596
Dominik Riebelingd131a312008-06-17 17:52:13 +0000597 /* Firstly read the original firmware into ipod_sectorbuf */
Dave Chapman75a11122006-12-14 18:41:03 +0000598 fprintf(stderr,"[INFO] Reading original firmware...\n");
Dave Chapman31aa4522007-02-04 11:42:11 +0000599 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000600 fprintf(stderr,"[ERR] Seek failed\n");
601 return -1;
602 }
603
Dominik Riebelingd131a312008-06-17 17:52:13 +0000604 if ((n = ipod_read(ipod,ipod_sectorbuf,entryOffset)) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000605 perror("[ERR] Read failed\n");
606 return -1;
607 }
608
609 if (n < entryOffset) {
610 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n"
Dave Chapman3b1119b2007-07-29 20:43:42 +0000611 ,entryOffset,n);
Dave Chapman75a11122006-12-14 18:41:03 +0000612 return -1;
613 }
614
Dave Chapmaneca52222007-02-08 21:31:38 +0000615#ifdef WITH_BOOTOBJS
616 if (type == FILETYPE_INTERNAL) {
Dominik Riebelingd131a312008-06-17 17:52:13 +0000617 memcpy(ipod_sectorbuf+entryOffset,ipod->bootloader,ipod->bootloader_len);
Dave Chapmaneca52222007-02-08 21:31:38 +0000618 }
619 else
620#endif
621 {
Dominik Riebelingd131a312008-06-17 17:52:13 +0000622 memcpy(ipod_sectorbuf+entryOffset,bootloader_buf,length);
Dave Chapmaneca52222007-02-08 21:31:38 +0000623 free(bootloader_buf);
624 }
625
Dave Chapman75a11122006-12-14 18:41:03 +0000626 /* Calculate new checksum for combined image */
627 chksum = 0;
628 for (i=0;i<entryOffset + length; i++) {
Dominik Riebelingd131a312008-06-17 17:52:13 +0000629 chksum += ipod_sectorbuf[i];
Dave Chapman75a11122006-12-14 18:41:03 +0000630 }
631
632 /* Now write the combined firmware image to the disk */
633
Dave Chapman31aa4522007-02-04 11:42:11 +0000634 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000635 fprintf(stderr,"[ERR] Seek failed\n");
636 return -1;
637 }
638
Dominik Riebelingd131a312008-06-17 17:52:13 +0000639 if ((n = ipod_write(ipod,ipod_sectorbuf,entryOffset+paddedlength)) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000640 perror("[ERR] Write failed\n");
641 return -1;
642 }
643
644 if (n < (entryOffset+paddedlength)) {
645 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n"
Dave Chapman3b1119b2007-07-29 20:43:42 +0000646 ,entryOffset+paddedlength,n);
Dave Chapman75a11122006-12-14 18:41:03 +0000647 return -1;
648 }
649
650 fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",entryOffset+paddedlength);
651
Dave Chapman31aa4522007-02-04 11:42:11 +0000652 x = ipod->diroffset % ipod->sector_size;
Dave Chapman75a11122006-12-14 18:41:03 +0000653
654 /* Read directory */
Dave Chapman31aa4522007-02-04 11:42:11 +0000655 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) {
656 fprintf(stderr,"[ERR] Seek failed\n");
657 return -1;
658 }
Dave Chapman75a11122006-12-14 18:41:03 +0000659
Dominik Riebelingd131a312008-06-17 17:52:13 +0000660 n=ipod_read(ipod, ipod_sectorbuf, ipod->sector_size);
Dave Chapman31aa4522007-02-04 11:42:11 +0000661 if (n < 0) {
662 fprintf(stderr,"[ERR] Directory read failed\n");
663 return -1;
664 }
Dave Chapman75a11122006-12-14 18:41:03 +0000665
666 /* Update entries for image 0 */
Dominik Riebelingd131a312008-06-17 17:52:13 +0000667 int2le(entryOffset+length,ipod_sectorbuf+x+16);
668 int2le(entryOffset,ipod_sectorbuf+x+24);
669 int2le(chksum,ipod_sectorbuf+x+28);
670 int2le(0xffffffff,ipod_sectorbuf+x+36); /* loadAddr */
Dave Chapman75a11122006-12-14 18:41:03 +0000671
672 /* Update devOffset entries for other images, if we have moved them */
673 if (delta > 0) {
Dave Chapman31aa4522007-02-04 11:42:11 +0000674 for (i=1;i<ipod->nimages;i++) {
Dominik Riebelingd131a312008-06-17 17:52:13 +0000675 int2le(le2int(ipod_sectorbuf+x+i*40+12)+delta,ipod_sectorbuf+x+i*40+12);
Dave Chapman75a11122006-12-14 18:41:03 +0000676 }
677 }
678
679 /* Write directory */
Dave Chapman31aa4522007-02-04 11:42:11 +0000680 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) {
Dave Chapmanc500b962007-02-04 23:41:47 +0000681 fprintf(stderr,"[ERR] Seek to %d failed\n", (int)(ipod->start+ipod->diroffset-x));
Dave Chapman31aa4522007-02-04 11:42:11 +0000682 return -1;
683 }
Dominik Riebelingd131a312008-06-17 17:52:13 +0000684 n=ipod_write(ipod, ipod_sectorbuf, ipod->sector_size);
Dave Chapman31aa4522007-02-04 11:42:11 +0000685 if (n < 0) {
686 fprintf(stderr,"[ERR] Directory write failed\n");
687 return -1;
688 }
Dave Chapman75a11122006-12-14 18:41:03 +0000689
690 return 0;
691}
692
Dave Chapman31aa4522007-02-04 11:42:11 +0000693int delete_bootloader(struct ipod_t* ipod)
Dave Chapman75a11122006-12-14 18:41:03 +0000694{
695 int length;
696 int i;
Dave Chapmand7729772006-12-15 15:52:08 +0000697 int x;
Dave Chapman75a11122006-12-14 18:41:03 +0000698 int n;
699 unsigned long chksum=0; /* 32 bit checksum - Rockbox .ipod style*/
700
701 /* Removing the bootloader involves adjusting the "length",
702 "chksum" and "entryOffset" values in the osos image's directory
703 entry. */
704
705 /* Firstly check we have a bootloader... */
706
Dave Chapman31aa4522007-02-04 11:42:11 +0000707 if (ipod->ipod_directory[0].entryOffset == 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000708 fprintf(stderr,"[ERR] No bootloader found.\n");
709 return -1;
710 }
711
Dave Chapman31aa4522007-02-04 11:42:11 +0000712 length = ipod->ipod_directory[0].entryOffset;
Dave Chapman75a11122006-12-14 18:41:03 +0000713
714 /* Read the firmware so we can calculate the checksum */
715 fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length);
716
Dave Chapman31aa4522007-02-04 11:42:11 +0000717 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000718 return -1;
719 }
720
Dave Chapman31aa4522007-02-04 11:42:11 +0000721 i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1);
Dave Chapman75a11122006-12-14 18:41:03 +0000722 fprintf(stderr,"[INFO] Padding read from 0x%08x to 0x%08x bytes\n",
723 length,i);
724
Dominik Riebelingd131a312008-06-17 17:52:13 +0000725 if ((n = ipod_read(ipod,ipod_sectorbuf,i)) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000726 return -1;
727 }
728
729 if (n < i) {
730 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n",
731 i,n);
732 return -1;
733 }
734
735 chksum = 0;
736 for (i = 0; i < length; i++) {
737 /* add 8 unsigned bits but keep a 32 bit sum */
Dominik Riebelingd131a312008-06-17 17:52:13 +0000738 chksum += ipod_sectorbuf[i];
Dave Chapman75a11122006-12-14 18:41:03 +0000739 }
740
741 /* Now write back the updated directory entry */
742
743 fprintf(stderr,"[INFO] Updating firmware checksum\n");
744
Dave Chapman31aa4522007-02-04 11:42:11 +0000745 x = ipod->diroffset % ipod->sector_size;
Dave Chapmand7729772006-12-15 15:52:08 +0000746
Dave Chapman75a11122006-12-14 18:41:03 +0000747 /* Read directory */
Dave Chapman31aa4522007-02-04 11:42:11 +0000748 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
Dave Chapman75a11122006-12-14 18:41:03 +0000749
Dominik Riebelingd131a312008-06-17 17:52:13 +0000750 n=ipod_read(ipod, ipod_sectorbuf, ipod->sector_size);
Dave Chapman75a11122006-12-14 18:41:03 +0000751 if (n < 0) { return -1; }
752
753 /* Update entries for image 0 */
Dominik Riebelingd131a312008-06-17 17:52:13 +0000754 int2le(length,ipod_sectorbuf+x+16);
755 int2le(0,ipod_sectorbuf+x+24);
756 int2le(chksum,ipod_sectorbuf+x+28);
Dave Chapman75a11122006-12-14 18:41:03 +0000757
758 /* Write directory */
Dave Chapman31aa4522007-02-04 11:42:11 +0000759 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
Dominik Riebelingd131a312008-06-17 17:52:13 +0000760 n=ipod_write(ipod, ipod_sectorbuf, ipod->sector_size);
Dave Chapman75a11122006-12-14 18:41:03 +0000761 if (n < 0) { return -1; }
762
763 return 0;
764}
765
Dave Chapman31aa4522007-02-04 11:42:11 +0000766int write_firmware(struct ipod_t* ipod, char* filename, int type)
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000767{
768 int length;
769 int i;
Dave Chapmand7729772006-12-15 15:52:08 +0000770 int x;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000771 int n;
772 int infile;
773 int newsize;
774 int bytesavailable;
775 unsigned long chksum=0;
776 unsigned long filechksum=0;
777 unsigned char header[8]; /* Header for .ipod file */
778
Dave Chapmanbbde4452007-06-02 10:10:31 +0000779#ifdef WITH_BOOTOBJS
780 if (type == FILETYPE_INTERNAL) {
781 fprintf(stderr,"[INFO] Using internal bootloader - %d bytes\n",ipod->bootloader_len);
782 length = ipod->bootloader_len;
783 infile = -1;
784 }
785 else
786#endif
787 {
788 /* First check that the input file is the correct type for this ipod. */
789 infile=open(filename,O_RDONLY);
790 if (infile < 0) {
791 fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename);
Dave Chapman31aa4522007-02-04 11:42:11 +0000792 return -1;
793 }
Dave Chapmanbbde4452007-06-02 10:10:31 +0000794
795 if (type==FILETYPE_DOT_IPOD) {
796 n = read(infile,header,8);
797 if (n < 8) {
798 fprintf(stderr,"[ERR] Failed to read header from %s\n",filename);
799 close(infile);
800 return -1;
801 }
802
803 if (memcmp(header+4, ipod->modelname,4)!=0) {
804 fprintf(stderr,"[ERR] Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n",
805 header[4],header[5],header[6],header[7], ipod->modelname);
806 close(infile);
807 return -1;
808 }
809
810 filechksum = be2int(header);
811
812 length = filesize(infile)-8;
813 } else {
814 length = filesize(infile);
Dave Chapman31aa4522007-02-04 11:42:11 +0000815 }
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000816 }
Dave Chapmanbbde4452007-06-02 10:10:31 +0000817
Dave Chapman31aa4522007-02-04 11:42:11 +0000818 newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000819
820 fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n",
821 length,newsize);
822
823 if (newsize > BUFFER_SIZE) {
824 fprintf(stderr,"[ERR] Input file too big for buffer\n");
Dave Chapmanbbde4452007-06-02 10:10:31 +0000825 if (infile >= 0) close(infile);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000826 return -1;
827 }
828
829 /* Check if we have enough space */
830 /* TODO: Check the size of the partition. */
Dave Chapman31aa4522007-02-04 11:42:11 +0000831 if (ipod->nimages > 1) {
832 bytesavailable=ipod->ipod_directory[1].devOffset-ipod->ipod_directory[0].devOffset;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000833 if (bytesavailable < newsize) {
834 fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n");
835
836 /* TODO: Implement image movement */
837 fprintf(stderr,"[ERR] Image movement not yet implemented.\n");
838 close(infile);
839 return -1;
840 }
841 }
842
Dave Chapmanbbde4452007-06-02 10:10:31 +0000843#ifdef WITH_BOOTOBJS
844 if (type == FILETYPE_INTERNAL) {
Dominik Riebelingd131a312008-06-17 17:52:13 +0000845 memcpy(ipod_sectorbuf,ipod->bootloader,ipod->bootloader_len);
Dave Chapmanbbde4452007-06-02 10:10:31 +0000846 }
847 else
848#endif
849 {
850 fprintf(stderr,"[INFO] Reading input file...\n");
851 /* We now know we have enough space, so write it. */
Dominik Riebelingd131a312008-06-17 17:52:13 +0000852 n = read(infile,ipod_sectorbuf,length);
Dave Chapmanbbde4452007-06-02 10:10:31 +0000853 if (n < 0) {
854 fprintf(stderr,"[ERR] Couldn't read input file\n");
855 close(infile);
856 return -1;
857 }
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000858 close(infile);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000859 }
Dave Chapmanbbde4452007-06-02 10:10:31 +0000860
861 /* Pad the data with zeros */
Dominik Riebelingd131a312008-06-17 17:52:13 +0000862 memset(ipod_sectorbuf+length,0,newsize-length);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000863
Dave Chapmanbdc27ff2007-02-08 18:05:50 +0000864 if (type==FILETYPE_DOT_IPOD) {
Dave Chapman31aa4522007-02-04 11:42:11 +0000865 chksum = ipod->modelnum;
866 for (i = 0; i < length; i++) {
867 /* add 8 unsigned bits but keep a 32 bit sum */
Dominik Riebelingd131a312008-06-17 17:52:13 +0000868 chksum += ipod_sectorbuf[i];
Dave Chapman31aa4522007-02-04 11:42:11 +0000869 }
870
871 if (chksum == filechksum) {
872 fprintf(stderr,"[INFO] Checksum OK in %s\n",filename);
873 } else {
874 fprintf(stderr,"[ERR] Checksum in %s failed check\n",filename);
875 return -1;
876 }
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000877 }
878
Dave Chapman31aa4522007-02-04 11:42:11 +0000879 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000880 fprintf(stderr,"[ERR] Seek failed\n");
881 return -1;
882 }
883
Dominik Riebelingd131a312008-06-17 17:52:13 +0000884 if ((n = ipod_write(ipod,ipod_sectorbuf,newsize)) < 0) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000885 perror("[ERR] Write failed\n");
886 return -1;
887 }
888
889 if (n < newsize) {
890 fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n"
Dave Chapman3b1119b2007-07-29 20:43:42 +0000891 ,newsize,n);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000892 return -1;
893 }
894 fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",n);
895
896 /* Now we need to update the "len", "entryOffset" and "chksum" fields */
897 chksum = 0;
898 for (i = 0; i < length; i++) {
899 /* add 8 unsigned bits but keep a 32 bit sum */
Dominik Riebelingd131a312008-06-17 17:52:13 +0000900 chksum += ipod_sectorbuf[i];
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000901 }
902
Dave Chapman31aa4522007-02-04 11:42:11 +0000903 x = ipod->diroffset % ipod->sector_size;
Dave Chapmand7729772006-12-15 15:52:08 +0000904
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000905 /* Read directory */
Dave Chapman31aa4522007-02-04 11:42:11 +0000906 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000907
Dominik Riebelingd131a312008-06-17 17:52:13 +0000908 n=ipod_read(ipod, ipod_sectorbuf, ipod->sector_size);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000909 if (n < 0) { return -1; }
910
911 /* Update entries for image 0 */
Dominik Riebelingd131a312008-06-17 17:52:13 +0000912 int2le(length,ipod_sectorbuf+x+16);
913 int2le(0,ipod_sectorbuf+x+24);
914 int2le(chksum,ipod_sectorbuf+x+28);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000915
916 /* Write directory */
Dave Chapman31aa4522007-02-04 11:42:11 +0000917 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
Dominik Riebelingd131a312008-06-17 17:52:13 +0000918 n=ipod_write(ipod, ipod_sectorbuf, ipod->sector_size);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000919 if (n < 0) { return -1; }
920
921 return 0;
922}
923
Dave Chapman427fff42007-04-13 23:28:20 +0000924int read_firmware(struct ipod_t* ipod, char* filename, int type)
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000925{
926 int length;
927 int i;
928 int outfile;
929 int n;
930 unsigned long chksum=0; /* 32 bit checksum - Rockbox .ipod style*/
931 unsigned char header[8]; /* Header for .ipod file */
932
Dave Chapman31aa4522007-02-04 11:42:11 +0000933 if (ipod->ipod_directory[0].entryOffset != 0) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000934 /* We have a bootloader... */
Dave Chapman31aa4522007-02-04 11:42:11 +0000935 length = ipod->ipod_directory[0].entryOffset;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000936 } else {
Dave Chapman31aa4522007-02-04 11:42:11 +0000937 length = ipod->ipod_directory[0].len;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000938 }
939
940 fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length);
941
Dave Chapman31aa4522007-02-04 11:42:11 +0000942 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000943 return -1;
944 }
945
Dave Chapman31aa4522007-02-04 11:42:11 +0000946 i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000947 fprintf(stderr,"[INFO] Padding read from 0x%08x to 0x%08x bytes\n",
948 length,i);
949
Dominik Riebelingd131a312008-06-17 17:52:13 +0000950 if ((n = ipod_read(ipod,ipod_sectorbuf,i)) < 0) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000951 return -1;
952 }
953
954 if (n < i) {
955 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n",
956 i,n);
957 return -1;
958 }
959
Dave Chapmancd067552006-12-14 10:16:10 +0000960 outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000961 if (outfile < 0) {
962 fprintf(stderr,"[ERR] Couldn't open file %s\n",filename);
963 return -1;
964 }
965
Dave Chapman427fff42007-04-13 23:28:20 +0000966 if (type == FILETYPE_DOT_IPOD) {
967 chksum = ipod->modelnum;
968 for (i = 0; i < length; i++) {
969 /* add 8 unsigned bits but keep a 32 bit sum */
Dominik Riebelingd131a312008-06-17 17:52:13 +0000970 chksum += ipod_sectorbuf[i];
Dave Chapman427fff42007-04-13 23:28:20 +0000971 }
972
973 int2be(chksum,header);
974 memcpy(header+4, ipod->modelname,4);
975
Dave Chapman33bc6f32007-07-29 21:34:28 +0000976 n = write(outfile,header,8);
977 if (n != 8) {
978 fprintf(stderr,"[ERR] Write error - %d\n",n);
979 }
Dave Chapman427fff42007-04-13 23:28:20 +0000980 }
981
Dominik Riebelingd131a312008-06-17 17:52:13 +0000982 n = write(outfile,ipod_sectorbuf,length);
Dave Chapman33bc6f32007-07-29 21:34:28 +0000983 if (n != length) {
984 fprintf(stderr,"[ERR] Write error - %d\n",n);
985 }
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000986 close(outfile);
987
988 return 0;
989}
990
Dave Chapman31aa4522007-02-04 11:42:11 +0000991int read_directory(struct ipod_t* ipod)
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000992{
993 int n;
Dave Chapmand7729772006-12-15 15:52:08 +0000994 int x;
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000995 unsigned char* p;
Dave Chapman31aa4522007-02-04 11:42:11 +0000996 unsigned short version;
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000997
Dave Chapman1026c0f2007-02-06 21:00:56 +0000998 ipod->nimages=0;
999
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001000 /* Read firmware partition header (first 512 bytes of disk - but
1001 let's read a whole sector) */
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001002
Dave Chapman31aa4522007-02-04 11:42:11 +00001003 if (ipod_seek(ipod, ipod->start) < 0) {
1004 fprintf(stderr,"[ERR] Seek to 0x%08x in read_directory() failed.\n",
1005 (unsigned int)(ipod->start));
Dave Chapmand7729772006-12-15 15:52:08 +00001006 return -1;
1007 }
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001008
Dominik Riebelingd131a312008-06-17 17:52:13 +00001009 n=ipod_read(ipod, ipod_sectorbuf, ipod->sector_size);
Dave Chapmand7729772006-12-15 15:52:08 +00001010 if (n < 0) {
Dave Chapman31aa4522007-02-04 11:42:11 +00001011 fprintf(stderr,"[ERR] ipod_read(ipod,buf,0x%08x) failed in read_directory()\n", ipod->sector_size);
Dave Chapmand7729772006-12-15 15:52:08 +00001012 return -1;
1013 }
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001014
Dominik Riebelingd131a312008-06-17 17:52:13 +00001015 if (memcmp(ipod_sectorbuf,apple_stop_sign,sizeof(apple_stop_sign))!=0) {
Dave Chapman1026c0f2007-02-06 21:00:56 +00001016 fprintf(stderr,"[ERR] Firmware partition doesn't contain Apple copyright, aborting.\n");
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001017 return -1;
1018 }
1019
Dominik Riebelingd131a312008-06-17 17:52:13 +00001020 if (memcmp(ipod_sectorbuf+0x100,"]ih[",4)!=0) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001021 fprintf(stderr,"[ERR] Bad firmware directory\n");
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001022 return -1;
1023 }
1024
Dominik Riebelingd131a312008-06-17 17:52:13 +00001025 version = le2ushort(ipod_sectorbuf+0x10a);
Dave Chapman31aa4522007-02-04 11:42:11 +00001026 if ((version != 2) && (version != 3)) {
1027 fprintf(stderr,"[ERR] Unknown firmware format version %04x\n",
1028 version);
1029 }
Dominik Riebelingd131a312008-06-17 17:52:13 +00001030 ipod->diroffset=le2int(ipod_sectorbuf+0x104) + 0x200;
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001031
Dave Chapmand7729772006-12-15 15:52:08 +00001032 /* diroffset may not be sector-aligned */
Dave Chapman31aa4522007-02-04 11:42:11 +00001033 x = ipod->diroffset % ipod->sector_size;
Dave Chapmand7729772006-12-15 15:52:08 +00001034
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001035 /* Read directory */
Dave Chapman31aa4522007-02-04 11:42:11 +00001036 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) {
1037 fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset);
Dave Chapmand7729772006-12-15 15:52:08 +00001038 return -1;
1039 }
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001040
Dominik Riebelingd131a312008-06-17 17:52:13 +00001041 n=ipod_read(ipod, ipod_sectorbuf, ipod->sector_size);
Dave Chapmand7729772006-12-15 15:52:08 +00001042 if (n < 0) {
1043 fprintf(stderr,"[ERR] Read of directory failed.\n");
1044 return -1;
1045 }
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001046
Dominik Riebelingd131a312008-06-17 17:52:13 +00001047 p = ipod_sectorbuf + x;
Dave Chapman564d2492007-08-03 02:12:32 +00001048
1049 /* A hack to detect 2nd gen Nanos - maybe there is a better way? */
1050 if (p[0] == 0)
1051 {
Dominik Riebelingd131a312008-06-17 17:52:13 +00001052 n=ipod_read(ipod, ipod_sectorbuf, ipod->sector_size);
Dave Chapman564d2492007-08-03 02:12:32 +00001053 if (n < 0) {
1054 fprintf(stderr,"[ERR] Read of directory failed.\n");
1055 return -1;
1056 }
Dominik Riebelingd131a312008-06-17 17:52:13 +00001057 p = ipod_sectorbuf;
Dave Chapman564d2492007-08-03 02:12:32 +00001058 }
1059
Dominik Riebelingd131a312008-06-17 17:52:13 +00001060 while ((ipod->nimages < MAX_IMAGES) && (p < (ipod_sectorbuf + x + 400)) &&
Dave Chapman564d2492007-08-03 02:12:32 +00001061 ((memcmp(p,"!ATA",4)==0) || (memcmp(p,"DNAN",4)==0))) {
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001062 p+=4;
1063 if (memcmp(p,"soso",4)==0) {
Dave Chapman31aa4522007-02-04 11:42:11 +00001064 ipod->ipod_directory[ipod->nimages].ftype=FTYPE_OSOS;
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001065 } else if (memcmp(p,"crsr",4)==0) {
Dave Chapman31aa4522007-02-04 11:42:11 +00001066 ipod->ipod_directory[ipod->nimages].ftype=FTYPE_RSRC;
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001067 } else if (memcmp(p,"dpua",4)==0) {
Dave Chapman31aa4522007-02-04 11:42:11 +00001068 ipod->ipod_directory[ipod->nimages].ftype=FTYPE_AUPD;
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001069 } else if (memcmp(p,"ebih",4)==0) {
Dave Chapman31aa4522007-02-04 11:42:11 +00001070 ipod->ipod_directory[ipod->nimages].ftype=FTYPE_HIBE;
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001071 } else {
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001072 fprintf(stderr,"[ERR] Unknown image type %c%c%c%c\n",
1073 p[0],p[1],p[2],p[3]);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001074 }
1075 p+=4;
Dave Chapman31aa4522007-02-04 11:42:11 +00001076 ipod->ipod_directory[ipod->nimages].id=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001077 p+=4;
Dave Chapman31aa4522007-02-04 11:42:11 +00001078 ipod->ipod_directory[ipod->nimages].devOffset=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001079 p+=4;
Dave Chapman31aa4522007-02-04 11:42:11 +00001080 ipod->ipod_directory[ipod->nimages].len=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001081 p+=4;
Dave Chapman31aa4522007-02-04 11:42:11 +00001082 ipod->ipod_directory[ipod->nimages].addr=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001083 p+=4;
Dave Chapman31aa4522007-02-04 11:42:11 +00001084 ipod->ipod_directory[ipod->nimages].entryOffset=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001085 p+=4;
Dave Chapman31aa4522007-02-04 11:42:11 +00001086 ipod->ipod_directory[ipod->nimages].chksum=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001087 p+=4;
Dave Chapman31aa4522007-02-04 11:42:11 +00001088 ipod->ipod_directory[ipod->nimages].vers=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001089 p+=4;
Dave Chapman31aa4522007-02-04 11:42:11 +00001090 ipod->ipod_directory[ipod->nimages].loadAddr=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001091 p+=4;
Dave Chapman31aa4522007-02-04 11:42:11 +00001092 ipod->nimages++;
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001093 }
Dave Chapman31aa4522007-02-04 11:42:11 +00001094
1095 if ((ipod->nimages > 1) && (version==2)) {
1096 /* The 3g firmware image doesn't appear to have a version, so
1097 let's make one up... Note that this is never written back to the
1098 ipod, so it's OK to do. */
1099
1100 if (ipod->ipod_directory[0].vers == 0) { ipod->ipod_directory[0].vers = 3; }
1101
1102 ipod->fwoffset = ipod->start;
1103 } else {
1104 ipod->fwoffset = ipod->start + ipod->sector_size;
1105 }
1106
1107 return 0;
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001108}
1109
Dave Chapman31aa4522007-02-04 11:42:11 +00001110int list_images(struct ipod_t* ipod)
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001111{
1112 int i;
1113
Dominik Riebelingd131a312008-06-17 17:52:13 +00001114 if (ipod_verbose) {
Dave Chapman75a11122006-12-14 18:41:03 +00001115 printf(" Type id devOffset len addr entryOffset chksum vers loadAddr devOffset+len\n");
Dave Chapman31aa4522007-02-04 11:42:11 +00001116 for (i = 0 ; i < ipod->nimages; i++) {
Dave Chapman75a11122006-12-14 18:41:03 +00001117 printf("%d - %s 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",i,
Dave Chapman31aa4522007-02-04 11:42:11 +00001118 ftypename[ipod->ipod_directory[i].ftype],
1119 ipod->ipod_directory[i].id,
1120 ipod->ipod_directory[i].devOffset,
1121 ipod->ipod_directory[i].len,
1122 ipod->ipod_directory[i].addr,
1123 ipod->ipod_directory[i].entryOffset,
1124 ipod->ipod_directory[i].chksum,
1125 ipod->ipod_directory[i].vers,
1126 ipod->ipod_directory[i].loadAddr,
1127 ipod->ipod_directory[i].devOffset+((ipod->ipod_directory[i].len+ipod->sector_size-1)&~(ipod->sector_size-1)));
Dave Chapman75a11122006-12-14 18:41:03 +00001128 }
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001129 }
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001130
1131 printf("\n");
1132 printf("Listing firmware partition contents:\n");
1133 printf("\n");
1134
Dave Chapman31aa4522007-02-04 11:42:11 +00001135 for (i = 0 ; i < ipod->nimages; i++) {
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001136 printf("Image %d:\n",i+1);
Dave Chapman31aa4522007-02-04 11:42:11 +00001137 switch(ipod->ipod_directory[i].ftype) {
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001138 case FTYPE_OSOS:
Dave Chapman31aa4522007-02-04 11:42:11 +00001139 if (ipod->ipod_directory[i].entryOffset==0) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001140 printf(" Main firmware - %d bytes\n",
Dave Chapman31aa4522007-02-04 11:42:11 +00001141 ipod->ipod_directory[i].len);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001142 } else {
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001143 printf(" Main firmware - %d bytes\n",
Dave Chapman31aa4522007-02-04 11:42:11 +00001144 ipod->ipod_directory[i].entryOffset);
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001145 printf(" Third-party bootloader - %d bytes\n",
Dave Chapman31aa4522007-02-04 11:42:11 +00001146 ipod->ipod_directory[i].len-ipod->ipod_directory[i].entryOffset);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001147 }
1148 break;
1149 default:
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001150 printf(" %s - %d bytes\n",
Dave Chapman31aa4522007-02-04 11:42:11 +00001151 ftypename[ipod->ipod_directory[i].ftype],
1152 ipod->ipod_directory[i].len);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001153 }
1154 }
1155 printf("\n");
1156
1157 return 0;
1158}
1159
Dave Chapman31aa4522007-02-04 11:42:11 +00001160int getmodel(struct ipod_t* ipod, int ipod_version)
Dave Chapman57b84b62006-12-17 23:00:15 +00001161{
1162 switch (ipod_version) {
Dave Chapman6a20def2007-07-26 20:21:11 +00001163 case 0x01:
1164 ipod->modelstr="1st or 2nd Generation";
1165 ipod->modelnum = 19;
1166 ipod->modelname = "1g2g";
1167 ipod->targetname = "ipod1g2g";
1168#ifdef WITH_BOOTOBJS
1169 ipod->bootloader = ipod1g2g;
1170 ipod->bootloader_len = LEN_ipod1g2g;
1171#endif
1172 break;
Dave Chapman31aa4522007-02-04 11:42:11 +00001173 case 0x02:
1174 ipod->modelstr="3rd Generation";
1175 ipod->modelnum = 7;
1176 ipod->modelname = "ip3g";
Dave Chapmana6d68bd2007-02-10 20:09:23 +00001177 ipod->targetname = "ipod3g";
Dave Chapmanbdc27ff2007-02-08 18:05:50 +00001178#ifdef WITH_BOOTOBJS
1179 ipod->bootloader = ipod3g;
1180 ipod->bootloader_len = LEN_ipod3g;
1181#endif
Dave Chapman57b84b62006-12-17 23:00:15 +00001182 break;
Dave Chapman31aa4522007-02-04 11:42:11 +00001183 case 0x40:
1184 ipod->modelstr="1st Generation Mini";
1185 ipod->modelnum = 9;
1186 ipod->modelname = "mini";
Dominik Wengerac3db332007-08-24 08:10:19 +00001187 ipod->targetname = "ipodmini1g";
Dave Chapmanbdc27ff2007-02-08 18:05:50 +00001188#ifdef WITH_BOOTOBJS
1189 ipod->bootloader = ipodmini;
1190 ipod->bootloader_len = LEN_ipodmini;
1191#endif
Dave Chapman57b84b62006-12-17 23:00:15 +00001192 break;
Dave Chapman31aa4522007-02-04 11:42:11 +00001193 case 0x50:
1194 ipod->modelstr="4th Generation";
1195 ipod->modelnum = 8;
1196 ipod->modelname = "ip4g";
Dominik Wengerac3db332007-08-24 08:10:19 +00001197 ipod->targetname = "ipod4gray";
Dave Chapmanbdc27ff2007-02-08 18:05:50 +00001198#ifdef WITH_BOOTOBJS
1199 ipod->bootloader = ipod4g;
1200 ipod->bootloader_len = LEN_ipod4g;
1201#endif
Dave Chapman57b84b62006-12-17 23:00:15 +00001202 break;
Dave Chapman31aa4522007-02-04 11:42:11 +00001203 case 0x60:
1204 ipod->modelstr="Photo/Color";
1205 ipod->modelnum = 3;
1206 ipod->modelname = "ipco";
Dave Chapmana6d68bd2007-02-10 20:09:23 +00001207 ipod->targetname = "ipodcolor";
Dave Chapmanbdc27ff2007-02-08 18:05:50 +00001208#ifdef WITH_BOOTOBJS
1209 ipod->bootloader = ipodcolor;
1210 ipod->bootloader_len = LEN_ipodcolor;
1211#endif
Dave Chapman57b84b62006-12-17 23:00:15 +00001212 break;
Dave Chapman31aa4522007-02-04 11:42:11 +00001213 case 0x70:
1214 ipod->modelstr="2nd Generation Mini";
1215 ipod->modelnum = 11;
1216 ipod->modelname = "mn2g";
Dave Chapmana6d68bd2007-02-10 20:09:23 +00001217 ipod->targetname = "ipodmini2g";
Dave Chapmanbdc27ff2007-02-08 18:05:50 +00001218#ifdef WITH_BOOTOBJS
1219 ipod->bootloader = ipodmini2g;
1220 ipod->bootloader_len = LEN_ipodmini2g;
1221#endif
Dave Chapman57b84b62006-12-17 23:00:15 +00001222 break;
Dave Chapman31aa4522007-02-04 11:42:11 +00001223 case 0xc0:
1224 ipod->modelstr="1st Generation Nano";
1225 ipod->modelnum = 4;
1226 ipod->modelname = "nano";
Dave Chapmana6d68bd2007-02-10 20:09:23 +00001227 ipod->targetname = "ipodnano";
Dave Chapmanbdc27ff2007-02-08 18:05:50 +00001228#ifdef WITH_BOOTOBJS
1229 ipod->bootloader = ipodnano;
1230 ipod->bootloader_len = LEN_ipodnano;
1231#endif
Dave Chapman57b84b62006-12-17 23:00:15 +00001232 break;
Dave Chapman31aa4522007-02-04 11:42:11 +00001233 case 0xb0:
1234 ipod->modelstr="Video (aka 5th Generation)";
1235 ipod->modelnum = 5;
1236 ipod->modelname = "ipvd";
Dave Chapmana6d68bd2007-02-10 20:09:23 +00001237 ipod->targetname = "ipodvideo";
Dave Chapmanbdc27ff2007-02-08 18:05:50 +00001238#ifdef WITH_BOOTOBJS
1239 ipod->bootloader = ipodvideo;
1240 ipod->bootloader_len = LEN_ipodvideo;
1241#endif
Dave Chapman57b84b62006-12-17 23:00:15 +00001242 break;
Dave Chapman564d2492007-08-03 02:12:32 +00001243 case 0x100:
1244 ipod->modelstr="2nd Generation Nano";
1245 ipod->modelnum = 0;
1246 ipod->targetname = NULL;
1247#ifdef WITH_BOOTOBJS
1248 ipod->bootloader = NULL;
1249 ipod->bootloader_len = 0;
1250#endif
1251 break;
Dave Chapman57b84b62006-12-17 23:00:15 +00001252 default:
Dave Chapman31aa4522007-02-04 11:42:11 +00001253 ipod->modelname = NULL;
1254 ipod->modelnum = 0;
Dave Chapmana6d68bd2007-02-10 20:09:23 +00001255 ipod->targetname = NULL;
Dave Chapmanbdc27ff2007-02-08 18:05:50 +00001256#ifdef WITH_BOOTOBJS
1257 ipod->bootloader = NULL;
1258 ipod->bootloader_len = 0;
1259#endif
Dave Chapman57b84b62006-12-17 23:00:15 +00001260 return -1;
1261 }
1262 return 0;
1263}
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001264
Dominik Riebeling194b2ca2008-05-04 11:59:04 +00001265/* returns number of found ipods or -1 if no ipods found and permission
1266 * for raw disc access was denied. */
Dave Chapman31aa4522007-02-04 11:42:11 +00001267int ipod_scan(struct ipod_t* ipod)
Dave Chapman57b84b62006-12-17 23:00:15 +00001268{
1269 int i;
1270 int n = 0;
Dave Chapman57b84b62006-12-17 23:00:15 +00001271 int ipod_version;
Dave Chapman1026c0f2007-02-06 21:00:56 +00001272 char last_ipod[4096];
Dominik Riebeling194b2ca2008-05-04 11:59:04 +00001273 int denied = 0;
1274 int result;
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001275
Dave Chapman57b84b62006-12-17 23:00:15 +00001276 printf("[INFO] Scanning disk devices...\n");
1277
Dave Chapman6e641e82007-02-05 18:29:39 +00001278 for (i = 0; i <= 25 ; i++) {
Dave Chapman57b84b62006-12-17 23:00:15 +00001279#ifdef __WIN32__
Dave Chapman31aa4522007-02-04 11:42:11 +00001280 sprintf(ipod->diskname,"\\\\.\\PhysicalDrive%d",i);
Dave Chapman57b84b62006-12-17 23:00:15 +00001281#elif defined(linux) || defined (__linux)
Dave Chapman31aa4522007-02-04 11:42:11 +00001282 sprintf(ipod->diskname,"/dev/sd%c",'a'+i);
Dave Chapman57b84b62006-12-17 23:00:15 +00001283#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \
1284 || defined(__bsdi__) || defined(__DragonFly__)
Dave Chapman31aa4522007-02-04 11:42:11 +00001285 sprintf(ipod->diskname,"/dev/da%d",i);
Dave Chapman57b84b62006-12-17 23:00:15 +00001286#elif defined(__APPLE__) && defined(__MACH__)
Dave Chapman31aa4522007-02-04 11:42:11 +00001287 sprintf(ipod->diskname,"/dev/disk%d",i);
Dave Chapman57b84b62006-12-17 23:00:15 +00001288#else
1289 #error No disk paths defined for this platform
1290#endif
Dominik Riebeling194b2ca2008-05-04 11:59:04 +00001291 if ((result = ipod_open(ipod, 1)) < 0) {
1292 if(result == -2) {
1293 denied++;
1294 }
Dave Chapman57b84b62006-12-17 23:00:15 +00001295 continue;
1296 }
1297
Dave Chapman31aa4522007-02-04 11:42:11 +00001298 if (read_partinfo(ipod,1) < 0) {
Dave Chapman57b84b62006-12-17 23:00:15 +00001299 continue;
1300 }
1301
Dave Chapman31aa4522007-02-04 11:42:11 +00001302 if ((ipod->pinfo[0].start==0) || (ipod->pinfo[0].type != 0)) {
Dave Chapman57b84b62006-12-17 23:00:15 +00001303 continue;
1304 }
1305
Dave Chapman31aa4522007-02-04 11:42:11 +00001306 if (read_directory(ipod) < 0) {
Dave Chapman57b84b62006-12-17 23:00:15 +00001307 continue;
1308 }
1309
Dave Chapman31aa4522007-02-04 11:42:11 +00001310 ipod_version=(ipod->ipod_directory[0].vers>>8);
1311 if (getmodel(ipod,ipod_version) < 0) {
Dave Chapman57b84b62006-12-17 23:00:15 +00001312 continue;
1313 }
1314
1315#ifdef __WIN32__
Dave Chapman6e641e82007-02-05 18:29:39 +00001316 printf("[INFO] Ipod found - %s (\"%s\") - disk device %d\n",
1317 ipod->modelstr,ipod->macpod ? "macpod" : "winpod",i);
Dave Chapman57b84b62006-12-17 23:00:15 +00001318#else
Dave Chapman6e641e82007-02-05 18:29:39 +00001319 printf("[INFO] Ipod found - %s (\"%s\") - %s\n",
1320 ipod->modelstr,ipod->macpod ? "macpod" : "winpod",ipod->diskname);
Dave Chapman57b84b62006-12-17 23:00:15 +00001321#endif
1322 n++;
Dave Chapman1026c0f2007-02-06 21:00:56 +00001323 strcpy(last_ipod,ipod->diskname);
Dave Chapmandd50c862007-02-06 22:48:30 +00001324 ipod_close(ipod);
Dave Chapman57b84b62006-12-17 23:00:15 +00001325 }
1326
Dave Chapmaneca52222007-02-08 21:31:38 +00001327 if (n==1) {
Dave Chapman1026c0f2007-02-06 21:00:56 +00001328 /* Remember the disk name */
1329 strcpy(ipod->diskname,last_ipod);
Dave Chapman57b84b62006-12-17 23:00:15 +00001330 }
Dominik Riebeling194b2ca2008-05-04 11:59:04 +00001331 else if(n == 0 && denied) {
1332 printf("[ERR] FATAL: Permission denied on %d device(s) and no ipod detected.\n", denied);
1333#ifdef __WIN32__
1334 printf("[ERR] You need to run this program with administrator priviledges!\n");
1335#else
1336 printf("[ERR] You need permissions for raw disc access for this program to work!\n");
1337#endif
1338 }
1339 return (n == 0 && denied) ? -1 : n;
Dave Chapman57b84b62006-12-17 23:00:15 +00001340}
Dave Chapman35735c662007-07-27 20:51:36 +00001341
1342static void put_int32le(uint32_t x, unsigned char* p)
1343{
1344 p[0] = x & 0xff;
1345 p[1] = (x >> 8) & 0xff;
1346 p[2] = (x >> 16) & 0xff;
1347 p[3] = (x >> 24) & 0xff;
1348}
1349
1350int write_dos_partition_table(struct ipod_t* ipod)
1351{
1352 unsigned char* p;
1353 int i, n;
1354 uint32_t type;
1355
1356 /* Only support 512-byte sectors at the moment */
1357 if ( ipod->sector_size != 512 )
1358 {
1359 fprintf(stderr,"[ERR] Only ipods with 512 bytes per sector are supported.\n");
1360 return -1;
1361 }
1362
1363 /* Firstly zero the entire MBR */
Dominik Riebelingd131a312008-06-17 17:52:13 +00001364 memset(ipod_sectorbuf, 0, ipod->sector_size);
Dave Chapman35735c662007-07-27 20:51:36 +00001365
1366 /* Now add the partition info */
1367 for (i=0; i < 4 ; i++)
1368 {
Dominik Riebelingd131a312008-06-17 17:52:13 +00001369 p = ipod_sectorbuf + 0x1be + i*16;
Dave Chapman35735c662007-07-27 20:51:36 +00001370
1371 /* Ensure first partition is type 0, and second is 0xb */
1372 if (i==0) { type = 0; }
1373 else if (i==1) { type = 0xb; }
1374 else { type = ipod->pinfo[i].type; }
1375
1376 put_int32le(type, p + 4);
1377 put_int32le(ipod->pinfo[i].start, p + 8);
1378 put_int32le(ipod->pinfo[i].size, p + 12);
1379 }
1380
1381 /* Finally add the magic */
Dominik Riebelingd131a312008-06-17 17:52:13 +00001382 ipod_sectorbuf[0x1fe] = 0x55;
1383 ipod_sectorbuf[0x1ff] = 0xaa;
Dave Chapman35735c662007-07-27 20:51:36 +00001384
1385 if (ipod_seek(ipod, 0) < 0) {
1386 fprintf(stderr,"[ERR] Seek failed writing MBR\n");
1387 return -1;
1388 }
1389
1390 /* Write MBR */
Dominik Riebelingd131a312008-06-17 17:52:13 +00001391 if ((n = ipod_write(ipod, ipod_sectorbuf, ipod->sector_size)) < 0) {
Dave Chapman35735c662007-07-27 20:51:36 +00001392 perror("[ERR] Write failed\n");
1393 return -1;
1394 }
1395
1396 return 0;
1397}
Dave Chapman6e797152007-09-08 23:27:49 +00001398
1399#ifndef RBUTIL
1400
1401static inline uint32_t getuint32le(unsigned char* buf)
1402{
1403 int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
1404
1405 return res;
1406}
1407
1408/* testMarker and GetSecurityBlockKey based on code from BadBlocks and
1409 Kingstone, posted at http://ipodlinux.org/Flash_Decryption
1410
1411*/
1412
1413static bool testMarker(int marker)
1414{
1415 int mask, decrypt, temp1, temp2;
1416
1417 mask = (marker&0xff)|((marker&0xff)<<8)|((marker&0xff)<<16)|((marker&0xff)<<24);
1418 decrypt = marker ^ mask;
1419 temp1=(int)((unsigned int)decrypt>>24);
1420 temp2=decrypt<<8;
1421
1422 if (temp1==0)
1423 return false;
1424
1425 temp2=(int)((unsigned int)temp2>>24);
1426 decrypt=decrypt<<16;
1427 decrypt=(int)((unsigned int)decrypt>>24);
1428
1429 if ((temp1 < temp2) && (temp2 < decrypt))
1430 {
1431 temp1 = temp1 & 0xf;
1432 temp2 = temp2 & 0xf;
1433 decrypt = decrypt & 0xf;
1434
1435 if ((temp1 > temp2) && (temp2 > decrypt) && (decrypt != 0))
1436 {
1437 return true;
1438 }
1439 }
1440 return false;
1441}
1442
1443static int GetSecurityBlockKey(unsigned char *data, unsigned char* this_key)
1444{
1445 int constant = 0x54c3a298;
1446 int key=0;
1447 int nkeys = 0;
1448 int aMarker=0;
1449 int pos=0;
1450 int c, count;
1451 int temp1;
1452 static const int offset[8]={0x5,0x25,0x6f,0x69,0x15,0x4d,0x40,0x34};
1453
1454 for (c = 0; c < 8; c++)
1455 {
1456 pos = offset[c]*4;
1457 aMarker = getuint32le(data + pos);
1458
1459 if (testMarker(aMarker))
1460 {
1461 if (c<7)
1462 pos =(offset[c+1]*4)+4;
1463 else
1464 pos =(offset[0]*4)+4;
1465
1466 key=0;
1467
1468 temp1=aMarker;
1469
1470 for (count=0;count<2;count++){
1471 int word = getuint32le(data + pos);
1472 temp1 = aMarker;
1473 temp1 = temp1^word;
1474 temp1 = temp1^constant;
1475 key = temp1;
1476 pos = pos+4;
1477 }
1478 int r1=0x6f;
1479 int r2=0;
1480 int r12;
1481 int r14;
1482 unsigned int r_tmp;
1483
1484 for (count=2;count<128;count=count+2){
1485 r2=getuint32le(data+count*4);
1486 r12=getuint32le(data+(count*4)+4);
1487 r_tmp=(unsigned int)r12>>16;
1488 r14=r2 | ((int)r_tmp);
1489 r2=r2&0xffff;
1490 r2=r2 | r12;
1491 r1=r1^r14;
1492 r1=r1+r2;
1493 }
1494 key=key^r1;
1495
1496 // Invert key, little endian
1497 this_key[0] = key & 0xff;
1498 this_key[1] = (key >> 8) & 0xff;
1499 this_key[2] = (key >> 16) & 0xff;
1500 this_key[3] = (key >> 24) & 0xff;
1501 nkeys++;
1502 }
1503 }
1504 return nkeys;
1505}
1506
1507static int find_key(struct ipod_t* ipod, int aupd, unsigned char* key)
1508{
1509 int n;
1510
1511 /* Firstly read the security block and find the RC4 key. This is
1512 in the sector preceeding the AUPD image. */
1513
1514 fprintf(stderr, "[INFO] Reading security block at offset 0x%08x\n",ipod->ipod_directory[aupd].devOffset-ipod->sector_size);
1515 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset-ipod->sector_size) < 0) {
1516 return -1;
1517 }
1518
Dominik Riebelingd131a312008-06-17 17:52:13 +00001519 if ((n = ipod_read(ipod, ipod_sectorbuf, 512)) < 0) {
Dave Chapman6e797152007-09-08 23:27:49 +00001520 return -1;
1521 }
1522
Dominik Riebelingd131a312008-06-17 17:52:13 +00001523 n = GetSecurityBlockKey(ipod_sectorbuf, key);
Dave Chapman6e797152007-09-08 23:27:49 +00001524
1525 if (n != 1)
1526 {
1527 fprintf(stderr, "[ERR] %d keys found in security block, can not continue\n",n);
1528 return -1;
1529 }
1530
1531 return 0;
1532}
1533
1534int read_aupd(struct ipod_t* ipod, char* filename)
1535{
1536 int length;
1537 int i;
1538 int outfile;
1539 int n;
1540 int aupd;
1541 struct rc4_key_t rc4;
1542 unsigned char key[4];
1543 unsigned long chksum=0;
1544
1545 aupd = 0;
1546 while ((aupd < ipod->nimages) && (ipod->ipod_directory[aupd].ftype != FTYPE_AUPD))
1547 {
1548 aupd++;
1549 }
1550
1551 if (aupd == ipod->nimages)
1552 {
1553 fprintf(stderr,"[ERR] No AUPD image in firmware partition.\n");
1554 return -1;
1555 }
1556
1557 length = ipod->ipod_directory[aupd].len;
1558
1559 fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length);
1560
1561 if (find_key(ipod, aupd, key) < 0)
1562 {
1563 return -1;
1564 }
1565
1566 fprintf(stderr, "[INFO] Decrypting AUPD image with key %02x%02x%02x%02x\n",key[0],key[1],key[2],key[3]);
1567
1568 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset) < 0) {
1569 return -1;
1570 }
1571
1572 i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1);
1573
Dominik Riebelingd131a312008-06-17 17:52:13 +00001574 if ((n = ipod_read(ipod,ipod_sectorbuf,i)) < 0) {
Dave Chapman6e797152007-09-08 23:27:49 +00001575 return -1;
1576 }
1577
1578 if (n < i) {
1579 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n",
1580 i,n);
1581 return -1;
1582 }
1583
1584 /* Perform the decryption - this is standard (A)RC4 */
1585 matrixArc4Init(&rc4, key, 4);
Dominik Riebelingd131a312008-06-17 17:52:13 +00001586 matrixArc4(&rc4, ipod_sectorbuf, ipod_sectorbuf, length);
Dave Chapman6e797152007-09-08 23:27:49 +00001587
1588 chksum = 0;
1589 for (i = 0; i < (int)length; i++) {
1590 /* add 8 unsigned bits but keep a 32 bit sum */
Dominik Riebelingd131a312008-06-17 17:52:13 +00001591 chksum += ipod_sectorbuf[i];
Dave Chapman6e797152007-09-08 23:27:49 +00001592 }
1593
1594 if (chksum != ipod->ipod_directory[aupd].chksum)
1595 {
1596 fprintf(stderr,"[ERR] Decryption failed - checksum error\n");
1597 return -1;
1598 }
1599 fprintf(stderr,"[INFO] Decrypted OK (checksum matches header)\n");
1600
1601 outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666);
1602 if (outfile < 0) {
1603 fprintf(stderr,"[ERR] Couldn't open file %s\n",filename);
1604 return -1;
1605 }
1606
Dominik Riebelingd131a312008-06-17 17:52:13 +00001607 n = write(outfile,ipod_sectorbuf,length);
Dave Chapman6e797152007-09-08 23:27:49 +00001608 if (n != length) {
1609 fprintf(stderr,"[ERR] Write error - %d\n",n);
1610 }
1611 close(outfile);
1612
1613 return 0;
1614}
1615
1616int write_aupd(struct ipod_t* ipod, char* filename)
1617{
1618 unsigned int length;
1619 int i;
1620 int x;
1621 int n;
1622 int infile;
1623 int newsize;
1624 int aupd;
1625 unsigned long chksum=0;
1626 struct rc4_key_t rc4;
1627 unsigned char key[4];
1628
1629 /* First check that the input file is the correct type for this ipod. */
1630 infile=open(filename,O_RDONLY);
1631 if (infile < 0) {
1632 fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename);
1633 return -1;
1634 }
1635
1636 length = filesize(infile);
1637 newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1);
1638
1639 fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n",
1640 length,newsize);
1641
1642 if (newsize > BUFFER_SIZE) {
1643 fprintf(stderr,"[ERR] Input file too big for buffer\n");
1644 if (infile >= 0) close(infile);
1645 return -1;
1646 }
1647
1648 /* Find aupd image number */
1649 aupd = 0;
1650 while ((aupd < ipod->nimages) && (ipod->ipod_directory[aupd].ftype != FTYPE_AUPD))
1651 {
1652 aupd++;
1653 }
1654
1655 if (aupd == ipod->nimages)
1656 {
1657 fprintf(stderr,"[ERR] No AUPD image in firmware partition.\n");
1658 return -1;
1659 }
1660
1661 if (length != ipod->ipod_directory[aupd].len)
1662 {
1663 fprintf(stderr,"[ERR] AUPD image (%d bytes) differs in size to %s (%d bytes).\n",
1664 ipod->ipod_directory[aupd].len, filename, length);
1665 return -1;
1666 }
1667
1668 if (find_key(ipod, aupd, key) < 0)
1669 {
1670 return -1;
1671 }
1672
1673 fprintf(stderr, "[INFO] Encrypting AUPD image with key %02x%02x%02x%02x\n",key[0],key[1],key[2],key[3]);
1674
1675 /* We now know we have enough space, so write it. */
1676
1677 fprintf(stderr,"[INFO] Reading input file...\n");
Dominik Riebelingd131a312008-06-17 17:52:13 +00001678 n = read(infile,ipod_sectorbuf,length);
Dave Chapman6e797152007-09-08 23:27:49 +00001679 if (n < 0) {
1680 fprintf(stderr,"[ERR] Couldn't read input file\n");
1681 close(infile);
1682 return -1;
1683 }
1684 close(infile);
1685
1686 /* Pad the data with zeros */
Dominik Riebelingd131a312008-06-17 17:52:13 +00001687 memset(ipod_sectorbuf+length,0,newsize-length);
Dave Chapman6e797152007-09-08 23:27:49 +00001688
1689 /* Calculate the new checksum (before we encrypt) */
1690 chksum = 0;
1691 for (i = 0; i < (int)length; i++) {
1692 /* add 8 unsigned bits but keep a 32 bit sum */
Dominik Riebelingd131a312008-06-17 17:52:13 +00001693 chksum += ipod_sectorbuf[i];
Dave Chapman6e797152007-09-08 23:27:49 +00001694 }
1695
1696 /* Perform the encryption - this is standard (A)RC4 */
1697 matrixArc4Init(&rc4, key, 4);
Dominik Riebelingd131a312008-06-17 17:52:13 +00001698 matrixArc4(&rc4, ipod_sectorbuf, ipod_sectorbuf, length);
Dave Chapman6e797152007-09-08 23:27:49 +00001699
1700 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset) < 0) {
1701 fprintf(stderr,"[ERR] Seek failed\n");
1702 return -1;
1703 }
1704
Dominik Riebelingd131a312008-06-17 17:52:13 +00001705 if ((n = ipod_write(ipod,ipod_sectorbuf,newsize)) < 0) {
Dave Chapman6e797152007-09-08 23:27:49 +00001706 perror("[ERR] Write failed\n");
1707 return -1;
1708 }
1709
1710 if (n < newsize) {
1711 fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n"
1712 ,newsize,n);
1713 return -1;
1714 }
1715 fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",n);
1716
1717 x = ipod->diroffset % ipod->sector_size;
1718
1719 /* Read directory */
1720 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
1721
Dominik Riebelingd131a312008-06-17 17:52:13 +00001722 n=ipod_read(ipod, ipod_sectorbuf, ipod->sector_size);
Dave Chapman6e797152007-09-08 23:27:49 +00001723 if (n < 0) { return -1; }
1724
1725 /* Update checksum */
Dominik Riebelingd131a312008-06-17 17:52:13 +00001726 fprintf(stderr,"[INFO] Updating checksum to 0x%08x (was 0x%08x)\n",(unsigned int)chksum,le2int(ipod_sectorbuf + x + aupd*40 + 28));
1727 int2le(chksum,ipod_sectorbuf+x+aupd*40+28);
Dave Chapman6e797152007-09-08 23:27:49 +00001728
1729 /* Write directory */
1730 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
Dominik Riebelingd131a312008-06-17 17:52:13 +00001731 n=ipod_write(ipod, ipod_sectorbuf, ipod->sector_size);
Dave Chapman6e797152007-09-08 23:27:49 +00001732 if (n < 0) { return -1; }
1733
1734 return 0;
1735}
1736
1737#endif