A few minor fixes (typos, accuracy in the Ondio manual).
[kugel-rb.git] / tools / scramble.c
blob30d4ea9edb60ce5b1031161c0e427452440a634f
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 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 void int2le(unsigned int val, unsigned char* addr)
57 addr[0] = val & 0xFF;
58 addr[1] = (val >> 8) & 0xff;
59 addr[2] = (val >> 16) & 0xff;
60 addr[3] = (val >> 24) & 0xff;
63 void int2be(unsigned int val, unsigned char* addr)
65 addr[0] = (val >> 24) & 0xff;
66 addr[1] = (val >> 16) & 0xff;
67 addr[2] = (val >> 8) & 0xff;
68 addr[3] = val & 0xFF;
71 void usage(void)
73 printf("usage: scramble [options] <input file> <output file> [xor string]\n");
74 printf("options:\n"
75 "\t-fm Archos FM recorder format\n"
76 "\t-v2 Archos V2 recorder format\n"
77 "\t-ofm Archos Ondio FM recorder format\n"
78 "\t-osp Archos Ondio SP format\n"
79 "\t-neo SSI Neo format\n"
80 "\t-mm=X Archos Multimedia format (X values: A=JBMM, B=AV1xx, C=AV3xx)\n"
81 "\t-iriver iRiver format\n"
82 "\t-iaudiox5 iAudio X5 format\n"
83 "\t-iaudiox5v iAudio X5V format\n"
84 "\t-ipod3g ipod firmware partition format (3rd Gen)\n"
85 "\t-ipod4g ipod firmware partition format (4th Gen, Mini, Nano, Photo/Color)\n"
86 "\t-ipod5g ipod firmware partition format (5th Gen - aka Video)\n"
87 "\t-gigabeat Toshiba Gigabeat format\n"
88 "\t-mi4v2 PortalPlayer .mi4 format (revision 010201)\n"
89 "\t-mi4v3 PortalPlayer .mi4 format (revision 010301)\n"
90 "\t-add=X Rockbox generic \"add-up\" checksum format\n"
91 "\t (X values: h100, h120, h140, h300, ipco, nano, ipvd\n"
92 "\t ip3g, ip4g, mini, x5, h10, h10_5gb)\n"
93 "\nNo option results in Archos standard player/recorder format.\n");
95 exit(1);
98 int main (int argc, char** argv)
100 unsigned long length,i,slen;
101 unsigned char *inbuf,*outbuf;
102 unsigned short crc=0;
103 unsigned long chksum=0; /* 32 bit checksum */
104 unsigned char header[24];
105 char *iname = argv[1];
106 char *oname = argv[2];
107 char *xorstring;
108 int headerlen = 6;
109 FILE* file;
110 int version;
111 unsigned long modelnum;
112 char modelname[5];
113 int model_id;
114 enum { none, scramble, xor, add } method = scramble;
116 model_id = ARCHOS_PLAYER;
118 if (argc < 3) {
119 usage();
122 if(!strcmp(argv[1], "-fm")) {
123 headerlen = 24;
124 iname = argv[2];
125 oname = argv[3];
126 version = 4;
127 model_id = ARCHOS_FMRECORDER;
130 else if(!strcmp(argv[1], "-v2")) {
131 headerlen = 24;
132 iname = argv[2];
133 oname = argv[3];
134 version = 2;
135 model_id = ARCHOS_V2RECORDER;
138 else if(!strcmp(argv[1], "-ofm")) {
139 headerlen = 24;
140 iname = argv[2];
141 oname = argv[3];
142 version = 8;
143 model_id = ARCHOS_ONDIO_FM;
146 else if(!strcmp(argv[1], "-osp")) {
147 headerlen = 24;
148 iname = argv[2];
149 oname = argv[3];
150 version = 16;
151 model_id = ARCHOS_ONDIO_SP;
154 else if(!strcmp(argv[1], "-neo")) {
155 headerlen = 17;
156 iname = argv[2];
157 oname = argv[3];
158 method = none;
160 else if(!strncmp(argv[1], "-mm=", 4)) {
161 headerlen = 16;
162 iname = argv[2];
163 oname = argv[3];
164 method = xor;
165 version = argv[1][4];
166 if (argc > 4)
167 xorstring = argv[4];
168 else {
169 printf("Multimedia needs an xor string\n");
170 return -1;
173 else if(!strncmp(argv[1], "-add=", 5)) {
174 iname = argv[2];
175 oname = argv[3];
176 method = add;
178 if(!strcmp(&argv[1][5], "h120"))
179 modelnum = 0;
180 else if(!strcmp(&argv[1][5], "h140"))
181 modelnum = 0; /* the same as the h120 */
182 else if(!strcmp(&argv[1][5], "h100"))
183 modelnum = 1;
184 else if(!strcmp(&argv[1][5], "h300"))
185 modelnum = 2;
186 else if(!strcmp(&argv[1][5], "ipco"))
187 modelnum = 3;
188 else if(!strcmp(&argv[1][5], "nano"))
189 modelnum = 4;
190 else if(!strcmp(&argv[1][5], "ipvd"))
191 modelnum = 5;
192 else if(!strcmp(&argv[1][5], "fp7x"))
193 modelnum = 6;
194 else if(!strcmp(&argv[1][5], "ip3g"))
195 modelnum = 7;
196 else if(!strcmp(&argv[1][5], "ip4g"))
197 modelnum = 8;
198 else if(!strcmp(&argv[1][5], "mini"))
199 modelnum = 9;
200 else if(!strcmp(&argv[1][5], "iax5"))
201 modelnum = 10;
202 else if(!strcmp(&argv[1][5], "mn2g"))
203 modelnum = 11;
204 else if(!strcmp(&argv[1][5], "h10"))
205 modelnum = 13;
206 else if(!strcmp(&argv[1][5], "h10_5gb"))
207 modelnum = 14;
208 else if(!strcmp(&argv[1][5], "tpj2"))
209 modelnum = 15;
210 else {
211 fprintf(stderr, "unsupported model: %s\n", &argv[1][5]);
212 return 2;
214 /* we store a 4-letter model name too, for humans */
215 strcpy(modelname, &argv[1][5]);
216 chksum = modelnum; /* start checksum calcs with this */
219 else if(!strcmp(argv[1], "-iriver")) {
220 /* iRiver code dealt with in the iriver.c code */
221 iname = argv[2];
222 oname = argv[3];
223 iriver_encode(iname, oname, FALSE);
224 return 0;
226 else if(!strcmp(argv[1], "-gigabeat")) {
227 /* iRiver code dealt with in the iriver.c code */
228 iname = argv[2];
229 oname = argv[3];
230 gigabeat_code(iname, oname);
231 return 0;
233 else if(!strcmp(argv[1], "-iaudiox5")) {
234 iname = argv[2];
235 oname = argv[3];
236 return iaudio_encode(iname, oname, "COWON_X5_FW");
238 else if(!strcmp(argv[1], "-iaudiox5v")) {
239 iname = argv[2];
240 oname = argv[3];
241 return iaudio_encode(iname, oname, "COWON_X5V_FW");
243 else if(!strcmp(argv[1], "-ipod3g")) {
244 iname = argv[2];
245 oname = argv[3];
246 return ipod_encode(iname, oname, 2, false); /* Firmware image v2 */
248 else if(!strcmp(argv[1], "-ipod4g")) {
249 iname = argv[2];
250 oname = argv[3];
251 return ipod_encode(iname, oname, 3, false); /* Firmware image v3 */
253 else if(!strcmp(argv[1], "-ipod5g")) {
254 iname = argv[2];
255 oname = argv[3];
256 return ipod_encode(iname, oname, 3, true); /* Firmware image v3 */
258 else if(!strcmp(argv[1], "-mi4v2")) {
259 iname = argv[2];
260 oname = argv[3];
261 return mi4_encode(iname, oname, 0x00010201);
263 else if(!strcmp(argv[1], "-mi4v3")) {
264 iname = argv[2];
265 oname = argv[3];
266 return mi4_encode(iname, oname, 0x00010301);
269 /* open file */
270 file = fopen(iname,"rb");
271 if (!file) {
272 perror(iname);
273 return -1;
275 fseek(file,0,SEEK_END);
276 length = ftell(file);
277 length = (length + 3) & ~3; /* Round up to nearest 4 byte boundary */
279 if ((method == scramble) &&
280 ((length + headerlen) >= size_limit[model_id])) {
281 printf("error: firmware image is %d bytes while max size is %d!\n",
282 length + headerlen,
283 size_limit[model_id]);
284 fclose(file);
285 return -1;
288 fseek(file,0,SEEK_SET);
289 inbuf = malloc(length);
290 if (method == xor)
291 outbuf = malloc(length*2);
292 else if(method == add)
293 outbuf = malloc(length + 8);
294 else
295 outbuf = malloc(length);
296 if ( !inbuf || !outbuf ) {
297 printf("out of memory!\n");
298 return -1;
300 if(length> 4) {
301 /* zero-fill the last 4 bytes to make sure there's no rubbish there
302 when we write the size-aligned file later */
303 memset(outbuf+length-4, 0, 4);
306 /* read file */
307 i=fread(inbuf,1,length,file);
308 if ( !i ) {
309 perror(iname);
310 return -1;
312 fclose(file);
314 switch (method)
316 case add:
317 for (i = 0; i < length; i++) {
318 /* add 8 unsigned bits but keep a 32 bit sum */
319 chksum += inbuf[i];
321 break;
322 case scramble:
323 slen = length/4;
324 for (i = 0; i < length; i++) {
325 unsigned long addr = (i >> 2) + ((i % 4) * slen);
326 unsigned char data = inbuf[i];
327 data = ~((data << 1) | ((data >> 7) & 1)); /* poor man's ROL */
328 outbuf[addr] = data;
330 break;
332 case xor:
333 /* "compress" */
334 slen = 0;
335 for (i=0; i<length; i++) {
336 if (!(i&7))
337 outbuf[slen++] = 0xff; /* all data is uncompressed */
338 outbuf[slen++] = inbuf[i];
340 break;
343 if(method != add) {
344 /* calculate checksum */
345 for (i=0;i<length;i++)
346 crc += inbuf[i];
349 memset(header, 0, sizeof header);
350 switch (method)
352 case add:
354 int2be(chksum, header); /* checksum, big-endian */
355 memcpy(&header[4], modelname, 4); /* 4 bytes model name */
356 memcpy(outbuf, inbuf, length); /* the input buffer to output*/
357 headerlen = 8;
359 break;
360 case scramble:
361 if (headerlen == 6) {
362 int2be(length, header);
363 header[4] = (crc >> 8) & 0xff;
364 header[5] = crc & 0xff;
366 else {
367 header[0] =
368 header[1] =
369 header[2] =
370 header[3] = 0xff; /* ??? */
372 header[6] = (crc >> 8) & 0xff;
373 header[7] = crc & 0xff;
375 header[11] = version;
377 header[15] = headerlen; /* really? */
379 int2be(length, &header[20]);
381 break;
383 case xor:
385 int xorlen = strlen(xorstring);
387 /* xor data */
388 for (i=0; i<slen; i++)
389 outbuf[i] ^= xorstring[i & (xorlen-1)];
391 /* calculate checksum */
392 for (i=0; i<slen; i++)
393 crc += outbuf[i];
395 header[0] = header[2] = 'Z';
396 header[1] = header[3] = version;
397 int2le(length, &header[4]);
398 int2le(slen, &header[8]);
399 int2le(crc, &header[12]);
400 length = slen;
401 break;
404 #define MY_FIRMWARE_TYPE "Rockbox"
405 #define MY_HEADER_VERSION 1
406 default:
407 strncpy((char *)header, MY_FIRMWARE_TYPE,9);
408 header[9]='\0'; /*shouldn't have to, but to be SURE */
409 header[10]=MY_HEADER_VERSION&0xFF;
410 header[11]=(crc>>8)&0xFF;
411 header[12]=crc&0xFF;
412 int2be(sizeof(header), &header[12]);
413 break;
416 /* write file */
417 file = fopen(oname,"wb");
418 if ( !file ) {
419 perror(oname);
420 return -1;
422 if ( !fwrite(header,headerlen,1,file) ) {
423 perror(oname);
424 return -1;
426 if ( !fwrite(outbuf,length,1,file) ) {
427 perror(oname);
428 return -1;
430 fclose(file);
432 free(inbuf);
433 free(outbuf);
435 return 0;
438 int iaudio_encode(char *iname, char *oname, char *idstring)
440 size_t len;
441 int length;
442 FILE *file;
443 unsigned char *outbuf;
444 int i;
445 unsigned char sum = 0;
447 file = fopen(iname, "rb");
448 if (!file) {
449 perror(iname);
450 return -1;
452 fseek(file,0,SEEK_END);
453 length = ftell(file);
455 fseek(file,0,SEEK_SET);
456 outbuf = malloc(length+0x1030);
458 if ( !outbuf ) {
459 printf("out of memory!\n");
460 return -1;
463 len = fread(outbuf+0x1030, 1, length, file);
464 if(len < length) {
465 perror(iname);
466 return -2;
469 memset(outbuf, 0, 0x1030);
470 strcpy((char *)outbuf, idstring);
472 for(i = 0; i < length;i++)
473 sum += outbuf[0x1030 + i];
475 int2be(length, &outbuf[0x1024]);
476 outbuf[0x102b] = sum;
478 fclose(file);
480 file = fopen(oname, "wb");
481 if (!file) {
482 perror(oname);
483 return -3;
486 len = fwrite(outbuf, 1, length+0x1030, file);
487 if(len < length) {
488 perror(oname);
489 return -4;
492 fclose(file);
496 /* Create an ipod firmware partition image
498 fw_ver = 2 for 3rd Gen ipods, 3 for all later ipods including 5g.
500 This function doesn't yet handle the Broadcom resource image for the 5g,
501 so the resulting images won't be usable.
503 This has also only been tested on an ipod Photo
506 int ipod_encode(char *iname, char *oname, int fw_ver, bool fake_rsrc)
508 static const char *apple_stop_sign = "{{~~ /-----\\ "\
509 "{{~~ / \\ "\
510 "{{~~| | "\
511 "{{~~| S T O P | "\
512 "{{~~| | "\
513 "{{~~ \\ / "\
514 "{{~~ \\-----/ "\
515 "Copyright(C) 200"\
516 "1 Apple Computer"\
517 ", Inc.----------"\
518 "----------------"\
519 "----------------"\
520 "----------------"\
521 "----------------"\
522 "----------------"\
523 "---------------";
524 size_t len;
525 int length;
526 int rsrclength;
527 int rsrcoffset;
528 FILE *file;
529 unsigned int sum = 0;
530 unsigned int rsrcsum = 0;
531 unsigned char *outbuf;
532 int bufsize;
533 int i;
535 file = fopen(iname, "rb");
536 if (!file) {
537 perror(iname);
538 return -1;
540 fseek(file,0,SEEK_END);
541 length = ftell(file);
543 fseek(file,0,SEEK_SET);
545 bufsize=(length+0x4600);
546 if (fake_rsrc) {
547 bufsize = (bufsize + 0x400) & ~0x200;
550 outbuf = malloc(bufsize);
552 if ( !outbuf ) {
553 printf("out of memory!\n");
554 return -1;
557 len = fread(outbuf+0x4600, 1, length, file);
558 if(len < length) {
559 perror(iname);
560 return -2;
562 fclose(file);
564 /* Calculate checksum for later use in header */
565 for(i = 0x4600; i < 0x4600+length;i++)
566 sum += outbuf[i];
568 /* Clear the header area to zero */
569 memset(outbuf, 0, 0x4600);
571 /* APPLE STOP SIGN */
572 strcpy((char *)outbuf, apple_stop_sign);
574 /* VOLUME HEADER */
575 memcpy(&outbuf[0x100],"]ih[",4); /* Magic */
576 int2le(0x4000, &outbuf[0x104]); /* Firmware offset relative to 0x200 */
577 short2le(0x10c, &outbuf[0x108]); /* Location of extended header */
578 short2le(fw_ver, &outbuf[0x10a]);
580 /* Firmware Directory - "osos" entry */
581 memcpy(&outbuf[0x4200],"!ATAsoso",8); /* dev and type */
582 int2le(0, &outbuf[0x4208]); /* id */
583 int2le(0x4400, &outbuf[0x420c]); /* devOffset */
584 int2le(length, &outbuf[0x4210]); /* Length of firmware */
585 int2le(0x10000000, &outbuf[0x4214]); /* Addr */
586 int2le(0, &outbuf[0x4218]); /* Entry Offset */
587 int2le(sum, &outbuf[0x421c]); /* Checksum */
588 int2le(0x00006012, &outbuf[0x4220]); /* vers - 0x6012 is a guess */
589 int2le(0xffffffff, &outbuf[0x4224]); /* LoadAddr - for flash images */
591 /* "rsrc" entry (if applicable) */
592 if (fake_rsrc) {
593 rsrcoffset=(length+0x4600+0x200) & ~0x200;
594 rsrclength=0x200;
595 rsrcsum=0;
597 memcpy(&outbuf[0x4228],"!ATAcrsr",8); /* dev and type */
598 int2le(0, &outbuf[0x4230]); /* id */
599 int2le(rsrcoffset, &outbuf[0x4234]); /* devOffset */
600 int2le(rsrclength, &outbuf[0x4238]); /* Length of firmware */
601 int2le(0x10000000, &outbuf[0x423c]); /* Addr */
602 int2le(0, &outbuf[0x4240]); /* Entry Offset */
603 int2le(rsrcsum, &outbuf[0x4244]); /* Checksum */
604 int2le(0x0000b000, &outbuf[0x4248]); /* vers */
605 int2le(0xffffffff, &outbuf[0x424c]); /* LoadAddr - for flash images */
608 file = fopen(oname, "wb");
609 if (!file) {
610 perror(oname);
611 return -3;
614 len = fwrite(outbuf, 1, length+0x4600, file);
615 if(len < length) {
616 perror(oname);
617 return -4;
620 fclose(file);
622 return 0;