First stab at porting rbutil to Qt4. Currently only installing a current or archived...
[Rockbox.git] / tools / scramble.c
blob531728379db1354e97250837706d38ab206375b0
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 {
230 fprintf(stderr, "unsupported model: %s\n", &argv[1][5]);
231 return 2;
233 /* we store a 4-letter model name too, for humans */
234 strcpy(modelname, &argv[1][5]);
235 chksum = modelnum; /* start checksum calcs with this */
238 else if(!strcmp(argv[1], "-iriver")) {
239 /* iRiver code dealt with in the iriver.c code */
240 iname = argv[2];
241 oname = argv[3];
242 iriver_encode(iname, oname, FALSE);
243 return 0;
245 else if(!strcmp(argv[1], "-gigabeat")) {
246 /* iRiver code dealt with in the iriver.c code */
247 iname = argv[2];
248 oname = argv[3];
249 gigabeat_code(iname, oname);
250 return 0;
252 else if(!strcmp(argv[1], "-iaudiox5")) {
253 iname = argv[2];
254 oname = argv[3];
255 return iaudio_encode(iname, oname, "COWON_X5_FW");
257 else if(!strcmp(argv[1], "-iaudiox5v")) {
258 iname = argv[2];
259 oname = argv[3];
260 return iaudio_encode(iname, oname, "COWON_X5V_FW");
262 else if(!strcmp(argv[1], "-iaudiom5")) {
263 iname = argv[2];
264 oname = argv[3];
265 return iaudio_encode(iname, oname, "COWON_M5_FW");
267 else if(!strcmp(argv[1], "-ipod3g")) {
268 iname = argv[2];
269 oname = argv[3];
270 return ipod_encode(iname, oname, 2, false); /* Firmware image v2 */
272 else if(!strcmp(argv[1], "-ipod4g")) {
273 iname = argv[2];
274 oname = argv[3];
275 return ipod_encode(iname, oname, 3, false); /* Firmware image v3 */
277 else if(!strcmp(argv[1], "-ipod5g")) {
278 iname = argv[2];
279 oname = argv[3];
280 return ipod_encode(iname, oname, 3, true); /* Firmware image v3 */
282 else if(!strncmp(argv[1], "-mi4", 4)) {
283 int mi4magic;
284 int version;
285 char model[4] = "";
286 char type[4] = "";
288 if(!strcmp(&argv[1][4], "v2")) {
289 mi4magic = MI4_MAGIC_DEFAULT;
290 version = 0x00010201;
292 else if(!strcmp(&argv[1][4], "v3")) {
293 mi4magic = MI4_MAGIC_DEFAULT;
294 version = 0x00010301;
296 else if(!strcmp(&argv[1][4], "r")) {
297 mi4magic = MI4_MAGIC_R;
298 version = 0x00010301;
300 else {
301 printf( "Invalid mi4 version: %s\n", &argv[1][4]);
302 return -1;
305 iname = argv[2];
306 oname = argv[3];
308 if(!strncmp(argv[2], "-model=", 7)) {
309 iname = argv[3];
310 oname = argv[4];
311 strncpy(model, &argv[2][7], 4);
313 if(!strncmp(argv[3], "-type=", 6)) {
314 iname = argv[4];
315 oname = argv[5];
316 strncpy(type, &argv[3][6], 4);
320 return mi4_encode(iname, oname, version, mi4magic, model, type);
323 /* open file */
324 file = fopen(iname,"rb");
325 if (!file) {
326 perror(iname);
327 return -1;
329 fseek(file,0,SEEK_END);
330 length = ftell(file);
331 length = (length + 3) & ~3; /* Round up to nearest 4 byte boundary */
333 if ((method == scramble) &&
334 ((length + headerlen) >= size_limit[model_id])) {
335 printf("error: firmware image is %d bytes while max size is %d!\n",
336 length + headerlen,
337 size_limit[model_id]);
338 fclose(file);
339 return -1;
342 fseek(file,0,SEEK_SET);
343 inbuf = malloc(length);
344 if (method == xor)
345 outbuf = malloc(length*2);
346 else if(method == add)
347 outbuf = malloc(length + 8);
348 else
349 outbuf = malloc(length);
350 if ( !inbuf || !outbuf ) {
351 printf("out of memory!\n");
352 return -1;
354 if(length> 4) {
355 /* zero-fill the last 4 bytes to make sure there's no rubbish there
356 when we write the size-aligned file later */
357 memset(outbuf+length-4, 0, 4);
360 /* read file */
361 i=fread(inbuf,1,length,file);
362 if ( !i ) {
363 perror(iname);
364 return -1;
366 fclose(file);
368 switch (method)
370 case add:
371 for (i = 0; i < length; i++) {
372 /* add 8 unsigned bits but keep a 32 bit sum */
373 chksum += inbuf[i];
375 break;
376 case scramble:
377 slen = length/4;
378 for (i = 0; i < length; i++) {
379 unsigned long addr = (i >> 2) + ((i % 4) * slen);
380 unsigned char data = inbuf[i];
381 data = ~((data << 1) | ((data >> 7) & 1)); /* poor man's ROL */
382 outbuf[addr] = data;
384 break;
386 case xor:
387 /* "compress" */
388 slen = 0;
389 for (i=0; i<length; i++) {
390 if (!(i&7))
391 outbuf[slen++] = 0xff; /* all data is uncompressed */
392 outbuf[slen++] = inbuf[i];
394 break;
397 if(method != add) {
398 /* calculate checksum */
399 for (i=0;i<length;i++)
400 crc += inbuf[i];
403 memset(header, 0, sizeof header);
404 switch (method)
406 case add:
408 int2be(chksum, header); /* checksum, big-endian */
409 memcpy(&header[4], modelname, 4); /* 4 bytes model name */
410 memcpy(outbuf, inbuf, length); /* the input buffer to output*/
411 headerlen = 8;
413 break;
414 case scramble:
415 if (headerlen == 6) {
416 int2be(length, header);
417 header[4] = (crc >> 8) & 0xff;
418 header[5] = crc & 0xff;
420 else {
421 header[0] =
422 header[1] =
423 header[2] =
424 header[3] = 0xff; /* ??? */
426 header[6] = (crc >> 8) & 0xff;
427 header[7] = crc & 0xff;
429 header[11] = version;
431 header[15] = headerlen; /* really? */
433 int2be(length, &header[20]);
435 break;
437 case xor:
439 int xorlen = strlen(xorstring);
441 /* xor data */
442 for (i=0; i<slen; i++)
443 outbuf[i] ^= xorstring[i & (xorlen-1)];
445 /* calculate checksum */
446 for (i=0; i<slen; i++)
447 crc += outbuf[i];
449 header[0] = header[2] = 'Z';
450 header[1] = header[3] = version;
451 int2le(length, &header[4]);
452 int2le(slen, &header[8]);
453 int2le(crc, &header[12]);
454 length = slen;
455 break;
458 #define MY_FIRMWARE_TYPE "Rockbox"
459 #define MY_HEADER_VERSION 1
460 default:
461 strncpy((char *)header, MY_FIRMWARE_TYPE,9);
462 header[9]='\0'; /*shouldn't have to, but to be SURE */
463 header[10]=MY_HEADER_VERSION&0xFF;
464 header[11]=(crc>>8)&0xFF;
465 header[12]=crc&0xFF;
466 int2be(sizeof(header), &header[12]);
467 break;
470 /* write file */
471 file = fopen(oname,"wb");
472 if ( !file ) {
473 perror(oname);
474 return -1;
476 if ( !fwrite(header,headerlen,1,file) ) {
477 perror(oname);
478 return -1;
480 if ( !fwrite(outbuf,length,1,file) ) {
481 perror(oname);
482 return -1;
484 fclose(file);
486 free(inbuf);
487 free(outbuf);
489 return 0;
492 int iaudio_encode(char *iname, char *oname, char *idstring)
494 size_t len;
495 int length;
496 FILE *file;
497 unsigned char *outbuf;
498 int i;
499 unsigned char sum = 0;
501 file = fopen(iname, "rb");
502 if (!file) {
503 perror(iname);
504 return -1;
506 fseek(file,0,SEEK_END);
507 length = ftell(file);
509 fseek(file,0,SEEK_SET);
510 outbuf = malloc(length+0x1030);
512 if ( !outbuf ) {
513 printf("out of memory!\n");
514 return -1;
517 len = fread(outbuf+0x1030, 1, length, file);
518 if(len < length) {
519 perror(iname);
520 return -2;
523 memset(outbuf, 0, 0x1030);
524 strcpy((char *)outbuf, idstring);
526 for(i = 0; i < length;i++)
527 sum += outbuf[0x1030 + i];
529 int2be(length, &outbuf[0x1024]);
530 outbuf[0x102b] = sum;
532 fclose(file);
534 file = fopen(oname, "wb");
535 if (!file) {
536 perror(oname);
537 return -3;
540 len = fwrite(outbuf, 1, length+0x1030, file);
541 if(len < length) {
542 perror(oname);
543 return -4;
546 fclose(file);
550 /* Create an ipod firmware partition image
552 fw_ver = 2 for 3rd Gen ipods, 3 for all later ipods including 5g.
554 This function doesn't yet handle the Broadcom resource image for the 5g,
555 so the resulting images won't be usable.
557 This has also only been tested on an ipod Photo
560 int ipod_encode(char *iname, char *oname, int fw_ver, bool fake_rsrc)
562 static const char *apple_stop_sign = "{{~~ /-----\\ "\
563 "{{~~ / \\ "\
564 "{{~~| | "\
565 "{{~~| S T O P | "\
566 "{{~~| | "\
567 "{{~~ \\ / "\
568 "{{~~ \\-----/ "\
569 "Copyright(C) 200"\
570 "1 Apple Computer"\
571 ", Inc.----------"\
572 "----------------"\
573 "----------------"\
574 "----------------"\
575 "----------------"\
576 "----------------"\
577 "---------------";
578 size_t len;
579 int length;
580 int rsrclength;
581 int rsrcoffset;
582 FILE *file;
583 unsigned int sum = 0;
584 unsigned int rsrcsum = 0;
585 unsigned char *outbuf;
586 int bufsize;
587 int i;
589 file = fopen(iname, "rb");
590 if (!file) {
591 perror(iname);
592 return -1;
594 fseek(file,0,SEEK_END);
595 length = ftell(file);
597 fseek(file,0,SEEK_SET);
599 bufsize=(length+0x4600);
600 if (fake_rsrc) {
601 bufsize = (bufsize + 0x400) & ~0x200;
604 outbuf = malloc(bufsize);
606 if ( !outbuf ) {
607 printf("out of memory!\n");
608 return -1;
611 len = fread(outbuf+0x4600, 1, length, file);
612 if(len < length) {
613 perror(iname);
614 return -2;
616 fclose(file);
618 /* Calculate checksum for later use in header */
619 for(i = 0x4600; i < 0x4600+length;i++)
620 sum += outbuf[i];
622 /* Clear the header area to zero */
623 memset(outbuf, 0, 0x4600);
625 /* APPLE STOP SIGN */
626 strcpy((char *)outbuf, apple_stop_sign);
628 /* VOLUME HEADER */
629 memcpy(&outbuf[0x100],"]ih[",4); /* Magic */
630 int2le(0x4000, &outbuf[0x104]); /* Firmware offset relative to 0x200 */
631 short2le(0x10c, &outbuf[0x108]); /* Location of extended header */
632 short2le(fw_ver, &outbuf[0x10a]);
634 /* Firmware Directory - "osos" entry */
635 memcpy(&outbuf[0x4200],"!ATAsoso",8); /* dev and type */
636 int2le(0, &outbuf[0x4208]); /* id */
637 int2le(0x4400, &outbuf[0x420c]); /* devOffset */
638 int2le(length, &outbuf[0x4210]); /* Length of firmware */
639 int2le(0x10000000, &outbuf[0x4214]); /* Addr */
640 int2le(0, &outbuf[0x4218]); /* Entry Offset */
641 int2le(sum, &outbuf[0x421c]); /* Checksum */
642 int2le(0x00006012, &outbuf[0x4220]); /* vers - 0x6012 is a guess */
643 int2le(0xffffffff, &outbuf[0x4224]); /* LoadAddr - for flash images */
645 /* "rsrc" entry (if applicable) */
646 if (fake_rsrc) {
647 rsrcoffset=(length+0x4600+0x200) & ~0x200;
648 rsrclength=0x200;
649 rsrcsum=0;
651 memcpy(&outbuf[0x4228],"!ATAcrsr",8); /* dev and type */
652 int2le(0, &outbuf[0x4230]); /* id */
653 int2le(rsrcoffset, &outbuf[0x4234]); /* devOffset */
654 int2le(rsrclength, &outbuf[0x4238]); /* Length of firmware */
655 int2le(0x10000000, &outbuf[0x423c]); /* Addr */
656 int2le(0, &outbuf[0x4240]); /* Entry Offset */
657 int2le(rsrcsum, &outbuf[0x4244]); /* Checksum */
658 int2le(0x0000b000, &outbuf[0x4248]); /* vers */
659 int2le(0xffffffff, &outbuf[0x424c]); /* LoadAddr - for flash images */
662 file = fopen(oname, "wb");
663 if (!file) {
664 perror(oname);
665 return -3;
668 len = fwrite(outbuf, 1, length+0x4600, file);
669 if(len < length) {
670 perror(oname);
671 return -4;
674 fclose(file);
676 return 0;