mirror of
				https://github.com/Rockbox/rockbox.git
				synced 2025-10-26 23:36:37 -04:00 
			
		
		
		
	Allow building both as library and executable at the same time. Change-Id: Idc40354fdedaeace727043936352fc17232bf16e
		
			
				
	
	
		
			1995 lines
		
	
	
	
		
			58 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1995 lines
		
	
	
	
		
			58 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /***************************************************************************
 | |
|  *             __________               __   ___.
 | |
|  *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 | |
|  *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 | |
|  *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 | |
|  *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 | |
|  *                     \/            \/     \/    \/            \/
 | |
|  * $Id$
 | |
|  *
 | |
|  * Copyright (C) 2006-2007 Dave Chapman
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU General Public License
 | |
|  * as published by the Free Software Foundation; either version 2
 | |
|  * of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 | |
|  * KIND, either express or implied.
 | |
|  *
 | |
|  ****************************************************************************/
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <unistd.h>
 | |
| #include <fcntl.h>
 | |
| #include <string.h>
 | |
| #include <stdlib.h>
 | |
| #include <inttypes.h>
 | |
| #include <stdbool.h>
 | |
| #include <sys/types.h>
 | |
| #include <sys/stat.h>
 | |
| 
 | |
| #include "parttypes.h"
 | |
| #include "ipodio.h"
 | |
| #include "ipodpatcher.h"
 | |
| 
 | |
| #ifdef WITH_BOOTOBJS
 | |
| #include "ipod1g2g.h"
 | |
| #include "ipod3g.h"
 | |
| #include "ipod4g.h"
 | |
| #include "ipodmini1g.h"
 | |
| #include "ipodmini2g.h"
 | |
| #include "ipodcolor.h"
 | |
| #include "ipodnano1g.h"
 | |
| #include "ipodvideo.h"
 | |
| #include "ipodnano2g.h"
 | |
| #endif
 | |
| 
 | |
| int ipod_verbose = 0;
 | |
| 
 | |
| 
 | |
| /* The following string appears at the start of the firmware partition */
 | |
| static const char apple_stop_sign[] = "{{~~  /-----\\   "\
 | |
|                                       "{{~~ /       \\  "\
 | |
|                                       "{{~~|         | "\
 | |
|                                       "{{~~| S T O P | "\
 | |
|                                       "{{~~|         | "\
 | |
|                                       "{{~~ \\       /  "\
 | |
|                                       "{{~~  \\-----/   "\
 | |
|                                       "Copyright(C) 200"\
 | |
|                                       "1 Apple Computer"\
 | |
|                                       ", Inc.----------"\
 | |
|                                       "----------------"\
 | |
|                                       "----------------"\
 | |
|                                       "----------------"\
 | |
|                                       "----------------"\
 | |
|                                       "----------------"\
 | |
|                                       "---------------";
 | |
| 
 | |
| /* Windows requires the buffer for disk I/O to be aligned in memory on a 
 | |
|    multiple of the disk volume size - so we use a single global variable
 | |
|    and initialise it with ipod_alloc_buf() 
 | |
| */
 | |
| 
 | |
| char* get_parttype(unsigned int pt)
 | |
| {
 | |
|     int i;
 | |
|     static char unknown[]="Unknown";
 | |
| 
 | |
|     if (pt == PARTTYPE_HFS) {
 | |
|         return "HFS/HFS+";
 | |
|     }
 | |
| 
 | |
|     i=0;
 | |
|     while (parttypes[i].name != NULL) {
 | |
|         if (parttypes[i].type == pt) {
 | |
|             return (parttypes[i].name);
 | |
|         }
 | |
|         i++;
 | |
|     }
 | |
| 
 | |
|     return unknown;
 | |
| }
 | |
| 
 | |
| off_t filesize(int fd) {
 | |
|     struct stat buf;
 | |
| 
 | |
|     if (fstat(fd,&buf) < 0) {
 | |
|         perror("[ERR]  Checking filesize of input file");
 | |
|         return -1;
 | |
|     } else {
 | |
|         return(buf.st_size);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Partition table parsing code taken from Rockbox */
 | |
| 
 | |
| #define MAX_SECTOR_SIZE 2048
 | |
| #define SECTOR_SIZE 512
 | |
| 
 | |
| static inline unsigned short le2ushort(unsigned char* buf)
 | |
| {
 | |
|    unsigned short res = (buf[1] << 8) | buf[0];
 | |
| 
 | |
|    return res;
 | |
| }
 | |
| 
 | |
| static inline int le2int(unsigned char* buf)
 | |
| {
 | |
|    int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
 | |
| 
 | |
|    return res;
 | |
| }
 | |
| 
 | |
| static inline int be2int(unsigned char* buf)
 | |
| {
 | |
|    int32_t res = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
 | |
| 
 | |
|    return res;
 | |
| }
 | |
| 
 | |
| static inline int getint16le(char* buf)
 | |
| {
 | |
|    int16_t res = (buf[1] << 8) | buf[0];
 | |
| 
 | |
|    return res;
 | |
| }
 | |
| 
 | |
| static inline void short2le(unsigned short val, unsigned char* addr)
 | |
| {
 | |
|     addr[0] = val & 0xFF;
 | |
|     addr[1] = (val >> 8) & 0xff;
 | |
| }
 | |
| 
 | |
| static inline void int2le(unsigned int val, unsigned char* addr)
 | |
| {
 | |
|     addr[0] = val & 0xFF;
 | |
|     addr[1] = (val >> 8) & 0xff;
 | |
|     addr[2] = (val >> 16) & 0xff;
 | |
|     addr[3] = (val >> 24) & 0xff;
 | |
| }
 | |
| 
 | |
| static inline void int2be(unsigned int val, unsigned char* addr)
 | |
| {
 | |
|     addr[0] = (val >> 24) & 0xff;
 | |
|     addr[1] = (val >> 16) & 0xff;
 | |
|     addr[2] = (val >> 8) & 0xff;
 | |
|     addr[3] = val & 0xFF;
 | |
| }
 | |
| 
 | |
| 
 | |
| #define BYTES2INT32(array,pos)\
 | |
|     ((long)array[pos] | ((long)array[pos+1] << 8 ) |\
 | |
|     ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 ))
 | |
| 
 | |
| int read_partinfo(struct ipod_t* ipod, int silent)
 | |
| {
 | |
|     int i;
 | |
|     unsigned long count;
 | |
| 
 | |
|     if(ipod->sectorbuf == NULL) {
 | |
|         fprintf(stderr,"[ERR]  Buffer not initialized.");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     count = ipod_read(ipod,ipod->sector_size);
 | |
| 
 | |
|     if (count <= 0) {
 | |
|         ipod_print_error(" Error reading from disk: ");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     memset(ipod->pinfo, 0, sizeof(ipod->pinfo));
 | |
| 
 | |
|     if ((ipod->sectorbuf[510] == 0x55) && (ipod->sectorbuf[511] == 0xaa)) {
 | |
|         /* DOS partition table */
 | |
|         ipod->macpod = 0;
 | |
|         /* parse partitions */
 | |
|         for ( i = 0; i < 4; i++ ) {
 | |
|             unsigned char* ptr = ipod->sectorbuf + 0x1be + 16*i;
 | |
|             ipod->pinfo[i].type  = ptr[4];
 | |
|             ipod->pinfo[i].start = BYTES2INT32(ptr, 8);
 | |
|             ipod->pinfo[i].size  = BYTES2INT32(ptr, 12);
 | |
| 
 | |
|             /* extended? */
 | |
|             if ( ipod->pinfo[i].type == 5 ) {
 | |
|                 /* not handled yet */
 | |
|             }
 | |
|         }
 | |
|     } else if ((ipod->sectorbuf[0] == 'E') && (ipod->sectorbuf[1] == 'R')) {
 | |
|         /* Apple Partition Map */
 | |
| 
 | |
|         /* APM parsing code based on the check_mac_partitions() function in
 | |
|            ipodloader2 - written by Thomas Tempelmann and released
 | |
|            under the GPL. */
 | |
| 
 | |
|         int blkNo = 1;
 | |
|         int partBlkCount = 1;
 | |
|         int partBlkSizMul = ipod->sectorbuf[2] / 2;
 | |
| 
 | |
|         int pmMapBlkCnt;      /* # of blks in partition map */
 | |
|         int pmPyPartStart;    /* physical start blk of partition */
 | |
|         int pmPartBlkCnt;     /* # of blks in this partition */
 | |
|         int i = 0;
 | |
| 
 | |
|         ipod->macpod = 1;
 | |
| 
 | |
|         memset(ipod->pinfo,0,sizeof(ipod->pinfo));
 | |
| 
 | |
|         while (blkNo <= partBlkCount) {
 | |
|             if (ipod_seek(ipod, blkNo * partBlkSizMul * 512) < 0) {
 | |
|                 fprintf(stderr,"[ERR]  Seek failed whilst reading APM\n");
 | |
|                 return -1;
 | |
|             }
 | |
| 
 | |
|             count = ipod_read(ipod, ipod->sector_size);
 | |
| 
 | |
|             if (count <= 0) {
 | |
|                 ipod_print_error(" Error reading from disk: ");
 | |
|                 return -1;
 | |
|             }
 | |
| 
 | |
|             /* see if it's a partition entry */
 | |
|             if ((ipod->sectorbuf[0] != 'P') || (ipod->sectorbuf[1] != 'M')) {
 | |
|                 /* end of partition table -> leave the loop */
 | |
|                 break;  
 | |
|             }
 | |
| 
 | |
|             /* Extract the interesting entries */
 | |
|             pmMapBlkCnt = be2int(ipod->sectorbuf + 4);
 | |
|             pmPyPartStart = be2int(ipod->sectorbuf + 8);
 | |
|             pmPartBlkCnt = be2int(ipod->sectorbuf + 12);
 | |
| 
 | |
|             /* update the number of part map blocks */
 | |
|             partBlkCount = pmMapBlkCnt;
 | |
| 
 | |
|             if (strncmp((char*)(ipod->sectorbuf + 48), "Apple_MDFW", 32)==0) {
 | |
|                  /* A Firmware partition */
 | |
|                  ipod->pinfo[i].start = pmPyPartStart;
 | |
|                  ipod->pinfo[i].size = pmPartBlkCnt;
 | |
|                  ipod->pinfo[i].type = 0;
 | |
|                  i++;
 | |
|             } else if (strncmp((char*)(ipod->sectorbuf + 48), "Apple_HFS", 32)==0) {
 | |
|                  /* A HFS partition */
 | |
|                  ipod->pinfo[i].start = pmPyPartStart;
 | |
|                  ipod->pinfo[i].size = pmPartBlkCnt;
 | |
|                  ipod->pinfo[i].type = PARTTYPE_HFS;
 | |
|                  i++;
 | |
|             }
 | |
| 
 | |
|             blkNo++; /* read next partition map entry */
 | |
|         }
 | |
|     } else {
 | |
|         if (!silent) fprintf(stderr,"[ERR]  Bad boot sector signature\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     /* Check that the partition table looks like an ipod:
 | |
|            1) Partition 1 is of type 0 (Empty) but isn't empty.
 | |
|            2) Partition 2 is of type 0xb or 0xc (winpod) or -1 (macpod)
 | |
|     */
 | |
|     if ((ipod->pinfo[0].type != 0) || (ipod->pinfo[0].size == 0) || 
 | |
|         ((ipod->pinfo[1].type != 0xb) && (ipod->pinfo[1].type != 0xc) && 
 | |
|          (ipod->pinfo[1].type != PARTTYPE_HFS))) {
 | |
|         if (!silent) fprintf(stderr,"[ERR]  Partition layout is not an ipod\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     ipod->start = ipod->pinfo[0].start*ipod->sector_size;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int read_partition(struct ipod_t* ipod, int outfile)
 | |
| {
 | |
|     int res;
 | |
|     ssize_t n;
 | |
|     int bytesleft;
 | |
|     int chunksize;
 | |
|     int count = ipod->pinfo[0].size;
 | |
| 
 | |
|     if (ipod_seek(ipod, ipod->start) < 0) {
 | |
|         return -1;
 | |
|     }
 | |
|     if(ipod->sectorbuf == NULL) {
 | |
|         fprintf(stderr,"[ERR]  Buffer not initialized.");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     fprintf(stderr,"[INFO] Writing %d sectors to output file\n",count);
 | |
| 
 | |
|     bytesleft = count * ipod->sector_size;
 | |
|     while (bytesleft > 0) {
 | |
|         if (bytesleft > BUFFER_SIZE) {
 | |
|            chunksize = BUFFER_SIZE;
 | |
|         } else {
 | |
|            chunksize = bytesleft;
 | |
|         }
 | |
| 
 | |
|         n = ipod_read(ipod, chunksize);
 | |
| 
 | |
|         if (n < 0) {
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         if (n < chunksize) {
 | |
|             fprintf(stderr,
 | |
|               "[ERR]  Short read in disk_read() - requested %d, got %d\n",
 | |
|               chunksize,(int)n);
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         bytesleft -= n;
 | |
| 
 | |
|         res = write(outfile,ipod->sectorbuf,n);
 | |
| 
 | |
|         if (res < 0) {
 | |
|             perror("[ERR]  write in disk_read");
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         if (res != n) {
 | |
|             fprintf(stderr,
 | |
|               "Short write - requested %d, received %d - aborting.\n",(int)n,res);
 | |
|             return -1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     fprintf(stderr,"[INFO] Done.\n");
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int write_partition(struct ipod_t* ipod, int infile)
 | |
| {
 | |
|     ssize_t res;
 | |
|     int n;
 | |
|     int bytesread;
 | |
|     int byteswritten = 0;
 | |
|     int eof;
 | |
|     int padding = 0;
 | |
| 
 | |
|     if (ipod_seek(ipod, ipod->start) < 0) {
 | |
|         return -1;
 | |
|     }
 | |
|     if(ipod->sectorbuf == NULL) {
 | |
|         fprintf(stderr,"[ERR]  Buffer not initialized.");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     fprintf(stderr,"[INFO] Writing input file to device\n");
 | |
|     bytesread = 0;
 | |
|     eof = 0;
 | |
|     while (!eof) {
 | |
|         n = read(infile,ipod->sectorbuf,BUFFER_SIZE);
 | |
| 
 | |
|         if (n < 0) {
 | |
|             perror("[ERR]  read in disk_write");
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         if (n < BUFFER_SIZE) {
 | |
|            eof = 1;
 | |
|            /* We need to pad the last write to a multiple of SECTOR_SIZE */
 | |
|            if ((n % ipod->sector_size) != 0) {
 | |
|                padding = (ipod->sector_size-(n % ipod->sector_size));
 | |
|                n += padding; 
 | |
|            }
 | |
|         }
 | |
| 
 | |
|         bytesread += n;
 | |
| 
 | |
|         res = ipod_write(ipod, n);
 | |
| 
 | |
|         if (res < 0) {
 | |
|             ipod_print_error(" Error writing to disk: ");
 | |
|             fprintf(stderr,"Bytes written: %d\n",byteswritten);
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         if (res != n) {
 | |
|             fprintf(stderr,"[ERR]  Short write - requested %d, received %d - aborting.\n",n,(int)res);
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         byteswritten += res;
 | |
|     }
 | |
| 
 | |
|     fprintf(stderr,"[INFO] Wrote %d bytes plus %d bytes padding.\n",
 | |
|             byteswritten-padding,padding);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| char* ftypename[] = { "OSOS", "RSRC", "AUPD", "HIBE", "OSBK" };
 | |
| 
 | |
| int diskmove(struct ipod_t* ipod, int delta)
 | |
| {
 | |
|     int src_start;
 | |
|     int src_end;
 | |
|     int bytesleft;
 | |
|     int chunksize;
 | |
|     int n;
 | |
| 
 | |
|     src_start = ipod->ipod_directory[1].devOffset;
 | |
|     src_end = (ipod->ipod_directory[ipod->nimages-1].devOffset + ipod->sector_size + 
 | |
|               ipod->ipod_directory[ipod->nimages-1].len + 
 | |
|               (ipod->sector_size-1)) & ~(ipod->sector_size-1);
 | |
|     bytesleft = src_end - src_start;
 | |
| 
 | |
|     if (ipod_verbose) {
 | |
|         fprintf(stderr,"[INFO] Need to move images 2-%d forward %08x bytes\n", ipod->nimages,delta);
 | |
|         fprintf(stderr,"[VERB] src_start     = %08x\n",src_start);
 | |
|         fprintf(stderr,"[VERB] src_end       = %08x\n",src_end);
 | |
|         fprintf(stderr,"[VERB] dest_start    = %08x\n",src_start+delta);
 | |
|         fprintf(stderr,"[VERB] dest_end      = %08x\n",src_end+delta);
 | |
|         fprintf(stderr,"[VERB] bytes to copy = %08x\n",bytesleft);
 | |
|     }
 | |
| 
 | |
|     while (bytesleft > 0) {
 | |
|         if (bytesleft <= BUFFER_SIZE) {
 | |
|             chunksize = bytesleft;
 | |
|         } else {
 | |
|             chunksize = BUFFER_SIZE;
 | |
|         }
 | |
| 
 | |
|         if (ipod_verbose) {
 | |
|             fprintf(stderr,"[VERB] Copying %08x bytes from %08x to %08x (absolute %08x to %08x)\n",
 | |
|                            chunksize,
 | |
|                            src_end-chunksize,
 | |
|                            src_end-chunksize+delta,
 | |
|                            (unsigned int)(ipod->start+src_end-chunksize),
 | |
|                            (unsigned int)(ipod->start+src_end-chunksize+delta));
 | |
|         }
 | |
| 
 | |
| 
 | |
|         if (ipod_seek(ipod, ipod->start+src_end-chunksize) < 0) {
 | |
|             fprintf(stderr,"[ERR]  Seek failed\n");
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         if ((n = ipod_read(ipod,chunksize)) < 0) {
 | |
|             perror("[ERR]  Write failed\n");
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         if (n < chunksize) {
 | |
|             fprintf(stderr,"[ERR]  Short read - requested %d bytes, received %d\n",
 | |
|                            chunksize,n);
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         if (ipod_seek(ipod, ipod->start+src_end-chunksize+delta) < 0) {
 | |
|             fprintf(stderr,"[ERR]  Seek failed\n");
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         if ((n = ipod_write(ipod,chunksize)) < 0) {
 | |
|             perror("[ERR]  Write failed\n");
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         if (n < chunksize) {
 | |
|             fprintf(stderr,"[ERR]  Short write - requested %d bytes, received %d\n"
 | |
|                           ,chunksize,n);
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         src_end -= chunksize;
 | |
|         bytesleft -= chunksize;
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int rename_image(struct ipod_t* ipod, char* from, char* to)
 | |
| {
 | |
|     int n;
 | |
|     int x;
 | |
|     int found;
 | |
|     int i;
 | |
|     unsigned char* p;
 | |
| 
 | |
|     /* diroffset may not be sector-aligned */
 | |
|     x = ipod->diroffset % ipod->sector_size;
 | |
| 
 | |
|     if(ipod->sectorbuf == NULL) {
 | |
|         fprintf(stderr,"[ERR]  Buffer not initialized.");
 | |
|         return -1;
 | |
|     }
 | |
|     /* Read directory */
 | |
|     if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { 
 | |
|         fprintf(stderr,"[ERR]  Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     n=ipod_read(ipod, ipod->sector_size);
 | |
|     if (n < 0) { 
 | |
|         fprintf(stderr,"[ERR]  Read of directory failed.\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     p = ipod->sectorbuf + x;
 | |
| 
 | |
|     /* A hack to detect 2nd gen Nanos - maybe there is a better way? */
 | |
|     if (p[0] == 0)
 | |
|     {
 | |
|         /* Adjust diroffset */
 | |
|         ipod->diroffset += ipod->sector_size - x;
 | |
| 
 | |
|         n=ipod_read(ipod, ipod->sector_size);
 | |
|         if (n < 0) { 
 | |
|             fprintf(stderr,"[ERR]  Read of directory failed.\n");
 | |
|             return -1;
 | |
|         }
 | |
|         p = ipod->sectorbuf;
 | |
|     }
 | |
| 
 | |
|     found = 0;
 | |
|     for (i=0 ; !found && i < MAX_IMAGES; i++) {
 | |
|        if (memcmp(p + 4, from, 4) == 0) {
 | |
|            memcpy(p + 4, to, 4);
 | |
| 
 | |
|            found = 1;
 | |
|        }
 | |
|        p += 40;
 | |
|     }
 | |
| 
 | |
|     if (!found) {
 | |
|         fprintf(stderr,"[ERR]  Unexpected error - no \"%s\" image!\n", from);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     /* Write directory back to disk */
 | |
|     if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { 
 | |
|         fprintf(stderr,"[ERR]  Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     n=ipod_write(ipod, ipod->sector_size);
 | |
|     if (n < 0) { 
 | |
|         fprintf(stderr,"[ERR]  Write of directory failed in rename_image.\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int delete_image(struct ipod_t* ipod, char* name)
 | |
| {
 | |
|     int n;
 | |
|     int x;
 | |
|     int found;
 | |
|     int i;
 | |
|     unsigned char* p;
 | |
| 
 | |
|     /* diroffset may not be sector-aligned */
 | |
|     x = ipod->diroffset % ipod->sector_size;
 | |
| 
 | |
|     /* Read directory */
 | |
|     if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { 
 | |
|         fprintf(stderr,"[ERR]  Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     n=ipod_read(ipod, ipod->sector_size);
 | |
|     if (n < 0) { 
 | |
|         fprintf(stderr,"[ERR]  Read of directory failed.\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     p = ipod->sectorbuf + x;
 | |
| 
 | |
|     /* A hack to detect 2nd gen Nanos - maybe there is a better way? */
 | |
|     if (p[0] == 0)
 | |
|     {
 | |
|         /* Adjust diroffset */
 | |
|         ipod->diroffset += ipod->sector_size - x;
 | |
| 
 | |
|         n=ipod_read(ipod, ipod->sector_size);
 | |
|         if (n < 0) { 
 | |
|             fprintf(stderr,"[ERR]  Read of directory failed.\n");
 | |
|             return -1;
 | |
|         }
 | |
|         p = ipod->sectorbuf;
 | |
|     }
 | |
| 
 | |
|     found = 0;
 | |
|     for (i=0 ; !found && i < MAX_IMAGES; i++) {
 | |
|        if (memcmp(p + 4, name, 4) == 0) {
 | |
|            memset(p, 0, 40);  /* Delete directory entry */
 | |
|            found = 1;
 | |
|        }
 | |
|        p += 40;
 | |
|     }
 | |
| 
 | |
|     if (!found) {
 | |
|         fprintf(stderr,"[ERR]  Unexpected error - no \"%s\" image!\n", name);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     /* Write directory back to disk */
 | |
|     if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { 
 | |
|         fprintf(stderr,"[ERR]  Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     n=ipod_write(ipod, ipod->sector_size);
 | |
|     if (n < 0) { 
 | |
|         fprintf(stderr,"[ERR]  Write of directory failed in delete_image.\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int add_new_image(struct ipod_t* ipod, char* imagename, char* filename, int type)
 | |
| {
 | |
|     int length;
 | |
|     int found;
 | |
|     int i;
 | |
|     int x;
 | |
|     int n;
 | |
|     int infile;
 | |
|     int newsize;
 | |
|     unsigned long chksum=0;
 | |
|     unsigned long filechksum=0;
 | |
|     unsigned long offset;
 | |
|     unsigned char header[8];  /* Header for .ipod file */
 | |
|     unsigned char* p;
 | |
| 
 | |
|     if(ipod->sectorbuf == NULL) {
 | |
|         fprintf(stderr,"[ERR]  Buffer not initialized.");
 | |
|         return -1;
 | |
|     }
 | |
| #ifdef WITH_BOOTOBJS
 | |
|     if (type == FILETYPE_INTERNAL) {
 | |
|         fprintf(stderr,"[INFO] Using internal bootloader - %d bytes\n",ipod->bootloader_len);
 | |
|         length = ipod->bootloader_len;
 | |
|         infile = -1;
 | |
|     } 
 | |
|     else 
 | |
| #endif
 | |
|     {
 | |
|         /* First check that the input file is the correct type for this ipod. */
 | |
|         infile=open(filename,O_RDONLY);
 | |
|         if (infile < 0) {
 | |
|             fprintf(stderr,"[ERR]  Couldn't open input file %s\n",filename);
 | |
|             return -1;
 | |
|         }
 | |
|     
 | |
|         if (type==FILETYPE_DOT_IPOD) {
 | |
|             n = read(infile,header,8);
 | |
|             if (n < 8) {
 | |
|                 fprintf(stderr,"[ERR]  Failed to read header from %s\n",filename);
 | |
|                 close(infile);
 | |
|                 return -1;
 | |
|             }
 | |
|     
 | |
|             if (memcmp(header+4, ipod->modelname,4)!=0) {
 | |
|                 fprintf(stderr,"[ERR]  Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n",
 | |
|                         header[4],header[5],header[6],header[7], ipod->modelname);
 | |
|                 close(infile);
 | |
|                 return -1;
 | |
|             }
 | |
|     
 | |
|             filechksum = be2int(header);
 | |
|     
 | |
|             length = filesize(infile)-8;
 | |
|         } else {
 | |
|             length = filesize(infile);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1);
 | |
| 
 | |
|     fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n",
 | |
|             length,newsize);
 | |
| 
 | |
|     if (newsize > BUFFER_SIZE) {
 | |
|         fprintf(stderr,"[ERR]  Input file too big for buffer\n");
 | |
|         if (infile >= 0) close(infile);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     /* TODO: Check if we have enough space in the partition for the new image */
 | |
| 
 | |
| #ifdef WITH_BOOTOBJS
 | |
|     if (type == FILETYPE_INTERNAL) {
 | |
|         memcpy(ipod->sectorbuf,ipod->bootloader,ipod->bootloader_len);
 | |
|     } 
 | |
|     else
 | |
| #endif
 | |
|     {
 | |
|         fprintf(stderr,"[INFO] Reading input file...\n");
 | |
| 
 | |
|         n = read(infile,ipod->sectorbuf,length);
 | |
|         if (n < 0) {
 | |
|             fprintf(stderr,"[ERR]  Couldn't read input file\n");
 | |
|             close(infile);
 | |
|             return -1;
 | |
|         }
 | |
|         close(infile);
 | |
|     }
 | |
| 
 | |
|     /* Pad the data with zeros */
 | |
|     memset(ipod->sectorbuf+length,0,newsize-length);
 | |
| 
 | |
|     if (type==FILETYPE_DOT_IPOD) {
 | |
|         chksum = ipod->modelnum;
 | |
|         for (i = 0; i < length; i++) {
 | |
|             /* add 8 unsigned bits but keep a 32 bit sum */
 | |
|             chksum += ipod->sectorbuf[i];
 | |
|         }
 | |
| 
 | |
|         if (chksum == filechksum) {
 | |
|             fprintf(stderr,"[INFO] Checksum OK in %s\n",filename);
 | |
|         } else {
 | |
|             fprintf(stderr,"[ERR]  Checksum in %s failed check\n",filename);
 | |
|             return -1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
| 
 | |
|     offset = ipod->fwoffset + ipod->ipod_directory[ipod->nimages - 1].devOffset +
 | |
|              ipod->ipod_directory[ipod->nimages - 1].len + ipod->sector_size;
 | |
| 
 | |
|     /* 2nd Gen Nano has encrypted firmware, and the sector
 | |
|        preceeding the firmware contains hashes that need to be
 | |
|        preserved.  Nano 2G images include these extra 2048 (0x800)
 | |
|        bytes 
 | |
|      */
 | |
|     if (ipod_seek(ipod, offset - (ipod->modelnum == 62 ? 0x800 : 0)) < 0) {
 | |
|         fprintf(stderr,"[ERR]  Seek failed\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if ((n = ipod_write(ipod,newsize)) < 0) {
 | |
|         perror("[ERR]  Write failed\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (n < newsize) {
 | |
|         fprintf(stderr,"[ERR]  Short write - requested %d bytes, received %d\n"
 | |
|                       ,newsize,n);
 | |
|         return -1;
 | |
|     }
 | |
|     fprintf(stderr,"[INFO]  Wrote %d bytes to firmware partition\n",n);
 | |
| 
 | |
|     /* Now we need to create a new directory entry 
 | |
| 
 | |
|        NOTE: On the Nano 2G, the checksum is the checksum of the
 | |
|              unencrypted firmware.  But this isn't checked by the NOR
 | |
|              bootloader (there are cryptographic hashes in the
 | |
|              firmware itself), so it doesn't matter that this is
 | |
|              wrong.
 | |
|       */
 | |
|     chksum = 0;
 | |
|     for (i = 0; i < length; i++) {
 | |
|          /* add 8 unsigned bits but keep a 32 bit sum */
 | |
|          chksum += ipod->sectorbuf[i];
 | |
|     }
 | |
| 
 | |
|     x = ipod->diroffset % ipod->sector_size;
 | |
| 
 | |
|     /* Read directory */
 | |
|     if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
 | |
| 
 | |
|     n=ipod_read(ipod, ipod->sector_size);
 | |
|     if (n < 0) { return -1; }
 | |
| 
 | |
|     /* Create a new directory entry */
 | |
| 
 | |
|     /* Copy OSOS or OSBK details - we assume one of them exists */
 | |
|     p = ipod->sectorbuf + x;
 | |
|     found = 0;
 | |
|     for (i = 0; !found && i < ipod->nimages; i++) {
 | |
|         if ((memcmp(p + 4, "soso", 4)==0) || (memcmp(p + 4, "kbso", 4)==0)) {
 | |
|             found = 1;
 | |
|         } else {
 | |
|             p += 40;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (!found) {
 | |
|         fprintf(stderr,"[ERR]  No OSOS or OSBK image to copy directory from\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     /* Copy directory image */
 | |
|     memcpy(ipod->sectorbuf + x + (ipod->nimages * 40), p, 40);
 | |
|     p = ipod->sectorbuf + x + (ipod->nimages * 40);
 | |
| 
 | |
|     /* Modify directory. */
 | |
|     memcpy(p + 4, imagename, 4);
 | |
|     int2le(offset - ipod->fwoffset, p + 12);                     /* devOffset */
 | |
|     int2le(length - (ipod->modelnum==62 ? 0x800: 0), p + 16);    /* len */
 | |
|     int2le(chksum, p + 28);                                      /* checksum */
 | |
| 
 | |
|     /* Write directory */    
 | |
|     if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
 | |
|     n=ipod_write(ipod, ipod->sector_size);
 | |
|     if (n < 0) { return -1; }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| int ipod_has_bootloader(struct ipod_t* ipod)
 | |
| {
 | |
|     /* The 2nd gen Nano is installed differently */
 | |
|     if (ipod->modelnum == 62) {
 | |
|         int i;
 | |
|         int has_osbk = 0;
 | |
|         /* Check if we have an OSBK image */
 | |
|         for (i = 0; i < ipod->nimages; i++) {
 | |
|             if (ipod->ipod_directory[i].ftype==FTYPE_OSBK) {
 | |
|                 has_osbk = 1;
 | |
|             }
 | |
|         }
 | |
|         return has_osbk;
 | |
|     }
 | |
|     else {
 | |
|         return (ipod->ipod_directory[0].entryOffset != 0);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|     Bootloader installation on the Nano2G consists of renaming the
 | |
|     OSOS image to OSBK and then writing the Rockbox bootloader as a
 | |
|     new OSOS image.
 | |
| 
 | |
|     Maybe this approach can/should be adapted for other ipods, as it
 | |
|     prevents the Apple bootloader loading the original firmware into
 | |
|     RAM along with the Rockbox bootloader (and hence will give a
 | |
|     faster boot when the user just wants to start Rockbox).
 | |
| 
 | |
| */
 | |
| 
 | |
| static int add_bootloader_nano2g(struct ipod_t* ipod, char* filename, int type)
 | |
| {
 | |
|     /* Check if we already have an OSBK image */
 | |
|     if (ipod_has_bootloader(ipod) == 0) {
 | |
|         /* First-time install - rename OSOS to OSBK and create new OSOS for bootloader */
 | |
|         fprintf(stderr,"[INFO] Creating OSBK backup image of original firmware\n");
 | |
| 
 | |
|         if  (rename_image(ipod, "soso", "kbso") < 0) {
 | |
|             fprintf(stderr,"[ERR]  Could not rename OSOS image\n");
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         /* Add our bootloader as a brand new image */
 | |
|         return add_new_image(ipod, "soso", filename, type);
 | |
|     } else {
 | |
|         /* This is an update, just replace OSOS with our bootloader */
 | |
| 
 | |
|         return write_firmware(ipod, filename, type);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| static int delete_bootloader_nano2g(struct ipod_t* ipod)
 | |
| {
 | |
|     /* Check if we have an OSBK image */
 | |
|     if (ipod_has_bootloader(ipod) == 0) {
 | |
|         fprintf(stderr,"[ERR]  No OSBK image found - nothing to uninstall\n");
 | |
|         return -1;
 | |
|     } else {
 | |
|         /* Delete our bootloader image */
 | |
|         if (delete_image(ipod, "soso") < 0) {
 | |
|             fprintf(stderr,"[WARN] Could not delete OSOS image\n");
 | |
|         } else {
 | |
|             fprintf(stderr,"[INFO] OSOS image deleted\n");
 | |
|         }
 | |
| 
 | |
|         if  (rename_image(ipod, "kbso", "soso") < 0) {
 | |
|             fprintf(stderr,"[ERR]  Could not rename OSBK image\n");
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
| 
 | |
|         fprintf(stderr,"[INFO] OSBK image renamed to OSOS - bootloader uninstalled.\n");
 | |
|         return 0;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| int add_bootloader(struct ipod_t* ipod, char* filename, int type)
 | |
| {
 | |
|     int length;
 | |
|     int i;
 | |
|     int x;
 | |
|     int n;
 | |
|     int infile;
 | |
|     int paddedlength;
 | |
|     int entryOffset;
 | |
|     int delta = 0;
 | |
|     unsigned long chksum=0;
 | |
|     unsigned long filechksum=0;
 | |
|     unsigned char header[8];  /* Header for .ipod file */
 | |
|     unsigned char* bootloader_buf;
 | |
| 
 | |
|     /* The 2nd gen Nano is installed differently */
 | |
|     if (ipod->modelnum == 62) {
 | |
|         return add_bootloader_nano2g(ipod, filename, type);
 | |
|     }
 | |
|     if(ipod->sectorbuf == NULL) {
 | |
|         fprintf(stderr,"[ERR]  Buffer not initialized.");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     /* Calculate the position in the OSOS image where our bootloader will go. */
 | |
|     if (ipod->ipod_directory[0].entryOffset>0) {
 | |
|         /* Keep the same entryOffset */
 | |
|         entryOffset = ipod->ipod_directory[0].entryOffset;
 | |
|     } else {
 | |
|         entryOffset = (ipod->ipod_directory[0].len+ipod->sector_size-1)&~(ipod->sector_size-1);
 | |
|     }
 | |
| 
 | |
| #ifdef WITH_BOOTOBJS
 | |
|     if (type == FILETYPE_INTERNAL) {
 | |
|         fprintf(stderr,"[INFO] Using internal bootloader - %d bytes\n",ipod->bootloader_len);
 | |
|         memcpy(ipod->sectorbuf+entryOffset,ipod->bootloader,ipod->bootloader_len);
 | |
|         length = ipod->bootloader_len;
 | |
|         paddedlength=(ipod->bootloader_len+ipod->sector_size-1)&~(ipod->sector_size-1);
 | |
|     } 
 | |
|     else 
 | |
| #endif
 | |
|     {
 | |
|         infile=open(filename,O_RDONLY);
 | |
|         if (infile < 0) {
 | |
|             fprintf(stderr,"[ERR]  Couldn't open input file %s\n",filename);
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         if (type==FILETYPE_DOT_IPOD) {
 | |
|             /* First check that the input file is the correct type for this ipod. */
 | |
|             n = read(infile,header,8);
 | |
|             if (n < 8) {
 | |
|                 fprintf(stderr,"[ERR]  Failed to read header from %s\n",filename);
 | |
|                 close(infile);
 | |
|                 return -1;
 | |
|             }
 | |
|     
 | |
|             if (memcmp(header+4, ipod->modelname,4)!=0) {
 | |
|                 fprintf(stderr,"[ERR]  Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n",
 | |
|                         header[4],header[5],header[6],header[7], ipod->modelname);
 | |
|                 close(infile);
 | |
|                 return -1;
 | |
|             }
 | |
|     
 | |
|             filechksum = be2int(header);
 | |
|     
 | |
|             length=filesize(infile)-8;
 | |
|         } else {
 | |
|             length=filesize(infile);
 | |
|         }
 | |
|         paddedlength=(length+ipod->sector_size-1)&~(ipod->sector_size-1);
 | |
| 
 | |
|         bootloader_buf = malloc(length);
 | |
|         if (bootloader_buf == NULL) {
 | |
|             fprintf(stderr,"[ERR]  Can not allocate memory for bootloader\n");
 | |
|             return -1;
 | |
|         }
 | |
|         /* Now read our bootloader - we need to check it before modifying the partition*/
 | |
|         n = read(infile,bootloader_buf,length);
 | |
|         close(infile);
 | |
| 
 | |
|         if (n < 0) {
 | |
|             fprintf(stderr,"[ERR]  Couldn't read input file\n");
 | |
|             return -1;
 | |
|         }
 | |
|     
 | |
|         if (type==FILETYPE_DOT_IPOD) {
 | |
|             /* Calculate and confirm bootloader checksum */
 | |
|             chksum = ipod->modelnum;
 | |
|             for (i = 0; i < length; i++) {
 | |
|                  /* add 8 unsigned bits but keep a 32 bit sum */
 | |
|                  chksum += bootloader_buf[i];
 | |
|             }
 | |
|     
 | |
|             if (chksum == filechksum) {
 | |
|                 fprintf(stderr,"[INFO] Checksum OK in %s\n",filename);
 | |
|             } else {
 | |
|                 fprintf(stderr,"[ERR]  Checksum in %s failed check\n",filename);
 | |
|                 return -1;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (entryOffset+paddedlength > BUFFER_SIZE) {
 | |
|         fprintf(stderr,"[ERR]  Input file too big for buffer\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (ipod_verbose) {
 | |
|         fprintf(stderr,"[VERB] Original firmware begins at 0x%08x\n", ipod->ipod_directory[0].devOffset + ipod->sector_size);
 | |
|         fprintf(stderr,"[VERB] New entryOffset will be 0x%08x\n",entryOffset);
 | |
|         fprintf(stderr,"[VERB] End of bootloader will be at 0x%08x\n",entryOffset+paddedlength);
 | |
|     }
 | |
| 
 | |
|     /* Check if we have enough space */
 | |
|     /* TODO: Check the size of the partition. */
 | |
|     if (ipod->nimages > 1) {
 | |
|         if ((ipod->ipod_directory[0].devOffset+entryOffset+paddedlength) >
 | |
|              ipod->ipod_directory[1].devOffset) {
 | |
|             fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n");
 | |
|             delta = ipod->ipod_directory[0].devOffset + entryOffset+paddedlength
 | |
|                     - ipod->ipod_directory[1].devOffset + ipod->sector_size;
 | |
| 
 | |
|             if (diskmove(ipod, delta) < 0) {
 | |
|                 fprintf(stderr,"[ERR]  Image movement failed.\n");
 | |
|                 return -1;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /* We have moved the partitions, now we can write our bootloader */
 | |
| 
 | |
|     /* Firstly read the original firmware into ipod->sectorbuf */
 | |
|     fprintf(stderr,"[INFO] Reading original firmware...\n");
 | |
|     if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) {
 | |
|         fprintf(stderr,"[ERR]  Seek failed\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if ((n = ipod_read(ipod,entryOffset)) < 0) {
 | |
|         perror("[ERR]  Read failed\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (n < entryOffset) {
 | |
|         fprintf(stderr,"[ERR]  Short read - requested %d bytes, received %d\n"
 | |
|                       ,entryOffset,n);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
| #ifdef WITH_BOOTOBJS
 | |
|     if (type == FILETYPE_INTERNAL) {
 | |
|         memcpy(ipod->sectorbuf+entryOffset,ipod->bootloader,ipod->bootloader_len);
 | |
|     }
 | |
|     else
 | |
| #endif
 | |
|     {
 | |
|         memcpy(ipod->sectorbuf+entryOffset,bootloader_buf,length);
 | |
|         free(bootloader_buf);
 | |
|     }
 | |
| 
 | |
|     /* Calculate new checksum for combined image */
 | |
|     chksum = 0;
 | |
|     for (i=0;i<entryOffset + length; i++) {
 | |
|          chksum += ipod->sectorbuf[i];
 | |
|     }    
 | |
| 
 | |
|     /* Now write the combined firmware image to the disk */
 | |
| 
 | |
|     if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) {
 | |
|         fprintf(stderr,"[ERR]  Seek failed\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if ((n = ipod_write(ipod,entryOffset+paddedlength)) < 0) {
 | |
|         perror("[ERR]  Write failed\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (n < (entryOffset+paddedlength)) {
 | |
|         fprintf(stderr,"[ERR]  Short read - requested %d bytes, received %d\n"
 | |
|                       ,entryOffset+paddedlength,n);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     fprintf(stderr,"[INFO]  Wrote %d bytes to firmware partition\n",entryOffset+paddedlength);
 | |
| 
 | |
|     x = ipod->diroffset % ipod->sector_size;
 | |
| 
 | |
|     /* Read directory */
 | |
|     if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { 
 | |
|         fprintf(stderr,"[ERR]  Seek failed\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     n=ipod_read(ipod, ipod->sector_size);
 | |
|     if (n < 0) {
 | |
|         fprintf(stderr,"[ERR]  Directory read failed\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     /* Update entries for image 0 */
 | |
|     int2le(entryOffset+length,ipod->sectorbuf+x+16);
 | |
|     int2le(entryOffset,ipod->sectorbuf+x+24);
 | |
|     int2le(chksum,ipod->sectorbuf+x+28);
 | |
|     int2le(0xffffffff,ipod->sectorbuf+x+36);  /* loadAddr */
 | |
| 
 | |
|     /* Update devOffset entries for other images, if we have moved them */
 | |
|     if (delta > 0) {
 | |
|         for (i=1;i<ipod->nimages;i++) {
 | |
|            int2le(le2int(ipod->sectorbuf+x+i*40+12)+delta,ipod->sectorbuf+x+i*40+12);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* Write directory */    
 | |
|     if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { 
 | |
|         fprintf(stderr,"[ERR]  Seek to %d failed\n", (int)(ipod->start+ipod->diroffset-x));
 | |
|         return -1;
 | |
|     }
 | |
|     n=ipod_write(ipod, ipod->sector_size);
 | |
|     if (n < 0) { 
 | |
|         fprintf(stderr,"[ERR]  Directory write failed\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int delete_bootloader(struct ipod_t* ipod)
 | |
| {
 | |
|     int length;
 | |
|     int i;
 | |
|     int x;
 | |
|     int n;
 | |
|     unsigned long chksum=0;   /* 32 bit checksum - Rockbox .ipod style*/
 | |
| 
 | |
|     /* The 2nd gen Nano is installed differently */
 | |
|     if (ipod->modelnum == 62) {
 | |
|         return delete_bootloader_nano2g(ipod);
 | |
|     }
 | |
|     if(ipod->sectorbuf == NULL) {
 | |
|         fprintf(stderr,"[ERR]  Buffer not initialized.");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     /* Removing the bootloader involves adjusting the "length",
 | |
|        "chksum" and "entryOffset" values in the osos image's directory
 | |
|        entry. */
 | |
| 
 | |
|     /* Firstly check we have a bootloader... */
 | |
| 
 | |
|     if (ipod_has_bootloader(ipod) == 0) {
 | |
|         fprintf(stderr,"[ERR]  No bootloader found.\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     length = ipod->ipod_directory[0].entryOffset;
 | |
| 
 | |
|     /* Read the firmware so we can calculate the checksum */
 | |
|     fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length);
 | |
| 
 | |
|     if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1);
 | |
|     fprintf(stderr,"[INFO] Padding read from 0x%08x to 0x%08x bytes\n",
 | |
|             length,i);
 | |
| 
 | |
|     if ((n = ipod_read(ipod,i)) < 0) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (n < i) {
 | |
|         fprintf(stderr,"[ERR]  Short read - requested %d bytes, received %d\n",
 | |
|                 i,n);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     chksum = 0;
 | |
|     for (i = 0; i < length; i++) {
 | |
|          /* add 8 unsigned bits but keep a 32 bit sum */
 | |
|          chksum += ipod->sectorbuf[i];
 | |
|     }
 | |
| 
 | |
|     /* Now write back the updated directory entry */
 | |
| 
 | |
|     fprintf(stderr,"[INFO] Updating firmware checksum\n");
 | |
| 
 | |
|     x = ipod->diroffset % ipod->sector_size;
 | |
| 
 | |
|     /* Read directory */
 | |
|     if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
 | |
| 
 | |
|     n=ipod_read(ipod, ipod->sector_size);
 | |
|     if (n < 0) { return -1; }
 | |
| 
 | |
|     /* Update entries for image 0 */
 | |
|     int2le(length,ipod->sectorbuf+x+16);
 | |
|     int2le(0,ipod->sectorbuf+x+24);
 | |
|     int2le(chksum,ipod->sectorbuf+x+28);
 | |
| 
 | |
|     /* Write directory */    
 | |
|     if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
 | |
|     n=ipod_write(ipod, ipod->sector_size);
 | |
|     if (n < 0) { return -1; }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int write_firmware(struct ipod_t* ipod, char* filename, int type)
 | |
| {
 | |
|     int length;
 | |
|     int i;
 | |
|     int x;
 | |
|     int n;
 | |
|     int infile;
 | |
|     int newsize;
 | |
|     int bytesavailable;
 | |
|     unsigned long chksum=0;
 | |
|     unsigned long filechksum=0;
 | |
|     unsigned long offset;
 | |
|     unsigned char header[8];  /* Header for .ipod file */
 | |
|     unsigned char* p;
 | |
| 
 | |
|     if(ipod->sectorbuf == NULL) {
 | |
|         fprintf(stderr,"[ERR]  Buffer not initialized.");
 | |
|         return -1;
 | |
|     }
 | |
| #ifdef WITH_BOOTOBJS
 | |
|     if (type == FILETYPE_INTERNAL) {
 | |
|         fprintf(stderr,"[INFO] Using internal bootloader - %d bytes\n",ipod->bootloader_len);
 | |
|         length = ipod->bootloader_len;
 | |
|         infile = -1;
 | |
|     } 
 | |
|     else 
 | |
| #endif
 | |
|     {
 | |
|         /* First check that the input file is the correct type for this ipod. */
 | |
|         infile=open(filename,O_RDONLY);
 | |
|         if (infile < 0) {
 | |
|             fprintf(stderr,"[ERR]  Couldn't open input file %s\n",filename);
 | |
|             return -1;
 | |
|         }
 | |
|     
 | |
|         if (type==FILETYPE_DOT_IPOD) {
 | |
|             n = read(infile,header,8);
 | |
|             if (n < 8) {
 | |
|                 fprintf(stderr,"[ERR]  Failed to read header from %s\n",filename);
 | |
|                 close(infile);
 | |
|                 return -1;
 | |
|             }
 | |
|     
 | |
|             if (memcmp(header+4, ipod->modelname,4)!=0) {
 | |
|                 fprintf(stderr,"[ERR]  Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n",
 | |
|                         header[4],header[5],header[6],header[7], ipod->modelname);
 | |
|                 close(infile);
 | |
|                 return -1;
 | |
|             }
 | |
|     
 | |
|             filechksum = be2int(header);
 | |
|     
 | |
|             length = filesize(infile)-8;
 | |
|         } else {
 | |
|             length = filesize(infile);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1);
 | |
| 
 | |
|     fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n",
 | |
|             length,newsize);
 | |
| 
 | |
|     if (newsize > BUFFER_SIZE) {
 | |
|         fprintf(stderr,"[ERR]  Input file too big for buffer\n");
 | |
|         if (infile >= 0) close(infile);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     /* Check if we have enough space */
 | |
|     /* TODO: Check the size of the partition. */
 | |
|     if (ipod->nimages > 1) {
 | |
|         bytesavailable=ipod->ipod_directory[1].devOffset-ipod->ipod_directory[0].devOffset;
 | |
|         if (bytesavailable < newsize) {
 | |
|             fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n");
 | |
| 
 | |
|             /* TODO: Implement image movement */
 | |
|             fprintf(stderr,"[ERR]  Image movement not yet implemented.\n");
 | |
|             close(infile);
 | |
|             return -1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
| #ifdef WITH_BOOTOBJS
 | |
|     if (type == FILETYPE_INTERNAL) {
 | |
|         memcpy(ipod->sectorbuf,ipod->bootloader,ipod->bootloader_len);
 | |
|     } 
 | |
|     else
 | |
| #endif
 | |
|     {
 | |
|         fprintf(stderr,"[INFO] Reading input file...\n");
 | |
|         /* We now know we have enough space, so write it. */
 | |
|         n = read(infile,ipod->sectorbuf,length);
 | |
|         if (n < 0) {
 | |
|             fprintf(stderr,"[ERR]  Couldn't read input file\n");
 | |
|             close(infile);
 | |
|             return -1;
 | |
|         }
 | |
|         close(infile);
 | |
|     }
 | |
| 
 | |
|     /* Pad the data with zeros */
 | |
|     memset(ipod->sectorbuf+length,0,newsize-length);
 | |
| 
 | |
|     if (type==FILETYPE_DOT_IPOD) {
 | |
|         chksum = ipod->modelnum;
 | |
|         for (i = 0; i < length; i++) {
 | |
|             /* add 8 unsigned bits but keep a 32 bit sum */
 | |
|             chksum += ipod->sectorbuf[i];
 | |
|         }
 | |
| 
 | |
|         if (chksum == filechksum) {
 | |
|             fprintf(stderr,"[INFO] Checksum OK in %s\n",filename);
 | |
|         } else {
 | |
|             fprintf(stderr,"[ERR]  Checksum in %s failed check\n",filename);
 | |
|             return -1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
| 
 | |
|     offset = ipod->fwoffset+ipod->ipod_directory[ipod->ososimage].devOffset;
 | |
| 
 | |
|     if (ipod->modelnum==62) {
 | |
| 
 | |
|         /* 2nd Gen Nano has encrypted firmware, and the sector
 | |
|            preceeding the firmware contains hashes that need to be
 | |
|            preserved.  Nano 2G images include these extra 2048 (0x800)
 | |
|            bytes 
 | |
|         */
 | |
| 
 | |
|         offset -= 0x800;
 | |
| 
 | |
|        /* TODO: The above checks need to take into account this 0x800 bytes */
 | |
|     }
 | |
| 
 | |
|     if (ipod_seek(ipod, offset) < 0) {
 | |
|         fprintf(stderr,"[ERR]  Seek failed\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if ((n = ipod_write(ipod,newsize)) < 0) {
 | |
|         perror("[ERR]  Write failed\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (n < newsize) {
 | |
|         fprintf(stderr,"[ERR]  Short write - requested %d bytes, received %d\n"
 | |
|                       ,newsize,n);
 | |
|         return -1;
 | |
|     }
 | |
|     fprintf(stderr,"[INFO]  Wrote %d bytes to firmware partition\n",n);
 | |
| 
 | |
|     /* Now we need to update the "len", "entryOffset" and "chksum" fields
 | |
| 
 | |
|        NOTE: On the Nano 2G, the checksum is the checksum of the
 | |
|              unencrypted firmware.  But this isn't checked by the NOR
 | |
|              bootloader (there are cryptographic hashes in the
 | |
|              firmware itself), so it doesn't matter that this is
 | |
|              wrong.
 | |
|       */
 | |
|     chksum = 0;
 | |
|     for (i = 0; i < length; i++) {
 | |
|          /* add 8 unsigned bits but keep a 32 bit sum */
 | |
|          chksum += ipod->sectorbuf[i];
 | |
|     }
 | |
| 
 | |
|     x = ipod->diroffset % ipod->sector_size;
 | |
| 
 | |
|     /* Read directory */
 | |
|     if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
 | |
| 
 | |
|     n=ipod_read(ipod, ipod->sector_size);
 | |
|     if (n < 0) { return -1; }
 | |
| 
 | |
|     /* Update entries for image */
 | |
|     p = ipod->sectorbuf + x + (ipod->ososimage * 40);
 | |
|     int2le(length - (ipod->modelnum==62 ? 0x800: 0), p + 16);
 | |
|     int2le(0, p + 24);
 | |
|     int2le(chksum, p + 28);
 | |
| 
 | |
|     /* Write directory */
 | |
|     if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
 | |
|     n=ipod_write(ipod, ipod->sector_size);
 | |
|     if (n < 0) { return -1; }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int read_firmware(struct ipod_t* ipod, char* filename, int type)
 | |
| {
 | |
|     int length;
 | |
|     int i;
 | |
|     int outfile;
 | |
|     int n;
 | |
|     unsigned long offset;
 | |
|     unsigned long chksum=0;   /* 32 bit checksum - Rockbox .ipod style*/
 | |
|     unsigned char header[8];  /* Header for .ipod file */
 | |
| 
 | |
|     if(ipod->sectorbuf == NULL) {
 | |
|         fprintf(stderr,"[ERR]  Buffer not initialized.");
 | |
|         return -1;
 | |
|     }
 | |
|     if (ipod->ipod_directory[ipod->ososimage].entryOffset != 0) {
 | |
|         /* We have a bootloader... */
 | |
|         length = ipod->ipod_directory[ipod->ososimage].entryOffset;
 | |
|     } else {
 | |
|         length = ipod->ipod_directory[ipod->ososimage].len;
 | |
|     }
 | |
| 
 | |
|     fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length);
 | |
| 
 | |
|     offset = ipod->fwoffset + ipod->ipod_directory[ipod->ososimage].devOffset;
 | |
|     i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1);
 | |
|     fprintf(stderr,"[INFO] Padding read from 0x%08x to 0x%08x bytes\n",
 | |
|             length,i);
 | |
| 
 | |
|     if (ipod->modelnum==62) {
 | |
|         /* 2nd Gen Nano has encrypted firmware, and we need to dump the
 | |
|            sector preceeding the image - it contains hashes */
 | |
|         offset -= 0x800;
 | |
|         length += 0x800;
 | |
|         i += 0x800;
 | |
|     }
 | |
| 
 | |
|     if (ipod_seek(ipod, offset)) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if ((n = ipod_read(ipod,i)) < 0) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (n < i) {
 | |
|         fprintf(stderr,"[ERR]  Short read - requested %d bytes, received %d\n",
 | |
|                 i,n);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666);
 | |
|     if (outfile < 0) {
 | |
|         fprintf(stderr,"[ERR]  Couldn't open file %s\n",filename);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (type == FILETYPE_DOT_IPOD) {
 | |
|         chksum = ipod->modelnum;
 | |
|         for (i = 0; i < length; i++) {
 | |
|             /* add 8 unsigned bits but keep a 32 bit sum */
 | |
|             chksum += ipod->sectorbuf[i];
 | |
|         }
 | |
| 
 | |
|         int2be(chksum,header);
 | |
|         memcpy(header+4, ipod->modelname,4);
 | |
| 
 | |
|         n = write(outfile,header,8);
 | |
|         if (n != 8) {
 | |
|             fprintf(stderr,"[ERR]  Write error - %d\n",n);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     n = write(outfile,ipod->sectorbuf,length);
 | |
|     if (n != length) {
 | |
|         fprintf(stderr,"[ERR]  Write error - %d\n",n);
 | |
|     }
 | |
|     close(outfile);
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int read_directory(struct ipod_t* ipod)
 | |
| {
 | |
|     ssize_t n;
 | |
|     int x;
 | |
|     unsigned char* p;
 | |
|     unsigned short version;
 | |
| 
 | |
|     ipod->nimages=0;
 | |
| 
 | |
|     /* Read firmware partition header (first 512 bytes of disk - but 
 | |
|        let's read a whole sector) */
 | |
| 
 | |
|     if (ipod_seek(ipod, ipod->start) < 0) { 
 | |
|         fprintf(stderr,"[ERR]  Seek to 0x%08x in read_directory() failed.\n",
 | |
|                        (unsigned int)(ipod->start));
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     n=ipod_read(ipod, ipod->sector_size);
 | |
|     if (n < 0) { 
 | |
|         fprintf(stderr,"[ERR]  ipod_read(ipod,0x%08x) failed in read_directory()\n", ipod->sector_size);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (memcmp(ipod->sectorbuf,apple_stop_sign,sizeof(apple_stop_sign))!=0) {
 | |
|         fprintf(stderr,"[ERR]  Firmware partition doesn't contain Apple copyright, aborting.\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (memcmp(ipod->sectorbuf+0x100,"]ih[",4)!=0) {
 | |
|         fprintf(stderr,"[ERR]  Bad firmware directory\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     version = le2ushort(ipod->sectorbuf+0x10a);
 | |
|     if ((version != 2) && (version != 3)) {
 | |
|         fprintf(stderr,"[ERR]  Unknown firmware format version %04x\n",
 | |
|                 version);
 | |
|     }
 | |
|     ipod->diroffset=le2int(ipod->sectorbuf+0x104) + 0x200;
 | |
| 
 | |
|     /* diroffset may not be sector-aligned */
 | |
|     x = ipod->diroffset % ipod->sector_size;
 | |
| 
 | |
|     /* Read directory */
 | |
|     if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { 
 | |
|         fprintf(stderr,"[ERR]  Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     n=ipod_read(ipod, ipod->sector_size);
 | |
|     if (n < 0) { 
 | |
|         fprintf(stderr,"[ERR]  Read of directory failed.\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     p = ipod->sectorbuf + x;
 | |
| 
 | |
|     /* A hack to detect 2nd gen Nanos - maybe there is a better way? */
 | |
|     if (p[0] == 0)
 | |
|     {
 | |
|         /* Adjust diroffset */
 | |
|         ipod->diroffset += ipod->sector_size - x;
 | |
| 
 | |
|         n=ipod_read(ipod, ipod->sector_size);
 | |
|         if (n < 0) { 
 | |
|             fprintf(stderr,"[ERR]  Read of directory failed.\n");
 | |
|             return -1;
 | |
|         }
 | |
|         p = ipod->sectorbuf;
 | |
|     }
 | |
| 
 | |
|     ipod->ososimage = -1;
 | |
|     while ((ipod->nimages < MAX_IMAGES) && (p < (ipod->sectorbuf + x + 400)) && 
 | |
|            ((memcmp(p,"!ATA",4)==0) || (memcmp(p,"DNAN",4)==0))) {
 | |
|         p+=4;
 | |
|         if (memcmp(p,"soso",4)==0) {
 | |
|             ipod->ipod_directory[ipod->nimages].ftype=FTYPE_OSOS;
 | |
|             ipod->ososimage = ipod->nimages;
 | |
|         } else if (memcmp(p,"crsr",4)==0) {
 | |
|             ipod->ipod_directory[ipod->nimages].ftype=FTYPE_RSRC;
 | |
|         } else if (memcmp(p,"dpua",4)==0) {
 | |
|             ipod->ipod_directory[ipod->nimages].ftype=FTYPE_AUPD;
 | |
|         } else if (memcmp(p,"kbso",4)==0) {
 | |
|             ipod->ipod_directory[ipod->nimages].ftype=FTYPE_OSBK;
 | |
|         } else if (memcmp(p,"ebih",4)==0) {
 | |
|             ipod->ipod_directory[ipod->nimages].ftype=FTYPE_HIBE;
 | |
|         } else {
 | |
|             fprintf(stderr,"[ERR]  Unknown image type %c%c%c%c\n",
 | |
|                            p[0],p[1],p[2],p[3]);
 | |
|         }
 | |
|         p+=4;
 | |
|         ipod->ipod_directory[ipod->nimages].id=le2int(p);
 | |
|         p+=4;
 | |
|         ipod->ipod_directory[ipod->nimages].devOffset=le2int(p);
 | |
|         p+=4;
 | |
|         ipod->ipod_directory[ipod->nimages].len=le2int(p);
 | |
|         p+=4;
 | |
|         ipod->ipod_directory[ipod->nimages].addr=le2int(p);
 | |
|         p+=4;
 | |
|         ipod->ipod_directory[ipod->nimages].entryOffset=le2int(p);
 | |
|         p+=4;
 | |
|         ipod->ipod_directory[ipod->nimages].chksum=le2int(p);
 | |
|         p+=4;
 | |
|         ipod->ipod_directory[ipod->nimages].vers=le2int(p);
 | |
|         p+=4;
 | |
|         ipod->ipod_directory[ipod->nimages].loadAddr=le2int(p);
 | |
|         p+=4;
 | |
|         ipod->nimages++;
 | |
|     }
 | |
| 
 | |
|     if (ipod->ososimage < 0) {
 | |
|         fprintf(stderr,"[ERR]  No OSOS image found.\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if ((ipod->nimages > 1) && (version==2)) {
 | |
|         /* The 3g firmware image doesn't appear to have a version, so
 | |
|            let's make one up... Note that this is never written back to the
 | |
|            ipod, so it's OK to do. */
 | |
| 
 | |
|         if (ipod->ipod_directory[ipod->ososimage].vers == 0) { ipod->ipod_directory[ipod->ososimage].vers = 3; }
 | |
| 
 | |
|         ipod->fwoffset = ipod->start;
 | |
|     } else {
 | |
|         ipod->fwoffset = ipod->start + ipod->sector_size;
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int list_images(struct ipod_t* ipod)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     if (ipod_verbose) {
 | |
|         printf("    Type         id  devOffset        len       addr entryOffset    chksum       vers   loadAddr   devOffset+len\n");
 | |
|         for (i = 0 ; i < ipod->nimages; i++) {
 | |
|             printf("%d - %s 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",i,
 | |
|                    ftypename[ipod->ipod_directory[i].ftype],
 | |
|                    ipod->ipod_directory[i].id,
 | |
|                    ipod->ipod_directory[i].devOffset,
 | |
|                    ipod->ipod_directory[i].len,
 | |
|                    ipod->ipod_directory[i].addr,
 | |
|                    ipod->ipod_directory[i].entryOffset,
 | |
|                    ipod->ipod_directory[i].chksum,
 | |
|                    ipod->ipod_directory[i].vers,
 | |
|                    ipod->ipod_directory[i].loadAddr,
 | |
|                    ipod->ipod_directory[i].devOffset+((ipod->ipod_directory[i].len+ipod->sector_size-1)&~(ipod->sector_size-1)));
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     printf("\n");
 | |
|     printf("Listing firmware partition contents:\n");
 | |
|     printf("\n");
 | |
| 
 | |
|     for (i = 0 ; i < ipod->nimages; i++) {
 | |
|          printf("Image %d:\n",i+1);
 | |
|          switch(ipod->ipod_directory[i].ftype) {
 | |
|              case FTYPE_OSOS:
 | |
|                  if (ipod->ipod_directory[i].entryOffset==0) {
 | |
|                      printf("    Main firmware - %d bytes\n",
 | |
|                        ipod->ipod_directory[i].len);
 | |
|                  } else {
 | |
|                      printf("    Main firmware - %d bytes\n",
 | |
|                        ipod->ipod_directory[i].entryOffset);
 | |
|                      printf("    Third-party bootloader - %d bytes\n",
 | |
|                        ipod->ipod_directory[i].len-ipod->ipod_directory[i].entryOffset);
 | |
|                  }
 | |
|                  break;
 | |
|              default:
 | |
|                      printf("    %s - %d bytes\n",
 | |
|                             ftypename[ipod->ipod_directory[i].ftype],
 | |
|                             ipod->ipod_directory[i].len);
 | |
|          }
 | |
|     }
 | |
|     printf("\n");
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int getmodel(struct ipod_t* ipod, int ipod_version)
 | |
| {
 | |
|     switch (ipod_version) {
 | |
|         case 0x01:
 | |
|             ipod->modelstr="1st or 2nd Generation";
 | |
|             ipod->modelnum = 19;
 | |
|             ipod->modelname = "1g2g";
 | |
|             ipod->targetname = "ipod1g2g";
 | |
| #ifdef WITH_BOOTOBJS
 | |
|             ipod->bootloader = ipod1g2g;
 | |
|             ipod->bootloader_len = LEN_ipod1g2g;
 | |
| #endif
 | |
|             break;
 | |
|         case 0x02:
 | |
|             ipod->modelstr="3rd Generation";
 | |
|             ipod->modelnum = 7;
 | |
|             ipod->modelname = "ip3g";
 | |
|             ipod->targetname = "ipod3g";
 | |
| #ifdef WITH_BOOTOBJS
 | |
|             ipod->bootloader = ipod3g;
 | |
|             ipod->bootloader_len = LEN_ipod3g;
 | |
| #endif
 | |
|             break;
 | |
|         case 0x40:
 | |
|             ipod->modelstr="1st Generation Mini";
 | |
|             ipod->modelnum = 9;
 | |
|             ipod->modelname = "mini";
 | |
|             ipod->targetname = "ipodmini1g";
 | |
| #ifdef WITH_BOOTOBJS
 | |
|             ipod->bootloader = ipodmini1g;
 | |
|             ipod->bootloader_len = LEN_ipodmini1g;
 | |
| #endif
 | |
|             break;
 | |
|         case 0x50:
 | |
|             ipod->modelstr="4th Generation";
 | |
|             ipod->modelnum = 8;
 | |
|             ipod->modelname = "ip4g";
 | |
|             ipod->targetname = "ipod4gray";
 | |
| #ifdef WITH_BOOTOBJS
 | |
|             ipod->bootloader = ipod4g;
 | |
|             ipod->bootloader_len = LEN_ipod4g;
 | |
| #endif
 | |
|             break;
 | |
|         case 0x60:
 | |
|             ipod->modelstr="Photo/Color";
 | |
|             ipod->modelnum = 3;
 | |
|             ipod->modelname = "ipco";
 | |
|             ipod->targetname = "ipodcolor";
 | |
| #ifdef WITH_BOOTOBJS
 | |
|             ipod->bootloader = ipodcolor;
 | |
|             ipod->bootloader_len = LEN_ipodcolor;
 | |
| #endif
 | |
|             break;
 | |
|         case 0x70:
 | |
|             ipod->modelstr="2nd Generation Mini";
 | |
|             ipod->modelnum = 11;
 | |
|             ipod->modelname = "mn2g";
 | |
|             ipod->targetname = "ipodmini2g";
 | |
| #ifdef WITH_BOOTOBJS
 | |
|             ipod->bootloader = ipodmini2g;
 | |
|             ipod->bootloader_len = LEN_ipodmini2g;
 | |
| #endif
 | |
|             break;
 | |
|         case 0xc0:
 | |
|             ipod->modelstr="1st Generation Nano";
 | |
|             ipod->modelnum = 4;
 | |
|             ipod->modelname = "nano";
 | |
|             ipod->targetname = "ipodnano1g";
 | |
| #ifdef WITH_BOOTOBJS
 | |
|             ipod->bootloader = ipodnano1g;
 | |
|             ipod->bootloader_len = LEN_ipodnano1g;
 | |
| #endif
 | |
|             break;
 | |
|         case 0xb0:
 | |
|             ipod->modelstr="Video (aka 5th Generation)";
 | |
|             ipod->modelnum = 5;
 | |
|             ipod->modelname = "ipvd";
 | |
|             ipod->targetname = "ipodvideo";
 | |
| #ifdef WITH_BOOTOBJS
 | |
|             ipod->bootloader = ipodvideo;
 | |
|             ipod->bootloader_len = LEN_ipodvideo;
 | |
| #endif
 | |
|             break;
 | |
|         case 0x100:
 | |
|             ipod->modelstr="2nd Generation Nano";
 | |
|             ipod->modelnum = 62;
 | |
|             ipod->modelname = "nn2x";
 | |
|             ipod->targetname = "ipodnano2g";
 | |
| #ifdef WITH_BOOTOBJS
 | |
|             ipod->bootloader = ipodnano2g;
 | |
|             ipod->bootloader_len = LEN_ipodnano2g;
 | |
| #endif
 | |
|             break;
 | |
|         default:
 | |
|             ipod->modelname = NULL;
 | |
|             ipod->modelnum = 0;
 | |
|             ipod->targetname = NULL;
 | |
| #ifdef WITH_BOOTOBJS
 | |
|             ipod->bootloader = NULL;
 | |
|             ipod->bootloader_len = 0;
 | |
| #endif
 | |
|             return -1;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* returns number of found ipods or -1 if no ipods found and permission
 | |
|  * for raw disc access was denied. */
 | |
| int ipod_scan(struct ipod_t* ipod)
 | |
| {
 | |
|     int i;
 | |
|     int n = 0;
 | |
|     int ipod_version;
 | |
|     struct ipod_t ipod_found;
 | |
|     int denied = 0;
 | |
|     int result;
 | |
| 
 | |
|     printf("[INFO] Scanning disk devices...\n");
 | |
| 
 | |
|     for (i = 0; i <= 25 ; i++) {
 | |
| #ifdef __WIN32__
 | |
|          sprintf(ipod->diskname,"\\\\.\\PhysicalDrive%d",i);
 | |
| #elif defined(linux) || defined (__linux)
 | |
|          sprintf(ipod->diskname,"/dev/sd%c",'a'+i);
 | |
| #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \
 | |
|       || defined(__bsdi__) || defined(__DragonFly__)
 | |
|          sprintf(ipod->diskname,"/dev/da%d",i);
 | |
| #elif defined(__APPLE__) && defined(__MACH__)
 | |
|          sprintf(ipod->diskname,"/dev/disk%d",i);
 | |
| #else
 | |
|     #error No disk paths defined for this platform
 | |
| #endif
 | |
|          if ((result = ipod_open(ipod, 1)) < 0) {
 | |
|              if(result == -2) {
 | |
|                  denied++;
 | |
|              }
 | |
|              ipod_close(ipod);
 | |
|              continue;
 | |
|          }
 | |
| 
 | |
|          if (read_partinfo(ipod,1) < 0) {
 | |
|              ipod_close(ipod);
 | |
|              continue;
 | |
|          }
 | |
| 
 | |
|          if ((ipod->pinfo[0].start==0) || (ipod->pinfo[0].type != 0)) {
 | |
|              ipod_close(ipod);
 | |
|              continue;
 | |
|          }
 | |
| 
 | |
|          if (read_directory(ipod) < 0) {
 | |
|              ipod_close(ipod);
 | |
|              continue;
 | |
|          }
 | |
| 
 | |
|          ipod_version=(ipod->ipod_directory[ipod->ososimage].vers>>8);
 | |
|          ipod->ramsize = 0;
 | |
| #ifdef __WIN32__
 | |
|          /* Windows requires the ipod in R/W mode for SCSI Inquiry.
 | |
|           * ipod_reopen_rw does unmount the player on OS X so do this on
 | |
|           * W32 only during scanning. */
 | |
|          ipod_reopen_rw(ipod);
 | |
| #endif
 | |
|          ipod_get_xmlinfo(ipod);
 | |
|          ipod_get_ramsize(ipod);
 | |
|          if (getmodel(ipod,ipod_version) < 0) {
 | |
|              ipod_close(ipod);
 | |
|              continue;
 | |
|          }
 | |
| 
 | |
| #ifdef __WIN32__
 | |
|          printf("[INFO] Ipod found - %s (\"%s\") - disk device %d\n", 
 | |
|              ipod->modelstr,ipod->macpod ? "macpod" : "winpod",i);
 | |
| #else
 | |
|          printf("[INFO] Ipod found - %s (\"%s\") - %s\n",
 | |
|              ipod->modelstr,ipod->macpod ? "macpod" : "winpod",ipod->diskname);
 | |
| #endif
 | |
|          n++;
 | |
|          /* save the complete ipod_t structure for match. The for loop might
 | |
|           * overwrite it, so we need to restore it later if only one found. */
 | |
|          memcpy(&ipod_found, ipod, sizeof(struct ipod_t));
 | |
|          ipod_close(ipod);
 | |
|     }
 | |
| 
 | |
|     if (n==1) {
 | |
|         /* restore the ipod_t structure, it might have been overwritten */
 | |
|         memcpy(ipod, &ipod_found, sizeof(struct ipod_t));
 | |
|     }
 | |
|     else if(n == 0 && denied) {
 | |
|         printf("[ERR]  FATAL: Permission denied on %d device(s) and no ipod detected.\n", denied);
 | |
| #ifdef __WIN32__
 | |
|         printf("[ERR]  You need to run this program with administrator priviledges!\n");
 | |
| #else
 | |
|         printf("[ERR]  You need permissions for raw disc access for this program to work!\n");
 | |
| #endif
 | |
|     }
 | |
|     return (n == 0 && denied) ? -1 : n;
 | |
| }
 | |
| 
 | |
| static void put_int32le(uint32_t x, unsigned char* p)
 | |
| {
 | |
|     p[0] = x & 0xff;
 | |
|     p[1] = (x >> 8) & 0xff;
 | |
|     p[2] = (x >> 16) & 0xff;
 | |
|     p[3] = (x >> 24) & 0xff;
 | |
| }
 | |
| 
 | |
| int write_dos_partition_table(struct ipod_t* ipod)
 | |
| {
 | |
|     unsigned char* p;
 | |
|     int i, n;
 | |
|     uint32_t type;
 | |
| 
 | |
|     /* Only support 512-byte sectors at the moment */
 | |
|     if ( ipod->sector_size != 512 )
 | |
|     {
 | |
|         fprintf(stderr,"[ERR]  Only ipods with 512 bytes per sector are supported.\n");
 | |
|         return -1;
 | |
|     }
 | |
|     if(ipod->sectorbuf == NULL) {
 | |
|         fprintf(stderr,"[ERR]  Buffer not initialized.");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     /* Firstly zero the entire MBR */
 | |
|     memset(ipod->sectorbuf, 0, ipod->sector_size);
 | |
| 
 | |
|     /* Now add the partition info */
 | |
|     for (i=0; i < 4 ; i++) 
 | |
|     {
 | |
|         p = ipod->sectorbuf + 0x1be + i*16;
 | |
| 
 | |
|         /* Ensure first partition is type 0, and second is 0xb */
 | |
|         if (i==0) { type = 0; }
 | |
|         else if (i==1) { type = 0xb; }
 | |
|         else { type = ipod->pinfo[i].type; }
 | |
| 
 | |
|         put_int32le(type, p + 4);
 | |
|         put_int32le(ipod->pinfo[i].start, p + 8);
 | |
|         put_int32le(ipod->pinfo[i].size, p + 12);
 | |
|     }
 | |
| 
 | |
|     /* Finally add the magic */
 | |
|     ipod->sectorbuf[0x1fe] = 0x55;
 | |
|     ipod->sectorbuf[0x1ff] = 0xaa;
 | |
| 
 | |
|     if (ipod_seek(ipod, 0) < 0) {
 | |
|         fprintf(stderr,"[ERR]  Seek failed writing MBR\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     /* Write MBR */
 | |
|     if ((n = ipod_write(ipod, ipod->sector_size)) < 0) {
 | |
|         perror("[ERR]  Write failed\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* Get the XML Device Information, as documented here:
 | |
| 
 | |
|    http://www.ipodlinux.org/wiki/Device_Information
 | |
| */
 | |
| 
 | |
| int ipod_get_xmlinfo(struct ipod_t* ipod)
 | |
| {
 | |
|     unsigned char hdr[255];
 | |
|     unsigned char buf[255];
 | |
|     char* p;
 | |
|     int psize;
 | |
|     int npages;
 | |
|     int i;
 | |
| 
 | |
|     if (ipod_scsi_inquiry(ipod, 0xc0, buf, sizeof(buf)) < 0)
 | |
|     {
 | |
|         fprintf(stderr,"[ERR]  Sending SCSI Command failed.\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     /* Reading directly into hdr[] causes problems (for an unknown reason) on 
 | |
|        win32 */
 | |
|     memcpy(hdr, buf, sizeof(hdr));
 | |
| 
 | |
|     npages = hdr[3];
 | |
| 
 | |
|     psize = npages * 0xf8; /* Hopefully this is enough. */
 | |
| 
 | |
|     ipod->xmlinfo = malloc(psize); 
 | |
|     ipod->xmlinfo_len = 0;
 | |
| 
 | |
|     if (ipod->xmlinfo == NULL) {
 | |
|         fprintf(stderr,"[ERR]  Could not allocate RAM for xmlinfo\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     p = ipod->xmlinfo;
 | |
|     
 | |
|     for (i=0; i < npages; i++) {
 | |
|         if (ipod_scsi_inquiry(ipod, hdr[i+4], buf, sizeof(buf)) < 0) {
 | |
|             fprintf(stderr,"[ERR]  Sending SCSI Command failed.\n");
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         if ((buf[3] + ipod->xmlinfo_len) > psize) {
 | |
|             fprintf(stderr,"[ERR]  Ran out of memory reading xmlinfo\n");
 | |
|             free(ipod->xmlinfo);
 | |
|             ipod->xmlinfo = NULL;
 | |
|             ipod->xmlinfo_len = 0;
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         memcpy(p, buf + 4, buf[3]);
 | |
|         p += buf[3];
 | |
|         ipod->xmlinfo_len += buf[3];        
 | |
|     }
 | |
| 
 | |
|     /* NULL-terminate the XML info */
 | |
|     *p = 0;
 | |
| 
 | |
|     fprintf(stderr,"[INFO] Read XML info (%d bytes)\n",ipod->xmlinfo_len);
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| void ipod_get_ramsize(struct ipod_t* ipod)
 | |
| {
 | |
|     const char needle[] = "<key>RAM</key>\n<integer>";
 | |
|     char* p;
 | |
| 
 | |
|     if (ipod->xmlinfo == NULL)
 | |
|         return;
 | |
| 
 | |
|     p = strstr(ipod->xmlinfo, needle);
 | |
| 
 | |
|     if (p) {
 | |
|         ipod->ramsize = atoi(p + sizeof(needle) - 1);
 | |
|     }
 | |
| }
 | |
| 
 |