A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
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