mpeg2dec in mpegplayer: Do a little code slashing and organizing. Much more to come...
[Rockbox.git] / tools / scramble.c
blob9a64a5aeed3c15e5c21bf5bfd88d2b23a317fb69
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 {
228 fprintf(stderr, "unsupported model: %s\n", &argv[1][5]);
229 return 2;
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 */
238 iname = argv[2];
239 oname = argv[3];
240 iriver_encode(iname, oname, FALSE);
241 return 0;
243 else if(!strcmp(argv[1], "-gigabeat")) {
244 /* iRiver code dealt with in the iriver.c code */
245 iname = argv[2];
246 oname = argv[3];
247 gigabeat_code(iname, oname);
248 return 0;
250 else if(!strcmp(argv[1], "-iaudiox5")) {
251 iname = argv[2];
252 oname = argv[3];
253 return iaudio_encode(iname, oname, "COWON_X5_FW");
255 else if(!strcmp(argv[1], "-iaudiox5v")) {
256 iname = argv[2];
257 oname = argv[3];
258 return iaudio_encode(iname, oname, "COWON_X5V_FW");
260 else if(!strcmp(argv[1], "-iaudiom5")) {
261 iname = argv[2];
262 oname = argv[3];
263 return iaudio_encode(iname, oname, "COWON_M5_FW");
265 else if(!strcmp(argv[1], "-ipod3g")) {
266 iname = argv[2];
267 oname = argv[3];
268 return ipod_encode(iname, oname, 2, false); /* Firmware image v2 */
270 else if(!strcmp(argv[1], "-ipod4g")) {
271 iname = argv[2];
272 oname = argv[3];
273 return ipod_encode(iname, oname, 3, false); /* Firmware image v3 */
275 else if(!strcmp(argv[1], "-ipod5g")) {
276 iname = argv[2];
277 oname = argv[3];
278 return ipod_encode(iname, oname, 3, true); /* Firmware image v3 */
280 else if(!strncmp(argv[1], "-mi4", 4)) {
281 int mi4magic;
282 int version;
283 char model[4] = "";
284 char type[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;
298 else {
299 printf( "Invalid mi4 version: %s\n", &argv[1][4]);
300 return -1;
303 iname = argv[2];
304 oname = argv[3];
306 if(!strncmp(argv[2], "-model=", 7)) {
307 iname = argv[3];
308 oname = argv[4];
309 strncpy(model, &argv[2][7], 4);
311 if(!strncmp(argv[3], "-type=", 6)) {
312 iname = argv[4];
313 oname = argv[5];
314 strncpy(type, &argv[3][6], 4);
318 return mi4_encode(iname, oname, version, mi4magic, model, type);
321 /* open file */
322 file = fopen(iname,"rb");
323 if (!file) {
324 perror(iname);
325 return -1;
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",
334 length + headerlen,
335 size_limit[model_id]);
336 fclose(file);
337 return -1;
340 fseek(file,0,SEEK_SET);
341 inbuf = malloc(length);
342 if (method == xor)
343 outbuf = malloc(length*2);
344 else if(method == add)
345 outbuf = malloc(length + 8);
346 else
347 outbuf = malloc(length);
348 if ( !inbuf || !outbuf ) {
349 printf("out of memory!\n");
350 return -1;
352 if(length> 4) {
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);
358 /* read file */
359 i=fread(inbuf,1,length,file);
360 if ( !i ) {
361 perror(iname);
362 return -1;
364 fclose(file);
366 switch (method)
368 case add:
369 for (i = 0; i < length; i++) {
370 /* add 8 unsigned bits but keep a 32 bit sum */
371 chksum += inbuf[i];
373 break;
374 case scramble:
375 slen = length/4;
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 */
380 outbuf[addr] = data;
382 break;
384 case xor:
385 /* "compress" */
386 slen = 0;
387 for (i=0; i<length; i++) {
388 if (!(i&7))
389 outbuf[slen++] = 0xff; /* all data is uncompressed */
390 outbuf[slen++] = inbuf[i];
392 break;
395 if(method != add) {
396 /* calculate checksum */
397 for (i=0;i<length;i++)
398 crc += inbuf[i];
401 memset(header, 0, sizeof header);
402 switch (method)
404 case add:
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*/
409 headerlen = 8;
411 break;
412 case scramble:
413 if (headerlen == 6) {
414 int2be(length, header);
415 header[4] = (crc >> 8) & 0xff;
416 header[5] = crc & 0xff;
418 else {
419 header[0] =
420 header[1] =
421 header[2] =
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]);
433 break;
435 case xor:
437 int xorlen = strlen(xorstring);
439 /* xor data */
440 for (i=0; i<slen; i++)
441 outbuf[i] ^= xorstring[i & (xorlen-1)];
443 /* calculate checksum */
444 for (i=0; i<slen; i++)
445 crc += outbuf[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]);
452 length = slen;
453 break;
456 #define MY_FIRMWARE_TYPE "Rockbox"
457 #define MY_HEADER_VERSION 1
458 default:
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;
463 header[12]=crc&0xFF;
464 int2be(sizeof(header), &header[12]);
465 break;
468 /* write file */
469 file = fopen(oname,"wb");
470 if ( !file ) {
471 perror(oname);
472 return -1;
474 if ( !fwrite(header,headerlen,1,file) ) {
475 perror(oname);
476 return -1;
478 if ( !fwrite(outbuf,length,1,file) ) {
479 perror(oname);
480 return -1;
482 fclose(file);
484 free(inbuf);
485 free(outbuf);
487 return 0;
490 int iaudio_encode(char *iname, char *oname, char *idstring)
492 size_t len;
493 int length;
494 FILE *file;
495 unsigned char *outbuf;
496 int i;
497 unsigned char sum = 0;
499 file = fopen(iname, "rb");
500 if (!file) {
501 perror(iname);
502 return -1;
504 fseek(file,0,SEEK_END);
505 length = ftell(file);
507 fseek(file,0,SEEK_SET);
508 outbuf = malloc(length+0x1030);
510 if ( !outbuf ) {
511 printf("out of memory!\n");
512 return -1;
515 len = fread(outbuf+0x1030, 1, length, file);
516 if(len < length) {
517 perror(iname);
518 return -2;
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;
530 fclose(file);
532 file = fopen(oname, "wb");
533 if (!file) {
534 perror(oname);
535 return -3;
538 len = fwrite(outbuf, 1, length+0x1030, file);
539 if(len < length) {
540 perror(oname);
541 return -4;
544 fclose(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 = "{{~~ /-----\\ "\
561 "{{~~ / \\ "\
562 "{{~~| | "\
563 "{{~~| S T O P | "\
564 "{{~~| | "\
565 "{{~~ \\ / "\
566 "{{~~ \\-----/ "\
567 "Copyright(C) 200"\
568 "1 Apple Computer"\
569 ", Inc.----------"\
570 "----------------"\
571 "----------------"\
572 "----------------"\
573 "----------------"\
574 "----------------"\
575 "---------------";
576 size_t len;
577 int length;
578 int rsrclength;
579 int rsrcoffset;
580 FILE *file;
581 unsigned int sum = 0;
582 unsigned int rsrcsum = 0;
583 unsigned char *outbuf;
584 int bufsize;
585 int i;
587 file = fopen(iname, "rb");
588 if (!file) {
589 perror(iname);
590 return -1;
592 fseek(file,0,SEEK_END);
593 length = ftell(file);
595 fseek(file,0,SEEK_SET);
597 bufsize=(length+0x4600);
598 if (fake_rsrc) {
599 bufsize = (bufsize + 0x400) & ~0x200;
602 outbuf = malloc(bufsize);
604 if ( !outbuf ) {
605 printf("out of memory!\n");
606 return -1;
609 len = fread(outbuf+0x4600, 1, length, file);
610 if(len < length) {
611 perror(iname);
612 return -2;
614 fclose(file);
616 /* Calculate checksum for later use in header */
617 for(i = 0x4600; i < 0x4600+length;i++)
618 sum += outbuf[i];
620 /* Clear the header area to zero */
621 memset(outbuf, 0, 0x4600);
623 /* APPLE STOP SIGN */
624 strcpy((char *)outbuf, apple_stop_sign);
626 /* VOLUME HEADER */
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) */
644 if (fake_rsrc) {
645 rsrcoffset=(length+0x4600+0x200) & ~0x200;
646 rsrclength=0x200;
647 rsrcsum=0;
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");
661 if (!file) {
662 perror(oname);
663 return -3;
666 len = fwrite(outbuf, 1, length+0x4600, file);
667 if(len < length) {
668 perror(oname);
669 return -4;
672 fclose(file);
674 return 0;