A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 632 lines 18 kB view raw
1/* 2 * make_fw.c - iPodLinux loader installer 3 * 4 * Copyright (C) 2003 Daniel Palffy 5 * 6 * based on Bernard Leach's patch_fw.c 7 * Copyright (C) 2002 Bernard Leach 8 * big endian support added 2003 Steven Lucy 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software Foundation, 22 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 23 */ 24 25#include <stdio.h> 26#include <stdlib.h> 27#include <string.h> 28#include <errno.h> 29#include <unistd.h> 30 31#ifdef __WIN32__ 32#include "getopt.c" 33#endif 34 35#define TBL 0x4200 36 37int sectorsize = 512; 38 39/* Some firmwares have padding becore the actual image. */ 40#define IMAGE_PADDING ((fw_version == 3) ? sectorsize : 0) 41#define FIRST_OFFSET (TBL + ((sectorsize == 0x200) ? 0x200 : 0x600) + IMAGE_PADDING) 42 43int be; 44unsigned short fw_version = 2; 45 46typedef struct _image { 47 char type[4]; /* '' */ 48 unsigned id; /* */ 49 char pad1[4]; /* 0000 0000 */ 50 unsigned devOffset; /* byte offset of start of image code */ 51 unsigned len; /* length in bytes of image */ 52 unsigned addr; /* load address */ 53 unsigned entryOffset; /* execution start within image */ 54 unsigned chksum; /* checksum for image */ 55 unsigned vers; /* image version */ 56 unsigned loadAddr; /* load address for image */ 57} image_t; 58 59static char *apple_copyright = "{{~~ /-----\\ {{~~ / \\ {{~~| | {{~~| S T O P | {{~~| | {{~~ \\ / {{~~ \\-----/ Copyright(C) 2001 Apple Computer, Inc.---------------------------------------------------------------------------------------------------------"; 60 61unsigned 62switch_32(unsigned l) 63{ 64 if (be) 65 return ((l & 0xff) << 24) 66 | ((l & 0xff00) << 8) 67 | ((l & 0xff0000) >> 8) 68 | ((l & 0xff000000) >> 24); 69 return l; 70} 71 72unsigned short 73switch_16(unsigned short s) 74{ 75 if (be) { 76 return ((s & 0xff) << 8) | ((s & 0xff00) >> 8); 77 } else { 78 return s; 79 } 80} 81 82void 83switch_endian(image_t *image) 84{ 85 if (be) { 86 image->id = switch_32(image->id); 87 image->devOffset = switch_32(image->devOffset); 88 image->len = switch_32(image->len); 89 image->addr = switch_32(image->addr); 90 image->entryOffset = switch_32(image->entryOffset); 91 image->chksum = switch_32(image->chksum); 92 image->vers = switch_32(image->vers); 93 image->loadAddr = switch_32(image->loadAddr); 94 } 95} 96 97void 98print_image(image_t *image, const char *head) 99{ 100 printf("%stype: '%s' id: 0x%08x len: 0x%x addr: 0x%08x vers: 0x%x\n", 101 head, image->type, image->id, image->len, image->addr, image->vers); 102 printf(" devOffset: 0x%08X entryOffset: 0x%08X " 103 "loadAddr: 0x%08X chksum: 0x%08X\n", 104 image->devOffset, image->entryOffset, 105 image->loadAddr, image->chksum); 106} 107 108void 109usage() 110{ 111 printf("Usage: ipod_fw [-h]\n" 112 " ipod_fw [-v] -o outfile -e img_no fw_file\n" 113 " ipod_fw [-v] -g gen [-r rev] [-s size] -o outfile [-i img_from_-e]* [-l raw_img]* ldr_img\n\n" 114 " -g: set target ipod generation, valid options are: 1g, 2g, 3g\n" 115 " 4g, 5g, scroll, touch, dock, mini, photo, color, nano and video\n" 116 " -e: extract the image at img_no in boot table to outfile\n" 117 " fw_file is an original firmware image\n" 118 " the original firmware has the sw at 0, and a flash updater at 1\n" 119 " -i|-l: create new image to outfile\n" 120 " up to 5 images, any of -i or -l allowed\n" 121 " -i: image extracted with -e, load and entry address preserved\n" 122 " -l: raw image, loaded to 0x28000000, entry at 0x00000000\n" 123 " -r: set master revision to rev (for example 210 for 2.10)\n" 124 " may be needed if newest -e img is not the same as the flash rev\n" 125 " ldr_img is the iPodLinux loader binary.\n" 126 " first image is loaded by default, 2., 3., 4. or 5. loaded if\n" 127 " rew, menu, play or ff is hold while booting\n" 128 " -n: do not add a boot loader (ldr_img)\n" 129 " -s: set sector size in bytes (default is 512)\n\n" 130 " -v: verbose\n\n" 131 " This program is used to create a bootable ipod image.\n\n"); 132} 133 134/* read len bytes from the beginning of s, 135 * calculate checksum, and 136 * if (d) copy to current offset in d */ 137unsigned 138copysum(FILE *s, FILE *d, unsigned len, unsigned off) 139{ 140 unsigned char temp; 141 unsigned sum = 0; 142 unsigned i; 143 if (fseek(s, off, SEEK_SET) == -1) { 144 fprintf(stderr, "fseek failed: %s\n", strerror(errno)); 145 return -1; 146 } 147 for (i = 0; i < len; i++) { 148 if (fread(&temp, 1, 1, s) != 1) { 149 fprintf(stderr, "Failure in copysum: "); 150 if (ferror(s)) 151 fprintf(stderr, "fread error: %s\n", strerror(errno)); 152 else if (feof(s)) 153 fprintf(stderr, "fread length 1 at offset %d hit EOF.\n", off); 154 return -1; 155 } 156 sum = sum + (temp & 0xff); 157 if (d) 158 if (fwrite(&temp, 1, 1, d) != 1) { 159 fprintf(stderr, "Failure in copysum; fwrite error: %s\n", strerror(errno)); 160 return -1; 161 } 162 } 163 return sum; 164} 165 166/* load the boot entry from 167 * boot table at offset, 168 * entry number entry 169 * file fw */ 170int 171load_entry(image_t *image, FILE *fw, unsigned offset, int entry) 172{ 173 if (fseek(fw, offset + entry * sizeof(image_t), SEEK_SET) == -1) { 174 fprintf(stderr, "fseek failed: %s\n", strerror(errno)); 175 return -1; 176 } 177 if (fread(image, sizeof(image_t), 1, fw) != 1) { 178 if (ferror(fw)) 179 fprintf(stderr, "fread error (%s), ", strerror(errno)); 180 else if (feof(fw)) 181 fprintf(stderr, "fread length %lu at offset %lu hit EOF, ", 182 sizeof(image_t), offset + entry * sizeof(image_t)); 183 fprintf(stderr, "unable to load boot entry.\n"); 184 return -1; 185 } 186 switch_endian(image); 187 188 /* If we find an "osos" image with devOffset 0x4800, we have 2048-byte 189 sectors. This isn't 100% future-proof, but works as of December 2006. 190 We display this information so users can spot any false-positives that 191 may occur in the future (although this is unlikely). */ 192 if ((image->id==0x6f736f73) && (image->devOffset==0x4800)) { 193 sectorsize=2048; 194 fprintf(stderr,"Detected 2048-byte sectors\n"); 195 } 196 return 0; 197} 198 199/* store the boot entry to 200 * boot table at offset, 201 * entry number entry 202 * file fw */ 203int 204write_entry(image_t *image, FILE *fw, unsigned offset, int entry) 205{ 206 if (fseek(fw, offset + entry * sizeof(image_t), SEEK_SET) == -1) { 207 fprintf(stderr, "fseek failed: %s\n", strerror(errno)); 208 return -1; 209 } 210 switch_endian(image); 211 if (fwrite(image, sizeof(image_t), 1, fw) != 1) { 212 fprintf(stderr, "fwrite error (%s), unable to write boot entry\n", strerror(errno)); 213 switch_endian(image); 214 return -1; 215 } 216 switch_endian(image); 217 return 0; 218} 219 220/* extract a single image from the fw 221 * the first 40 bytes contain a boot table entry, 222 * padded to one block (512 bytes */ 223int 224extract(FILE *f, int idx, FILE *out) 225{ 226 image_t *image; 227 unsigned char buf[512]; 228 unsigned off; 229 230 fseek(f, 0x100 + 10, SEEK_SET); 231 fread(&fw_version, sizeof(fw_version), 1, f); 232 fw_version = switch_16(fw_version); 233 234 image = (image_t *)buf; 235 236 /* We need to detect sector size, so always load image 0 directory 237 entry first */ 238 if (load_entry(image, f, TBL, 0) == -1) 239 return -1; 240 241 if (idx > 0) { /* Now read the real image (if it isn't 0) */ 242 if (load_entry(image, f, TBL, idx) == -1) 243 return -1; 244 } 245 246 off = image->devOffset + IMAGE_PADDING; 247 248 if (fseek(f, off, SEEK_SET) == -1) { 249 fprintf(stderr, "fseek failed: %s\n", strerror(errno)); 250 return -1; 251 } 252 if (write_entry(image, out, 0x0, 0) == -1) 253 return -1; 254 if (fseek(out, 512, SEEK_SET) == -1) { 255 fprintf(stderr, "fseek failed: %s\n", strerror(errno)); 256 return -1; 257 } 258 if (copysum(f, out, image->len, off) == -1) 259 return -1; 260 261 return 0; 262} 263 264/* list all images */ 265int 266listall(FILE *f) 267{ 268 image_t *image; 269 unsigned char buf[512]; 270 int idx; 271 272 fseek(f, 0x100 + 10, SEEK_SET); 273 fread(&fw_version, sizeof(fw_version), 1, f); 274 fw_version = switch_16(fw_version); 275 276 image = (image_t *)buf; 277 278 idx = 0; 279 while (idx < 20) { 280 char prefix[32]; 281 if (load_entry(image, f, TBL, idx) < 0) return -1; 282 if (!image->id) break; 283 sprintf (prefix, "%2d: ", idx); 284 print_image (image, prefix); 285 ++idx; 286 } 287 288 return 0; 289} 290 291/* return the size of f */ 292unsigned 293lengthof(FILE *f) 294{ 295 unsigned ret; 296 297 if (fseek(f, 0, SEEK_END) == -1) { 298 fprintf(stderr, "fseek failed: %s\n", strerror(errno)); 299 return -1; 300 } 301 if ((ret = ftell(f)) == -1) { 302 fprintf(stderr, "ftell failed: %s\n", strerror(errno)); 303 return -1; 304 } 305 return ret; 306} 307 308void 309test_endian(void) 310{ 311 char ch[4] = { '\0', '\1', '\2', '\3' }; 312 unsigned i = 0x00010203; 313 314 if (*((int *)ch) == i) 315 be = 1; 316 else 317 be = 0; 318 return; 319} 320 321int 322main(int argc, char **argv) 323{ 324 int c; 325 int verbose = 0, i, ext = 0; 326 FILE *in = NULL, *out = NULL; 327 char gen_set = 0; 328 image_t images[5]; 329 image_t image = { 330 { '!', 'A', 'T', 'A' }, // magic 331 0x6f736f73, // id 332 { '\0', '\0', '\0', '\0' }, // pad 333 0x4400, // devOffset 334 0, // len 335 0x28000000, // addr 336 0, // entry 337 0, // chksum 338 0, // vers 339 0xffffffff // loadAddr 340 }; 341 int images_done = 0; 342 unsigned version = 0, offset = 0, len = 0; 343 int needs_rcsc = 0; 344 int no_boot = 0; 345 346 test_endian(); 347 348 /* parse options */ 349 opterr = 0; 350 while ((c = getopt(argc, argv, "3hve:o:i:l:nr:g:s:")) != -1) 351 switch (c) { 352 case 'h': 353 if (verbose || in || out || images_done || ext) { 354 fprintf(stderr, 355 "-[?h] is exclusive with other arguments\n"); 356 usage(); 357 return 1; 358 } 359 usage(); 360 return 0; 361 case 'v': 362 if (verbose++) 363 fprintf(stderr, "Warning: multiple -v options specified\n"); 364 break; 365 case '3': 366 gen_set = 1; 367 fw_version = 3; 368 image.addr = 0x10000000; 369 break; 370 case 'g': 371 gen_set = 1; 372 if ((strcasecmp(optarg, "4g") == 0) || 373 (strcasecmp(optarg, "mini") == 0) || 374 (strcasecmp(optarg, "nano") == 0) || 375 (strcasecmp(optarg, "photo") == 0) || 376 (strcasecmp(optarg, "color") == 0) || 377 (strcasecmp(optarg, "video") == 0) || 378 (strcasecmp(optarg, "5g") == 0)) { 379 fw_version = 3; 380 image.addr = 0x10000000; 381 if ((strcasecmp(optarg, "5g") == 0) || (strcasecmp(optarg, "video") == 0)) { 382 needs_rcsc = 1; 383 } 384 } 385 else if ((strcasecmp(optarg, "1g") != 0) && 386 (strcasecmp(optarg, "2g") != 0) && 387 (strcasecmp(optarg, "3g") != 0) && 388 (strcasecmp(optarg, "scroll") != 0) && 389 (strcasecmp(optarg, "touch") != 0) && 390 (strcasecmp(optarg, "dock") != 0)) { 391 fprintf(stderr, "%s: bad gen. Valid options are: 1g, 2g," 392 " 3g, 4g, 5g, scroll, touch, dock, mini, nano," 393 " photo, color, and video\n", optarg); 394 return 1; 395 } 396 break; 397 case 'o': 398 if (out) { 399 fprintf(stderr, "output already opened\n"); 400 usage(); 401 return 1; 402 } 403 if ((out = fopen(optarg, "wb+")) == NULL) { 404 fprintf(stderr, "Cannot open output file %s\n", optarg); 405 return 1; 406 } 407 break; 408 case 'e': 409 if (!out || images_done || ext) { 410 usage(); 411 return 1; 412 } 413 414 ext = atoi(optarg) + 1; 415 break; 416 case 'i': 417 if (!out || ext) { 418 usage(); 419 return 1; 420 } 421 if (images_done == 5) { 422 fprintf(stderr, "Only 5 images supported\n"); 423 return 1; 424 } 425 if ((in = fopen(optarg, "rb")) == NULL) { 426 fprintf(stderr, "Cannot open firmware image file %s\n", optarg); 427 return 1; 428 } 429 if (load_entry(images + images_done, in, 0, 0) == -1) 430 return 1; 431 if (!offset) offset = FIRST_OFFSET; 432 else offset = (offset + 0x1ff) & ~0x1ff; 433 images[images_done].devOffset = offset; 434 if (fseek(out, offset, SEEK_SET) == -1) { 435 fprintf(stderr, "fseek failed: %s\n", strerror(errno)); 436 return 1; 437 } 438 if ((images[images_done].chksum = copysum(in, out, 439 images[images_done].len, 0x200)) == -1) 440 return 1; 441 offset += images[images_done].len; 442 if (verbose) print_image(images + images_done, "Apple image added: "); 443 images_done++; 444 fclose(in); 445 break; 446 case 'l': 447 if (!out || ext) { 448 usage(); 449 return 1; 450 } 451 if (images_done == 5) { 452 fprintf(stderr, "Only 5 images supported\n"); 453 return 1; 454 } 455 if ((in = fopen(optarg, "rb")) == NULL) { 456 fprintf(stderr, "Cannot open linux image file %s\n", optarg); 457 return 1; 458 } 459 if (!offset) offset = FIRST_OFFSET; 460 else offset = (offset + 0x1ff) & ~0x1ff; 461 images[images_done] = image; 462 images[images_done].devOffset = offset; 463 if ((images[images_done].len = lengthof(in)) == -1) 464 return 1; 465 if (fseek(out, offset, SEEK_SET) == -1) { 466 fprintf(stderr, "fseek failed: %s\n", strerror(errno)); 467 return 1; 468 } 469 if ((images[images_done].chksum = copysum(in, out, 470 images[images_done].len, 0)) == -1) 471 return 1; 472 offset += images[images_done].len; 473 if (verbose) print_image(images + images_done, "Linux image added: "); 474 images_done++; 475 fclose(in); 476 break; 477 case 'n': 478 no_boot = 1; 479 break; 480 case 'r': 481 if (ext) { 482 usage(); 483 return 1; 484 } 485 version = strtol(optarg, NULL, 16); 486 break; 487 case 's': 488 sectorsize = atoi(optarg); 489 break; 490 case '?': 491 fprintf(stderr, "invalid option -%c specified\n", optopt); 492 usage(); 493 return 1; 494 case ':': 495 fprintf(stderr, "option -%c needs an argument\n", optopt); 496 usage(); 497 return 1; 498 } 499 500 if ((argc - optind != 1) && !no_boot) { 501 usage(); 502 return 1; 503 } 504 505 if (ext) { 506 if ((in = fopen(argv[optind], "rb")) == NULL) { 507 fprintf(stderr, "Cannot open firmware image file %s\n", argv[optind]); 508 return 1; 509 } 510 if (extract(in, ext-1, out) == -1) return 1; 511 fclose(in); fclose(out); 512 return 0; 513 } 514 else if (!gen_set) { 515 if ((in = fopen(argv[optind], "rb")) != NULL) { 516 // just list all available entries 517 listall (in); 518 fclose(in); 519 return 0; 520 } 521 usage(); 522 return 1; 523 } 524 525 printf("Generating firmware image compatible with "); 526 if (fw_version == 3) { 527 if (needs_rcsc) { 528 printf("iPod video\n"); 529 } else { 530 printf("iPod mini, 4g and iPod photo/color...\n"); 531 } 532 } else { 533 printf("1g, 2g and 3g iPods...\n"); 534 } 535 536 if (!images_done) { 537 fprintf(stderr, "no images specified!\n"); 538 return 1; 539 } 540 if (!no_boot) 541 { 542 if ((in = fopen(argv[optind], "rb")) == NULL) { 543 fprintf(stderr, "Cannot open loader image file %s\n", argv[optind]); 544 return 1; 545 } 546 offset = (offset + 0x1ff) & ~0x1ff; 547 if ((len = lengthof(in)) == -1) 548 return 1; 549 if (fseek(out, offset, SEEK_SET) == -1) { 550 fprintf(stderr, "fseek failed: %s\n", strerror(errno)); 551 return 1; 552 } 553 if (copysum(in, out, len, 0) == -1) 554 return 1; 555 } 556 for (i=0; i < images_done; i++) { 557 if (images[i].vers > image.vers) image.vers = images[i].vers; 558 if (write_entry(images+i, out, offset+0x0100, i) == -1) 559 return 1; 560 } 561 if (version) image.vers = version; 562 image.len = offset + len - FIRST_OFFSET; 563 if (!no_boot) 564 { 565 image.entryOffset = offset - FIRST_OFFSET; 566 } 567 image.devOffset = (sectorsize==512 ? 0x4400 : 0x4800); 568 if ((image.chksum = copysum(out, NULL, image.len, FIRST_OFFSET)) == -1) 569 return 1; 570 if (fseek(out, 0x0, SEEK_SET) == -1) { 571 fprintf(stderr, "fseek failed: %s\n", strerror(errno)); 572 return 1; 573 } 574 if (fwrite(apple_copyright, 0x100, 1, out) != 1) { 575 fprintf(stderr, "fwrite error (%s) while writing copyright\n", strerror(errno)); 576 return 1; 577 } 578 version = switch_32(0x5b68695d); /* magic */ 579 if (fwrite(&version, 4, 1, out) != 1) { 580 fprintf(stderr, "fwrite error (%s) while writing version magic\n", strerror(errno)); 581 return 1; 582 } 583 version = switch_32(0x00004000); /* magic */ 584 if (fwrite(&version, 4, 1, out) != 1) { 585 fprintf(stderr, "fwrite error (%s) while writing version magic\n", strerror(errno)); 586 return 1; 587 } 588 if (fw_version == 3) { 589 version = switch_32(0x0003010c); /* magic */ 590 } else { 591 version = switch_32(0x0002010c); /* magic */ 592 } 593 594 if (fwrite(&version, 4, 1, out) != 1) { 595 fprintf(stderr, "fwrite error (%s) while writing version magic\n", strerror(errno)); 596 return 1; 597 } 598 if (write_entry(&image, out, TBL, 0) == -1) 599 return 1; 600 if (verbose) print_image(&image, "Master image: "); 601 602 if (needs_rcsc) { 603 image_t rsrc; 604 605 if ((in = fopen("apple_sw_5g_rcsc.bin", "rb")) == NULL) { 606 fprintf(stderr, "Cannot open firmware image file %s\n", "apple_sw_5g_rcsc.bin"); 607 return 1; 608 } 609 if (load_entry(&rsrc, in, 0, 0) == -1) { 610 return 1; 611 } 612 rsrc.devOffset = image.devOffset + image.len; 613 rsrc.devOffset = ((rsrc.devOffset + 0x1ff) & ~0x1ff) + 0x200; 614 if (fseek(out, rsrc.devOffset + IMAGE_PADDING, SEEK_SET) == -1) { 615 fprintf(stderr, "fseek failed: %s\n", strerror(errno)); 616 return 1; 617 } 618 if ((rsrc.chksum = copysum(in, out, rsrc.len, 0x200)) == -1) { 619 return 1; 620 } 621 fclose(in); 622 623 if (write_entry(&rsrc, out, TBL, 1) == -1) { 624 return 1; 625 } 626 627 if (verbose) print_image(&rsrc, "rsrc image: "); 628 } 629 630 return 0; 631} 632