1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002 - 2007 by Björn Stenberg
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
28 int iaudio_encode(char *iname
, char *oname
, char *idstring
);
29 int ipod_encode(char *iname
, char *oname
, int fw_ver
, bool fake_rsrc
);
33 ARCHOS_PLAYER
, /* and V1 recorder */
42 0x32000, /* ARCHOS_PLAYER */
43 0x64000, /* ARCHOS_V2RECORDER */
44 0x64000, /* ARCHOS_FMRECORDER */
45 0x64000, /* ARCHOS_ONDIO_SP */
46 0x64000 /* ARCHOS_ONDIO_FM */
49 void short2le(unsigned short val
, unsigned char* addr
)
52 addr
[1] = (val
>> 8) & 0xff;
55 unsigned int le2int(unsigned char* buf
)
57 unsigned int res
= (buf
[3] << 24) | (buf
[2] << 16) | (buf
[1] << 8) | buf
[0];
62 void int2le(unsigned int val
, unsigned char* addr
)
65 addr
[1] = (val
>> 8) & 0xff;
66 addr
[2] = (val
>> 16) & 0xff;
67 addr
[3] = (val
>> 24) & 0xff;
70 void int2be(unsigned int val
, unsigned char* addr
)
72 addr
[0] = (val
>> 24) & 0xff;
73 addr
[1] = (val
>> 16) & 0xff;
74 addr
[2] = (val
>> 8) & 0xff;
80 printf("usage: scramble [options] <input file> <output file> [xor string]\n");
82 "\t-fm Archos FM recorder format\n"
83 "\t-v2 Archos V2 recorder format\n"
84 "\t-ofm Archos Ondio FM recorder format\n"
85 "\t-osp Archos Ondio SP format\n"
86 "\t-neo SSI Neo format\n"
87 "\t-mm=X Archos Multimedia format (X values: A=JBMM, B=AV1xx, C=AV3xx)\n"
88 "\t-iriver iRiver format\n"
89 "\t-iaudiox5 iAudio X5 format\n"
90 "\t-iaudiox5v iAudio X5V format\n"
91 "\t-iaudiom5 iAudio M5 format\n"
92 "\t-ipod3g ipod firmware partition format (3rd Gen)\n"
93 "\t-ipod4g ipod firmware partition format (4th Gen, Mini, Nano, Photo/Color)\n"
94 "\t-ipod5g ipod firmware partition format (5th Gen - aka Video)\n"
95 "\t-gigabeat Toshiba Gigabeat format\n"
96 "\t-mi4v2 PortalPlayer .mi4 format (revision 010201)\n"
97 "\t-mi4v3 PortalPlayer .mi4 format (revision 010301)\n"
98 "\t-mi4r Sandisk Rhapsody .mi4 format\n"
99 "\t All mi4 options take two optional arguments:\n"
100 "\t -model=XXXX where XXXX is the model id string\n"
101 "\t -type=XXXX where XXXX is a string indicating the \n"
102 "\t type of binary, eg. RBOS, RBBL\n"
103 "\t-add=X Rockbox generic \"add-up\" checksum format\n"
104 "\t (X values: h100, h120, h140, h300, ipco, nano, ipvd, mn2g\n"
105 "\t ip3g, ip4g, mini, iax5, h10, h10_5gb, tpj2, e200)\n"
106 "\nNo option results in Archos standard player/recorder format.\n");
111 int main (int argc
, char** argv
)
113 unsigned long length
,i
,slen
;
114 unsigned char *inbuf
,*outbuf
;
115 unsigned short crc
=0;
116 unsigned long chksum
=0; /* 32 bit checksum */
117 unsigned char header
[24];
118 char *iname
= argv
[1];
119 char *oname
= argv
[2];
124 unsigned long modelnum
;
127 enum { none
, scramble
, xor, add
} method
= scramble
;
129 model_id
= ARCHOS_PLAYER
;
135 if(!strcmp(argv
[1], "-fm")) {
140 model_id
= ARCHOS_FMRECORDER
;
143 else if(!strcmp(argv
[1], "-v2")) {
148 model_id
= ARCHOS_V2RECORDER
;
151 else if(!strcmp(argv
[1], "-ofm")) {
156 model_id
= ARCHOS_ONDIO_FM
;
159 else if(!strcmp(argv
[1], "-osp")) {
164 model_id
= ARCHOS_ONDIO_SP
;
167 else if(!strcmp(argv
[1], "-neo")) {
173 else if(!strncmp(argv
[1], "-mm=", 4)) {
178 version
= argv
[1][4];
182 printf("Multimedia needs an xor string\n");
186 else if(!strncmp(argv
[1], "-add=", 5)) {
191 if(!strcmp(&argv
[1][5], "h120"))
193 else if(!strcmp(&argv
[1][5], "h140"))
194 modelnum
= 0; /* the same as the h120 */
195 else if(!strcmp(&argv
[1][5], "h100"))
197 else if(!strcmp(&argv
[1][5], "h300"))
199 else if(!strcmp(&argv
[1][5], "ipco"))
201 else if(!strcmp(&argv
[1][5], "nano"))
203 else if(!strcmp(&argv
[1][5], "ipvd"))
205 else if(!strcmp(&argv
[1][5], "fp7x"))
207 else if(!strcmp(&argv
[1][5], "ip3g"))
209 else if(!strcmp(&argv
[1][5], "ip4g"))
211 else if(!strcmp(&argv
[1][5], "mini"))
213 else if(!strcmp(&argv
[1][5], "iax5"))
215 else if(!strcmp(&argv
[1][5], "mn2g"))
217 else if(!strcmp(&argv
[1][5], "h10"))
219 else if(!strcmp(&argv
[1][5], "h10_5gb"))
221 else if(!strcmp(&argv
[1][5], "tpj2"))
223 else if(!strcmp(&argv
[1][5], "e200"))
225 else if(!strcmp(&argv
[1][5], "iam5"))
228 fprintf(stderr
, "unsupported model: %s\n", &argv
[1][5]);
231 /* we store a 4-letter model name too, for humans */
232 strcpy(modelname
, &argv
[1][5]);
233 chksum
= modelnum
; /* start checksum calcs with this */
236 else if(!strcmp(argv
[1], "-iriver")) {
237 /* iRiver code dealt with in the iriver.c code */
240 iriver_encode(iname
, oname
, FALSE
);
243 else if(!strcmp(argv
[1], "-gigabeat")) {
244 /* iRiver code dealt with in the iriver.c code */
247 gigabeat_code(iname
, oname
);
250 else if(!strcmp(argv
[1], "-iaudiox5")) {
253 return iaudio_encode(iname
, oname
, "COWON_X5_FW");
255 else if(!strcmp(argv
[1], "-iaudiox5v")) {
258 return iaudio_encode(iname
, oname
, "COWON_X5V_FW");
260 else if(!strcmp(argv
[1], "-iaudiom5")) {
263 return iaudio_encode(iname
, oname
, "COWON_M5_FW");
265 else if(!strcmp(argv
[1], "-ipod3g")) {
268 return ipod_encode(iname
, oname
, 2, false); /* Firmware image v2 */
270 else if(!strcmp(argv
[1], "-ipod4g")) {
273 return ipod_encode(iname
, oname
, 3, false); /* Firmware image v3 */
275 else if(!strcmp(argv
[1], "-ipod5g")) {
278 return ipod_encode(iname
, oname
, 3, true); /* Firmware image v3 */
280 else if(!strncmp(argv
[1], "-mi4", 4)) {
286 if(!strcmp(&argv
[1][4], "v2")) {
287 mi4magic
= MI4_MAGIC_DEFAULT
;
288 version
= 0x00010201;
290 else if(!strcmp(&argv
[1][4], "v3")) {
291 mi4magic
= MI4_MAGIC_DEFAULT
;
292 version
= 0x00010301;
294 else if(!strcmp(&argv
[1][4], "r")) {
295 mi4magic
= MI4_MAGIC_R
;
296 version
= 0x00010301;
299 printf( "Invalid mi4 version: %s\n", &argv
[1][4]);
306 if(!strncmp(argv
[2], "-model=", 7)) {
309 strncpy(model
, &argv
[2][7], 4);
311 if(!strncmp(argv
[3], "-type=", 6)) {
314 strncpy(type
, &argv
[3][6], 4);
318 return mi4_encode(iname
, oname
, version
, mi4magic
, model
, type
);
322 file
= fopen(iname
,"rb");
327 fseek(file
,0,SEEK_END
);
328 length
= ftell(file
);
329 length
= (length
+ 3) & ~3; /* Round up to nearest 4 byte boundary */
331 if ((method
== scramble
) &&
332 ((length
+ headerlen
) >= size_limit
[model_id
])) {
333 printf("error: firmware image is %d bytes while max size is %d!\n",
335 size_limit
[model_id
]);
340 fseek(file
,0,SEEK_SET
);
341 inbuf
= malloc(length
);
343 outbuf
= malloc(length
*2);
344 else if(method
== add
)
345 outbuf
= malloc(length
+ 8);
347 outbuf
= malloc(length
);
348 if ( !inbuf
|| !outbuf
) {
349 printf("out of memory!\n");
353 /* zero-fill the last 4 bytes to make sure there's no rubbish there
354 when we write the size-aligned file later */
355 memset(outbuf
+length
-4, 0, 4);
359 i
=fread(inbuf
,1,length
,file
);
369 for (i
= 0; i
< length
; i
++) {
370 /* add 8 unsigned bits but keep a 32 bit sum */
376 for (i
= 0; i
< length
; i
++) {
377 unsigned long addr
= (i
>> 2) + ((i
% 4) * slen
);
378 unsigned char data
= inbuf
[i
];
379 data
= ~((data
<< 1) | ((data
>> 7) & 1)); /* poor man's ROL */
387 for (i
=0; i
<length
; i
++) {
389 outbuf
[slen
++] = 0xff; /* all data is uncompressed */
390 outbuf
[slen
++] = inbuf
[i
];
396 /* calculate checksum */
397 for (i
=0;i
<length
;i
++)
401 memset(header
, 0, sizeof header
);
406 int2be(chksum
, header
); /* checksum, big-endian */
407 memcpy(&header
[4], modelname
, 4); /* 4 bytes model name */
408 memcpy(outbuf
, inbuf
, length
); /* the input buffer to output*/
413 if (headerlen
== 6) {
414 int2be(length
, header
);
415 header
[4] = (crc
>> 8) & 0xff;
416 header
[5] = crc
& 0xff;
422 header
[3] = 0xff; /* ??? */
424 header
[6] = (crc
>> 8) & 0xff;
425 header
[7] = crc
& 0xff;
427 header
[11] = version
;
429 header
[15] = headerlen
; /* really? */
431 int2be(length
, &header
[20]);
437 int xorlen
= strlen(xorstring
);
440 for (i
=0; i
<slen
; i
++)
441 outbuf
[i
] ^= xorstring
[i
& (xorlen
-1)];
443 /* calculate checksum */
444 for (i
=0; i
<slen
; i
++)
447 header
[0] = header
[2] = 'Z';
448 header
[1] = header
[3] = version
;
449 int2le(length
, &header
[4]);
450 int2le(slen
, &header
[8]);
451 int2le(crc
, &header
[12]);
456 #define MY_FIRMWARE_TYPE "Rockbox"
457 #define MY_HEADER_VERSION 1
459 strncpy((char *)header
, MY_FIRMWARE_TYPE
,9);
460 header
[9]='\0'; /*shouldn't have to, but to be SURE */
461 header
[10]=MY_HEADER_VERSION
&0xFF;
462 header
[11]=(crc
>>8)&0xFF;
464 int2be(sizeof(header
), &header
[12]);
469 file
= fopen(oname
,"wb");
474 if ( !fwrite(header
,headerlen
,1,file
) ) {
478 if ( !fwrite(outbuf
,length
,1,file
) ) {
490 int iaudio_encode(char *iname
, char *oname
, char *idstring
)
495 unsigned char *outbuf
;
497 unsigned char sum
= 0;
499 file
= fopen(iname
, "rb");
504 fseek(file
,0,SEEK_END
);
505 length
= ftell(file
);
507 fseek(file
,0,SEEK_SET
);
508 outbuf
= malloc(length
+0x1030);
511 printf("out of memory!\n");
515 len
= fread(outbuf
+0x1030, 1, length
, file
);
521 memset(outbuf
, 0, 0x1030);
522 strcpy((char *)outbuf
, idstring
);
524 for(i
= 0; i
< length
;i
++)
525 sum
+= outbuf
[0x1030 + i
];
527 int2be(length
, &outbuf
[0x1024]);
528 outbuf
[0x102b] = sum
;
532 file
= fopen(oname
, "wb");
538 len
= fwrite(outbuf
, 1, length
+0x1030, file
);
548 /* Create an ipod firmware partition image
550 fw_ver = 2 for 3rd Gen ipods, 3 for all later ipods including 5g.
552 This function doesn't yet handle the Broadcom resource image for the 5g,
553 so the resulting images won't be usable.
555 This has also only been tested on an ipod Photo
558 int ipod_encode(char *iname
, char *oname
, int fw_ver
, bool fake_rsrc
)
560 static const char *apple_stop_sign
= "{{~~ /-----\\ "\
581 unsigned int sum
= 0;
582 unsigned int rsrcsum
= 0;
583 unsigned char *outbuf
;
587 file
= fopen(iname
, "rb");
592 fseek(file
,0,SEEK_END
);
593 length
= ftell(file
);
595 fseek(file
,0,SEEK_SET
);
597 bufsize
=(length
+0x4600);
599 bufsize
= (bufsize
+ 0x400) & ~0x200;
602 outbuf
= malloc(bufsize
);
605 printf("out of memory!\n");
609 len
= fread(outbuf
+0x4600, 1, length
, file
);
616 /* Calculate checksum for later use in header */
617 for(i
= 0x4600; i
< 0x4600+length
;i
++)
620 /* Clear the header area to zero */
621 memset(outbuf
, 0, 0x4600);
623 /* APPLE STOP SIGN */
624 strcpy((char *)outbuf
, apple_stop_sign
);
627 memcpy(&outbuf
[0x100],"]ih[",4); /* Magic */
628 int2le(0x4000, &outbuf
[0x104]); /* Firmware offset relative to 0x200 */
629 short2le(0x10c, &outbuf
[0x108]); /* Location of extended header */
630 short2le(fw_ver
, &outbuf
[0x10a]);
632 /* Firmware Directory - "osos" entry */
633 memcpy(&outbuf
[0x4200],"!ATAsoso",8); /* dev and type */
634 int2le(0, &outbuf
[0x4208]); /* id */
635 int2le(0x4400, &outbuf
[0x420c]); /* devOffset */
636 int2le(length
, &outbuf
[0x4210]); /* Length of firmware */
637 int2le(0x10000000, &outbuf
[0x4214]); /* Addr */
638 int2le(0, &outbuf
[0x4218]); /* Entry Offset */
639 int2le(sum
, &outbuf
[0x421c]); /* Checksum */
640 int2le(0x00006012, &outbuf
[0x4220]); /* vers - 0x6012 is a guess */
641 int2le(0xffffffff, &outbuf
[0x4224]); /* LoadAddr - for flash images */
643 /* "rsrc" entry (if applicable) */
645 rsrcoffset
=(length
+0x4600+0x200) & ~0x200;
649 memcpy(&outbuf
[0x4228],"!ATAcrsr",8); /* dev and type */
650 int2le(0, &outbuf
[0x4230]); /* id */
651 int2le(rsrcoffset
, &outbuf
[0x4234]); /* devOffset */
652 int2le(rsrclength
, &outbuf
[0x4238]); /* Length of firmware */
653 int2le(0x10000000, &outbuf
[0x423c]); /* Addr */
654 int2le(0, &outbuf
[0x4240]); /* Entry Offset */
655 int2le(rsrcsum
, &outbuf
[0x4244]); /* Checksum */
656 int2le(0x0000b000, &outbuf
[0x4248]); /* vers */
657 int2le(0xffffffff, &outbuf
[0x424c]); /* LoadAddr - for flash images */
660 file
= fopen(oname
, "wb");
666 len
= fwrite(outbuf
, 1, length
+0x4600, file
);