Remove some debug code. Remove some unnecessary checks.
[kugel-rb.git] / tools / scramble.c
blobb7c8d1f41a3ed32f6521f7ba74116590cfece336
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stdbool.h>
23 #include <string.h>
24 #include "iriver.h"
25 #include "gigabeat.h"
26 #include "mi4.h"
28 int iaudio_encode(char *iname, char *oname, char *idstring);
29 int ipod_encode(char *iname, char *oname, int fw_ver, bool fake_rsrc);
31 enum
33 ARCHOS_PLAYER, /* and V1 recorder */
34 ARCHOS_V2RECORDER,
35 ARCHOS_FMRECORDER,
36 ARCHOS_ONDIO_SP,
37 ARCHOS_ONDIO_FM
40 int size_limit[] =
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)
51 addr[0] = val & 0xFF;
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];
59 return res;
62 void int2le(unsigned int val, unsigned char* addr)
64 addr[0] = val & 0xFF;
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;
75 addr[3] = val & 0xFF;
78 void usage(void)
80 printf("usage: scramble [options] <input file> <output file> [xor string]\n");
81 printf("options:\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");
108 exit(1);
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];
120 char *xorstring;
121 int headerlen = 6;
122 FILE* file;
123 int version;
124 unsigned long modelnum;
125 char modelname[5];
126 int model_id;
127 enum { none, scramble, xor, add } method = scramble;
129 model_id = ARCHOS_PLAYER;
131 if (argc < 3) {
132 usage();
135 if(!strcmp(argv[1], "-fm")) {
136 headerlen = 24;
137 iname = argv[2];
138 oname = argv[3];
139 version = 4;
140 model_id = ARCHOS_FMRECORDER;
143 else if(!strcmp(argv[1], "-v2")) {
144 headerlen = 24;
145 iname = argv[2];
146 oname = argv[3];
147 version = 2;
148 model_id = ARCHOS_V2RECORDER;
151 else if(!strcmp(argv[1], "-ofm")) {
152 headerlen = 24;
153 iname = argv[2];
154 oname = argv[3];
155 version = 8;
156 model_id = ARCHOS_ONDIO_FM;
159 else if(!strcmp(argv[1], "-osp")) {
160 headerlen = 24;
161 iname = argv[2];
162 oname = argv[3];
163 version = 16;
164 model_id = ARCHOS_ONDIO_SP;
167 else if(!strcmp(argv[1], "-neo")) {
168 headerlen = 17;
169 iname = argv[2];
170 oname = argv[3];
171 method = none;
173 else if(!strncmp(argv[1], "-mm=", 4)) {
174 headerlen = 16;
175 iname = argv[2];
176 oname = argv[3];
177 method = xor;
178 version = argv[1][4];
179 if (argc > 4)
180 xorstring = argv[4];
181 else {
182 printf("Multimedia needs an xor string\n");
183 return -1;
186 else if(!strncmp(argv[1], "-add=", 5)) {
187 iname = argv[2];
188 oname = argv[3];
189 method = add;
191 if(!strcmp(&argv[1][5], "h120"))
192 modelnum = 0;
193 else if(!strcmp(&argv[1][5], "h140"))
194 modelnum = 0; /* the same as the h120 */
195 else if(!strcmp(&argv[1][5], "h100"))
196 modelnum = 1;
197 else if(!strcmp(&argv[1][5], "h300"))
198 modelnum = 2;
199 else if(!strcmp(&argv[1][5], "ipco"))
200 modelnum = 3;
201 else if(!strcmp(&argv[1][5], "nano"))
202 modelnum = 4;
203 else if(!strcmp(&argv[1][5], "ipvd"))
204 modelnum = 5;
205 else if(!strcmp(&argv[1][5], "fp7x"))
206 modelnum = 6;
207 else if(!strcmp(&argv[1][5], "ip3g"))
208 modelnum = 7;
209 else if(!strcmp(&argv[1][5], "ip4g"))
210 modelnum = 8;
211 else if(!strcmp(&argv[1][5], "mini"))
212 modelnum = 9;
213 else if(!strcmp(&argv[1][5], "iax5"))
214 modelnum = 10;
215 else if(!strcmp(&argv[1][5], "mn2g"))
216 modelnum = 11;
217 else if(!strcmp(&argv[1][5], "h10"))
218 modelnum = 13;
219 else if(!strcmp(&argv[1][5], "h10_5gb"))
220 modelnum = 14;
221 else if(!strcmp(&argv[1][5], "tpj2"))
222 modelnum = 15;
223 else if(!strcmp(&argv[1][5], "e200"))
224 modelnum = 16;
225 else if(!strcmp(&argv[1][5], "iam5"))
226 modelnum = 17;
227 else if(!strcmp(&argv[1][5], "giga"))
228 modelnum = 18;
229 else if(!strcmp(&argv[1][5], "1g2g"))
230 modelnum = 19;
231 else {
232 fprintf(stderr, "unsupported model: %s\n", &argv[1][5]);
233 return 2;
235 /* we store a 4-letter model name too, for humans */
236 strcpy(modelname, &argv[1][5]);
237 chksum = modelnum; /* start checksum calcs with this */
240 else if(!strcmp(argv[1], "-iriver")) {
241 /* iRiver code dealt with in the iriver.c code */
242 iname = argv[2];
243 oname = argv[3];
244 iriver_encode(iname, oname, FALSE);
245 return 0;
247 else if(!strcmp(argv[1], "-gigabeat")) {
248 /* iRiver code dealt with in the iriver.c code */
249 iname = argv[2];
250 oname = argv[3];
251 gigabeat_code(iname, oname);
252 return 0;
254 else if(!strcmp(argv[1], "-iaudiox5")) {
255 iname = argv[2];
256 oname = argv[3];
257 return iaudio_encode(iname, oname, "COWON_X5_FW");
259 else if(!strcmp(argv[1], "-iaudiox5v")) {
260 iname = argv[2];
261 oname = argv[3];
262 return iaudio_encode(iname, oname, "COWON_X5V_FW");
264 else if(!strcmp(argv[1], "-iaudiom5")) {
265 iname = argv[2];
266 oname = argv[3];
267 return iaudio_encode(iname, oname, "COWON_M5_FW");
269 else if(!strcmp(argv[1], "-ipod3g")) {
270 iname = argv[2];
271 oname = argv[3];
272 return ipod_encode(iname, oname, 2, false); /* Firmware image v2 */
274 else if(!strcmp(argv[1], "-ipod4g")) {
275 iname = argv[2];
276 oname = argv[3];
277 return ipod_encode(iname, oname, 3, false); /* Firmware image v3 */
279 else if(!strcmp(argv[1], "-ipod5g")) {
280 iname = argv[2];
281 oname = argv[3];
282 return ipod_encode(iname, oname, 3, true); /* Firmware image v3 */
284 else if(!strncmp(argv[1], "-mi4", 4)) {
285 int mi4magic;
286 int version;
287 char model[4] = "";
288 char type[4] = "";
290 if(!strcmp(&argv[1][4], "v2")) {
291 mi4magic = MI4_MAGIC_DEFAULT;
292 version = 0x00010201;
294 else if(!strcmp(&argv[1][4], "v3")) {
295 mi4magic = MI4_MAGIC_DEFAULT;
296 version = 0x00010301;
298 else if(!strcmp(&argv[1][4], "r")) {
299 mi4magic = MI4_MAGIC_R;
300 version = 0x00010301;
302 else {
303 printf( "Invalid mi4 version: %s\n", &argv[1][4]);
304 return -1;
307 iname = argv[2];
308 oname = argv[3];
310 if(!strncmp(argv[2], "-model=", 7)) {
311 iname = argv[3];
312 oname = argv[4];
313 strncpy(model, &argv[2][7], 4);
315 if(!strncmp(argv[3], "-type=", 6)) {
316 iname = argv[4];
317 oname = argv[5];
318 strncpy(type, &argv[3][6], 4);
322 return mi4_encode(iname, oname, version, mi4magic, model, type);
325 /* open file */
326 file = fopen(iname,"rb");
327 if (!file) {
328 perror(iname);
329 return -1;
331 fseek(file,0,SEEK_END);
332 length = ftell(file);
333 length = (length + 3) & ~3; /* Round up to nearest 4 byte boundary */
335 if ((method == scramble) &&
336 ((length + headerlen) >= size_limit[model_id])) {
337 printf("error: firmware image is %d bytes while max size is %d!\n",
338 length + headerlen,
339 size_limit[model_id]);
340 fclose(file);
341 return -1;
344 fseek(file,0,SEEK_SET);
345 inbuf = malloc(length);
346 if (method == xor)
347 outbuf = malloc(length*2);
348 else if(method == add)
349 outbuf = malloc(length + 8);
350 else
351 outbuf = malloc(length);
352 if ( !inbuf || !outbuf ) {
353 printf("out of memory!\n");
354 return -1;
356 if(length> 4) {
357 /* zero-fill the last 4 bytes to make sure there's no rubbish there
358 when we write the size-aligned file later */
359 memset(outbuf+length-4, 0, 4);
362 /* read file */
363 i=fread(inbuf,1,length,file);
364 if ( !i ) {
365 perror(iname);
366 return -1;
368 fclose(file);
370 switch (method)
372 case add:
373 for (i = 0; i < length; i++) {
374 /* add 8 unsigned bits but keep a 32 bit sum */
375 chksum += inbuf[i];
377 break;
378 case scramble:
379 slen = length/4;
380 for (i = 0; i < length; i++) {
381 unsigned long addr = (i >> 2) + ((i % 4) * slen);
382 unsigned char data = inbuf[i];
383 data = ~((data << 1) | ((data >> 7) & 1)); /* poor man's ROL */
384 outbuf[addr] = data;
386 break;
388 case xor:
389 /* "compress" */
390 slen = 0;
391 for (i=0; i<length; i++) {
392 if (!(i&7))
393 outbuf[slen++] = 0xff; /* all data is uncompressed */
394 outbuf[slen++] = inbuf[i];
396 break;
399 if(method != add) {
400 /* calculate checksum */
401 for (i=0;i<length;i++)
402 crc += inbuf[i];
405 memset(header, 0, sizeof header);
406 switch (method)
408 case add:
410 int2be(chksum, header); /* checksum, big-endian */
411 memcpy(&header[4], modelname, 4); /* 4 bytes model name */
412 memcpy(outbuf, inbuf, length); /* the input buffer to output*/
413 headerlen = 8;
415 break;
416 case scramble:
417 if (headerlen == 6) {
418 int2be(length, header);
419 header[4] = (crc >> 8) & 0xff;
420 header[5] = crc & 0xff;
422 else {
423 header[0] =
424 header[1] =
425 header[2] =
426 header[3] = 0xff; /* ??? */
428 header[6] = (crc >> 8) & 0xff;
429 header[7] = crc & 0xff;
431 header[11] = version;
433 header[15] = headerlen; /* really? */
435 int2be(length, &header[20]);
437 break;
439 case xor:
441 int xorlen = strlen(xorstring);
443 /* xor data */
444 for (i=0; i<slen; i++)
445 outbuf[i] ^= xorstring[i & (xorlen-1)];
447 /* calculate checksum */
448 for (i=0; i<slen; i++)
449 crc += outbuf[i];
451 header[0] = header[2] = 'Z';
452 header[1] = header[3] = version;
453 int2le(length, &header[4]);
454 int2le(slen, &header[8]);
455 int2le(crc, &header[12]);
456 length = slen;
457 break;
460 #define MY_FIRMWARE_TYPE "Rockbox"
461 #define MY_HEADER_VERSION 1
462 default:
463 strncpy((char *)header, MY_FIRMWARE_TYPE,9);
464 header[9]='\0'; /*shouldn't have to, but to be SURE */
465 header[10]=MY_HEADER_VERSION&0xFF;
466 header[11]=(crc>>8)&0xFF;
467 header[12]=crc&0xFF;
468 int2be(sizeof(header), &header[12]);
469 break;
472 /* write file */
473 file = fopen(oname,"wb");
474 if ( !file ) {
475 perror(oname);
476 return -1;
478 if ( !fwrite(header,headerlen,1,file) ) {
479 perror(oname);
480 return -1;
482 if ( !fwrite(outbuf,length,1,file) ) {
483 perror(oname);
484 return -1;
486 fclose(file);
488 free(inbuf);
489 free(outbuf);
491 return 0;
494 int iaudio_encode(char *iname, char *oname, char *idstring)
496 size_t len;
497 int length;
498 FILE *file;
499 unsigned char *outbuf;
500 int i;
501 unsigned char sum = 0;
503 file = fopen(iname, "rb");
504 if (!file) {
505 perror(iname);
506 return -1;
508 fseek(file,0,SEEK_END);
509 length = ftell(file);
511 fseek(file,0,SEEK_SET);
512 outbuf = malloc(length+0x1030);
514 if ( !outbuf ) {
515 printf("out of memory!\n");
516 return -1;
519 len = fread(outbuf+0x1030, 1, length, file);
520 if(len < length) {
521 perror(iname);
522 return -2;
525 memset(outbuf, 0, 0x1030);
526 strcpy((char *)outbuf, idstring);
528 for(i = 0; i < length;i++)
529 sum += outbuf[0x1030 + i];
531 int2be(length, &outbuf[0x1024]);
532 outbuf[0x102b] = sum;
534 fclose(file);
536 file = fopen(oname, "wb");
537 if (!file) {
538 perror(oname);
539 return -3;
542 len = fwrite(outbuf, 1, length+0x1030, file);
543 if(len < length) {
544 perror(oname);
545 return -4;
548 fclose(file);
552 /* Create an ipod firmware partition image
554 fw_ver = 2 for 3rd Gen ipods, 3 for all later ipods including 5g.
556 This function doesn't yet handle the Broadcom resource image for the 5g,
557 so the resulting images won't be usable.
559 This has also only been tested on an ipod Photo
562 int ipod_encode(char *iname, char *oname, int fw_ver, bool fake_rsrc)
564 static const char *apple_stop_sign = "{{~~ /-----\\ "\
565 "{{~~ / \\ "\
566 "{{~~| | "\
567 "{{~~| S T O P | "\
568 "{{~~| | "\
569 "{{~~ \\ / "\
570 "{{~~ \\-----/ "\
571 "Copyright(C) 200"\
572 "1 Apple Computer"\
573 ", Inc.----------"\
574 "----------------"\
575 "----------------"\
576 "----------------"\
577 "----------------"\
578 "----------------"\
579 "---------------";
580 size_t len;
581 int length;
582 int rsrclength;
583 int rsrcoffset;
584 FILE *file;
585 unsigned int sum = 0;
586 unsigned int rsrcsum = 0;
587 unsigned char *outbuf;
588 int bufsize;
589 int i;
591 file = fopen(iname, "rb");
592 if (!file) {
593 perror(iname);
594 return -1;
596 fseek(file,0,SEEK_END);
597 length = ftell(file);
599 fseek(file,0,SEEK_SET);
601 bufsize=(length+0x4600);
602 if (fake_rsrc) {
603 bufsize = (bufsize + 0x400) & ~0x200;
606 outbuf = malloc(bufsize);
608 if ( !outbuf ) {
609 printf("out of memory!\n");
610 return -1;
613 len = fread(outbuf+0x4600, 1, length, file);
614 if(len < length) {
615 perror(iname);
616 return -2;
618 fclose(file);
620 /* Calculate checksum for later use in header */
621 for(i = 0x4600; i < 0x4600+length;i++)
622 sum += outbuf[i];
624 /* Clear the header area to zero */
625 memset(outbuf, 0, 0x4600);
627 /* APPLE STOP SIGN */
628 strcpy((char *)outbuf, apple_stop_sign);
630 /* VOLUME HEADER */
631 memcpy(&outbuf[0x100],"]ih[",4); /* Magic */
632 int2le(0x4000, &outbuf[0x104]); /* Firmware offset relative to 0x200 */
633 short2le(0x10c, &outbuf[0x108]); /* Location of extended header */
634 short2le(fw_ver, &outbuf[0x10a]);
636 /* Firmware Directory - "osos" entry */
637 memcpy(&outbuf[0x4200],"!ATAsoso",8); /* dev and type */
638 int2le(0, &outbuf[0x4208]); /* id */
639 int2le(0x4400, &outbuf[0x420c]); /* devOffset */
640 int2le(length, &outbuf[0x4210]); /* Length of firmware */
641 int2le(0x10000000, &outbuf[0x4214]); /* Addr */
642 int2le(0, &outbuf[0x4218]); /* Entry Offset */
643 int2le(sum, &outbuf[0x421c]); /* Checksum */
644 int2le(0x00006012, &outbuf[0x4220]); /* vers - 0x6012 is a guess */
645 int2le(0xffffffff, &outbuf[0x4224]); /* LoadAddr - for flash images */
647 /* "rsrc" entry (if applicable) */
648 if (fake_rsrc) {
649 rsrcoffset=(length+0x4600+0x200) & ~0x200;
650 rsrclength=0x200;
651 rsrcsum=0;
653 memcpy(&outbuf[0x4228],"!ATAcrsr",8); /* dev and type */
654 int2le(0, &outbuf[0x4230]); /* id */
655 int2le(rsrcoffset, &outbuf[0x4234]); /* devOffset */
656 int2le(rsrclength, &outbuf[0x4238]); /* Length of firmware */
657 int2le(0x10000000, &outbuf[0x423c]); /* Addr */
658 int2le(0, &outbuf[0x4240]); /* Entry Offset */
659 int2le(rsrcsum, &outbuf[0x4244]); /* Checksum */
660 int2le(0x0000b000, &outbuf[0x4248]); /* vers */
661 int2le(0xffffffff, &outbuf[0x424c]); /* LoadAddr - for flash images */
664 file = fopen(oname, "wb");
665 if (!file) {
666 perror(oname);
667 return -3;
670 len = fwrite(outbuf, 1, length+0x4600, file);
671 if(len < length) {
672 perror(oname);
673 return -4;
676 fclose(file);
678 return 0;