The M5 is greyscale, not color.
[Rockbox.git] / tools / scramble.c
blob9f18e96e855e864bf8327a3db4913506ed805929
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,\n"
106 "\t c200, e200)\n"
107 "\nNo option results in Archos standard player/recorder format.\n");
109 exit(1);
112 int main (int argc, char** argv)
114 unsigned long length,i,slen;
115 unsigned char *inbuf,*outbuf;
116 unsigned short crc=0;
117 unsigned long chksum=0; /* 32 bit checksum */
118 unsigned char header[24];
119 char *iname = argv[1];
120 char *oname = argv[2];
121 char *xorstring;
122 int headerlen = 6;
123 FILE* file;
124 int version;
125 unsigned long modelnum;
126 char modelname[5];
127 int model_id;
128 enum { none, scramble, xor, add } method = scramble;
130 model_id = ARCHOS_PLAYER;
132 if (argc < 3) {
133 usage();
136 if(!strcmp(argv[1], "-fm")) {
137 headerlen = 24;
138 iname = argv[2];
139 oname = argv[3];
140 version = 4;
141 model_id = ARCHOS_FMRECORDER;
144 else if(!strcmp(argv[1], "-v2")) {
145 headerlen = 24;
146 iname = argv[2];
147 oname = argv[3];
148 version = 2;
149 model_id = ARCHOS_V2RECORDER;
152 else if(!strcmp(argv[1], "-ofm")) {
153 headerlen = 24;
154 iname = argv[2];
155 oname = argv[3];
156 version = 8;
157 model_id = ARCHOS_ONDIO_FM;
160 else if(!strcmp(argv[1], "-osp")) {
161 headerlen = 24;
162 iname = argv[2];
163 oname = argv[3];
164 version = 16;
165 model_id = ARCHOS_ONDIO_SP;
168 else if(!strcmp(argv[1], "-neo")) {
169 headerlen = 17;
170 iname = argv[2];
171 oname = argv[3];
172 method = none;
174 else if(!strncmp(argv[1], "-mm=", 4)) {
175 headerlen = 16;
176 iname = argv[2];
177 oname = argv[3];
178 method = xor;
179 version = argv[1][4];
180 if (argc > 4)
181 xorstring = argv[4];
182 else {
183 printf("Multimedia needs an xor string\n");
184 return -1;
187 else if(!strncmp(argv[1], "-add=", 5)) {
188 iname = argv[2];
189 oname = argv[3];
190 method = add;
192 if(!strcmp(&argv[1][5], "h120"))
193 modelnum = 0;
194 else if(!strcmp(&argv[1][5], "h140"))
195 modelnum = 0; /* the same as the h120 */
196 else if(!strcmp(&argv[1][5], "h100"))
197 modelnum = 1;
198 else if(!strcmp(&argv[1][5], "h300"))
199 modelnum = 2;
200 else if(!strcmp(&argv[1][5], "ipco"))
201 modelnum = 3;
202 else if(!strcmp(&argv[1][5], "nano"))
203 modelnum = 4;
204 else if(!strcmp(&argv[1][5], "ipvd"))
205 modelnum = 5;
206 else if(!strcmp(&argv[1][5], "fp7x"))
207 modelnum = 6;
208 else if(!strcmp(&argv[1][5], "ip3g"))
209 modelnum = 7;
210 else if(!strcmp(&argv[1][5], "ip4g"))
211 modelnum = 8;
212 else if(!strcmp(&argv[1][5], "mini"))
213 modelnum = 9;
214 else if(!strcmp(&argv[1][5], "iax5"))
215 modelnum = 10;
216 else if(!strcmp(&argv[1][5], "mn2g"))
217 modelnum = 11;
218 else if(!strcmp(&argv[1][5], "h10"))
219 modelnum = 13;
220 else if(!strcmp(&argv[1][5], "h10_5gb"))
221 modelnum = 14;
222 else if(!strcmp(&argv[1][5], "tpj2"))
223 modelnum = 15;
224 else if(!strcmp(&argv[1][5], "e200"))
225 modelnum = 16;
226 else if(!strcmp(&argv[1][5], "iam5"))
227 modelnum = 17;
228 else if(!strcmp(&argv[1][5], "giga"))
229 modelnum = 18;
230 else if(!strcmp(&argv[1][5], "1g2g"))
231 modelnum = 19;
232 else if(!strcmp(&argv[1][5], "c200"))
233 modelnum = 20;
234 else {
235 fprintf(stderr, "unsupported model: %s\n", &argv[1][5]);
236 return 2;
238 /* we store a 4-letter model name too, for humans */
239 strcpy(modelname, &argv[1][5]);
240 chksum = modelnum; /* start checksum calcs with this */
243 else if(!strcmp(argv[1], "-iriver")) {
244 /* iRiver code dealt with in the iriver.c code */
245 iname = argv[2];
246 oname = argv[3];
247 iriver_encode(iname, oname, FALSE);
248 return 0;
250 else if(!strcmp(argv[1], "-gigabeat")) {
251 /* iRiver code dealt with in the iriver.c code */
252 iname = argv[2];
253 oname = argv[3];
254 gigabeat_code(iname, oname);
255 return 0;
257 else if(!strcmp(argv[1], "-iaudiox5")) {
258 iname = argv[2];
259 oname = argv[3];
260 return iaudio_encode(iname, oname, "COWON_X5_FW");
262 else if(!strcmp(argv[1], "-iaudiox5v")) {
263 iname = argv[2];
264 oname = argv[3];
265 return iaudio_encode(iname, oname, "COWON_X5V_FW");
267 else if(!strcmp(argv[1], "-iaudiom5")) {
268 iname = argv[2];
269 oname = argv[3];
270 return iaudio_encode(iname, oname, "COWON_M5_FW");
272 else if(!strcmp(argv[1], "-ipod3g")) {
273 iname = argv[2];
274 oname = argv[3];
275 return ipod_encode(iname, oname, 2, false); /* Firmware image v2 */
277 else if(!strcmp(argv[1], "-ipod4g")) {
278 iname = argv[2];
279 oname = argv[3];
280 return ipod_encode(iname, oname, 3, false); /* Firmware image v3 */
282 else if(!strcmp(argv[1], "-ipod5g")) {
283 iname = argv[2];
284 oname = argv[3];
285 return ipod_encode(iname, oname, 3, true); /* Firmware image v3 */
287 else if(!strncmp(argv[1], "-mi4", 4)) {
288 int mi4magic;
289 int version;
290 char model[4] = "";
291 char type[4] = "";
293 if(!strcmp(&argv[1][4], "v2")) {
294 mi4magic = MI4_MAGIC_DEFAULT;
295 version = 0x00010201;
297 else if(!strcmp(&argv[1][4], "v3")) {
298 mi4magic = MI4_MAGIC_DEFAULT;
299 version = 0x00010301;
301 else if(!strcmp(&argv[1][4], "r")) {
302 mi4magic = MI4_MAGIC_R;
303 version = 0x00010301;
305 else {
306 printf( "Invalid mi4 version: %s\n", &argv[1][4]);
307 return -1;
310 iname = argv[2];
311 oname = argv[3];
313 if(!strncmp(argv[2], "-model=", 7)) {
314 iname = argv[3];
315 oname = argv[4];
316 strncpy(model, &argv[2][7], 4);
318 if(!strncmp(argv[3], "-type=", 6)) {
319 iname = argv[4];
320 oname = argv[5];
321 strncpy(type, &argv[3][6], 4);
325 return mi4_encode(iname, oname, version, mi4magic, model, type);
328 /* open file */
329 file = fopen(iname,"rb");
330 if (!file) {
331 perror(iname);
332 return -1;
334 fseek(file,0,SEEK_END);
335 length = ftell(file);
336 length = (length + 3) & ~3; /* Round up to nearest 4 byte boundary */
338 if ((method == scramble) &&
339 ((length + headerlen) >= size_limit[model_id])) {
340 printf("error: firmware image is %d bytes while max size is %d!\n",
341 length + headerlen,
342 size_limit[model_id]);
343 fclose(file);
344 return -1;
347 fseek(file,0,SEEK_SET);
348 inbuf = malloc(length);
349 if (method == xor)
350 outbuf = malloc(length*2);
351 else if(method == add)
352 outbuf = malloc(length + 8);
353 else
354 outbuf = malloc(length);
355 if ( !inbuf || !outbuf ) {
356 printf("out of memory!\n");
357 return -1;
359 if(length> 4) {
360 /* zero-fill the last 4 bytes to make sure there's no rubbish there
361 when we write the size-aligned file later */
362 memset(outbuf+length-4, 0, 4);
365 /* read file */
366 i=fread(inbuf,1,length,file);
367 if ( !i ) {
368 perror(iname);
369 return -1;
371 fclose(file);
373 switch (method)
375 case add:
376 for (i = 0; i < length; i++) {
377 /* add 8 unsigned bits but keep a 32 bit sum */
378 chksum += inbuf[i];
380 break;
381 case scramble:
382 slen = length/4;
383 for (i = 0; i < length; i++) {
384 unsigned long addr = (i >> 2) + ((i % 4) * slen);
385 unsigned char data = inbuf[i];
386 data = ~((data << 1) | ((data >> 7) & 1)); /* poor man's ROL */
387 outbuf[addr] = data;
389 break;
391 case xor:
392 /* "compress" */
393 slen = 0;
394 for (i=0; i<length; i++) {
395 if (!(i&7))
396 outbuf[slen++] = 0xff; /* all data is uncompressed */
397 outbuf[slen++] = inbuf[i];
399 break;
402 if(method != add) {
403 /* calculate checksum */
404 for (i=0;i<length;i++)
405 crc += inbuf[i];
408 memset(header, 0, sizeof header);
409 switch (method)
411 case add:
413 int2be(chksum, header); /* checksum, big-endian */
414 memcpy(&header[4], modelname, 4); /* 4 bytes model name */
415 memcpy(outbuf, inbuf, length); /* the input buffer to output*/
416 headerlen = 8;
418 break;
419 case scramble:
420 if (headerlen == 6) {
421 int2be(length, header);
422 header[4] = (crc >> 8) & 0xff;
423 header[5] = crc & 0xff;
425 else {
426 header[0] =
427 header[1] =
428 header[2] =
429 header[3] = 0xff; /* ??? */
431 header[6] = (crc >> 8) & 0xff;
432 header[7] = crc & 0xff;
434 header[11] = version;
436 header[15] = headerlen; /* really? */
438 int2be(length, &header[20]);
440 break;
442 case xor:
444 int xorlen = strlen(xorstring);
446 /* xor data */
447 for (i=0; i<slen; i++)
448 outbuf[i] ^= xorstring[i & (xorlen-1)];
450 /* calculate checksum */
451 for (i=0; i<slen; i++)
452 crc += outbuf[i];
454 header[0] = header[2] = 'Z';
455 header[1] = header[3] = version;
456 int2le(length, &header[4]);
457 int2le(slen, &header[8]);
458 int2le(crc, &header[12]);
459 length = slen;
460 break;
463 #define MY_FIRMWARE_TYPE "Rockbox"
464 #define MY_HEADER_VERSION 1
465 default:
466 strncpy((char *)header, MY_FIRMWARE_TYPE,9);
467 header[9]='\0'; /*shouldn't have to, but to be SURE */
468 header[10]=MY_HEADER_VERSION&0xFF;
469 header[11]=(crc>>8)&0xFF;
470 header[12]=crc&0xFF;
471 int2be(sizeof(header), &header[12]);
472 break;
475 /* write file */
476 file = fopen(oname,"wb");
477 if ( !file ) {
478 perror(oname);
479 return -1;
481 if ( !fwrite(header,headerlen,1,file) ) {
482 perror(oname);
483 return -1;
485 if ( !fwrite(outbuf,length,1,file) ) {
486 perror(oname);
487 return -1;
489 fclose(file);
491 free(inbuf);
492 free(outbuf);
494 return 0;
497 int iaudio_encode(char *iname, char *oname, char *idstring)
499 size_t len;
500 int length;
501 FILE *file;
502 unsigned char *outbuf;
503 int i;
504 unsigned char sum = 0;
506 file = fopen(iname, "rb");
507 if (!file) {
508 perror(iname);
509 return -1;
511 fseek(file,0,SEEK_END);
512 length = ftell(file);
514 fseek(file,0,SEEK_SET);
515 outbuf = malloc(length+0x1030);
517 if ( !outbuf ) {
518 printf("out of memory!\n");
519 return -1;
522 len = fread(outbuf+0x1030, 1, length, file);
523 if(len < length) {
524 perror(iname);
525 return -2;
528 memset(outbuf, 0, 0x1030);
529 strcpy((char *)outbuf, idstring);
531 for(i = 0; i < length;i++)
532 sum += outbuf[0x1030 + i];
534 int2be(length, &outbuf[0x1024]);
535 outbuf[0x102b] = sum;
537 fclose(file);
539 file = fopen(oname, "wb");
540 if (!file) {
541 perror(oname);
542 return -3;
545 len = fwrite(outbuf, 1, length+0x1030, file);
546 if(len < length) {
547 perror(oname);
548 return -4;
551 fclose(file);
555 /* Create an ipod firmware partition image
557 fw_ver = 2 for 3rd Gen ipods, 3 for all later ipods including 5g.
559 This function doesn't yet handle the Broadcom resource image for the 5g,
560 so the resulting images won't be usable.
562 This has also only been tested on an ipod Photo
565 int ipod_encode(char *iname, char *oname, int fw_ver, bool fake_rsrc)
567 static const char *apple_stop_sign = "{{~~ /-----\\ "\
568 "{{~~ / \\ "\
569 "{{~~| | "\
570 "{{~~| S T O P | "\
571 "{{~~| | "\
572 "{{~~ \\ / "\
573 "{{~~ \\-----/ "\
574 "Copyright(C) 200"\
575 "1 Apple Computer"\
576 ", Inc.----------"\
577 "----------------"\
578 "----------------"\
579 "----------------"\
580 "----------------"\
581 "----------------"\
582 "---------------";
583 size_t len;
584 int length;
585 int rsrclength;
586 int rsrcoffset;
587 FILE *file;
588 unsigned int sum = 0;
589 unsigned int rsrcsum = 0;
590 unsigned char *outbuf;
591 int bufsize;
592 int i;
594 file = fopen(iname, "rb");
595 if (!file) {
596 perror(iname);
597 return -1;
599 fseek(file,0,SEEK_END);
600 length = ftell(file);
602 fseek(file,0,SEEK_SET);
604 bufsize=(length+0x4600);
605 if (fake_rsrc) {
606 bufsize = (bufsize + 0x400) & ~0x200;
609 outbuf = malloc(bufsize);
611 if ( !outbuf ) {
612 printf("out of memory!\n");
613 return -1;
616 len = fread(outbuf+0x4600, 1, length, file);
617 if(len < length) {
618 perror(iname);
619 return -2;
621 fclose(file);
623 /* Calculate checksum for later use in header */
624 for(i = 0x4600; i < 0x4600+length;i++)
625 sum += outbuf[i];
627 /* Clear the header area to zero */
628 memset(outbuf, 0, 0x4600);
630 /* APPLE STOP SIGN */
631 strcpy((char *)outbuf, apple_stop_sign);
633 /* VOLUME HEADER */
634 memcpy(&outbuf[0x100],"]ih[",4); /* Magic */
635 int2le(0x4000, &outbuf[0x104]); /* Firmware offset relative to 0x200 */
636 short2le(0x10c, &outbuf[0x108]); /* Location of extended header */
637 short2le(fw_ver, &outbuf[0x10a]);
639 /* Firmware Directory - "osos" entry */
640 memcpy(&outbuf[0x4200],"!ATAsoso",8); /* dev and type */
641 int2le(0, &outbuf[0x4208]); /* id */
642 int2le(0x4400, &outbuf[0x420c]); /* devOffset */
643 int2le(length, &outbuf[0x4210]); /* Length of firmware */
644 int2le(0x10000000, &outbuf[0x4214]); /* Addr */
645 int2le(0, &outbuf[0x4218]); /* Entry Offset */
646 int2le(sum, &outbuf[0x421c]); /* Checksum */
647 int2le(0x00006012, &outbuf[0x4220]); /* vers - 0x6012 is a guess */
648 int2le(0xffffffff, &outbuf[0x4224]); /* LoadAddr - for flash images */
650 /* "rsrc" entry (if applicable) */
651 if (fake_rsrc) {
652 rsrcoffset=(length+0x4600+0x200) & ~0x200;
653 rsrclength=0x200;
654 rsrcsum=0;
656 memcpy(&outbuf[0x4228],"!ATAcrsr",8); /* dev and type */
657 int2le(0, &outbuf[0x4230]); /* id */
658 int2le(rsrcoffset, &outbuf[0x4234]); /* devOffset */
659 int2le(rsrclength, &outbuf[0x4238]); /* Length of firmware */
660 int2le(0x10000000, &outbuf[0x423c]); /* Addr */
661 int2le(0, &outbuf[0x4240]); /* Entry Offset */
662 int2le(rsrcsum, &outbuf[0x4244]); /* Checksum */
663 int2le(0x0000b000, &outbuf[0x4248]); /* vers */
664 int2le(0xffffffff, &outbuf[0x424c]); /* LoadAddr - for flash images */
667 file = fopen(oname, "wb");
668 if (!file) {
669 perror(oname);
670 return -3;
673 len = fwrite(outbuf, 1, length+0x4600, file);
674 if(len < length) {
675 perror(oname);
676 return -4;
679 fclose(file);
681 return 0;