| /*************************************************************************** |
| * __________ __ ___. |
| * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| * \/ \/ \/ \/ \/ |
| * $Id$ |
| * |
| * Copyright (C) 2002 by Björn Stenberg |
| * |
| * All files in this archive are subject to the GNU General Public License. |
| * See the file COPYING in the source tree root for full license agreement. |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| * KIND, either express or implied. |
| * |
| ****************************************************************************/ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #include "iriver.h" |
| |
| enum |
| { |
| ARCHOS_PLAYER, /* and V1 recorder */ |
| ARCHOS_V2RECORDER, |
| ARCHOS_FMRECORDER, |
| ARCHOS_ONDIO_SP, |
| ARCHOS_ONDIO_FM |
| }; |
| |
| int size_limit[] = |
| { |
| 0x32000, /* ARCHOS_PLAYER */ |
| 0x64000, /* ARCHOS_V2RECORDER */ |
| 0x64000, /* ARCHOS_FMRECORDER */ |
| 0x64000, /* ARCHOS_ONDIO_SP */ |
| 0x64000 /* ARCHOS_ONDIO_FM */ |
| }; |
| |
| void int2le(unsigned int val, unsigned char* addr) |
| { |
| addr[0] = val & 0xFF; |
| addr[1] = (val >> 8) & 0xff; |
| addr[2] = (val >> 16) & 0xff; |
| addr[3] = (val >> 24) & 0xff; |
| } |
| |
| void int2be(unsigned int val, unsigned char* addr) |
| { |
| addr[0] = (val >> 24) & 0xff; |
| addr[1] = (val >> 16) & 0xff; |
| addr[2] = (val >> 8) & 0xff; |
| addr[3] = val & 0xFF; |
| } |
| |
| void usage(void) |
| { |
| printf("usage: scramble [options] <input file> <output file> [xor string]\n"); |
| printf("options:\n" |
| "\t-fm Archos FM recorder format\n" |
| "\t-v2 Archos V2 recorder format\n" |
| "\t-ofm Archos Ondio FM recorder format\n" |
| "\t-osp Archos Ondio SP format\n" |
| "\t-neo SSI Neo format\n" |
| "\t-mm=X Archos Multimedia format (X values: A=JBMM, B=AV1xx, C=AV3xx)\n" |
| "\t-iriver iRiver format\n" |
| "\t-add=X Rockbox iRiver \"add-up\" checksum format\n" |
| "\t (X values: h100, h120, h140, h300)\n" |
| "\nNo option results in Archos standard player/recorder format.\n"); |
| |
| exit(1); |
| } |
| |
| int main (int argc, char** argv) |
| { |
| unsigned long length,i,slen; |
| unsigned char *inbuf,*outbuf; |
| unsigned short crc=0; |
| unsigned long chksum=0; /* 32 bit checksum */ |
| unsigned char header[24]; |
| unsigned char *iname = argv[1]; |
| unsigned char *oname = argv[2]; |
| unsigned char *xorstring; |
| int headerlen = 6; |
| FILE* file; |
| int version; |
| unsigned long irivernum; |
| char irivermodel[5]; |
| int model_id; |
| enum { none, scramble, xor, add } method = scramble; |
| |
| model_id = ARCHOS_PLAYER; |
| |
| if (argc < 3) { |
| usage(); |
| } |
| |
| if(!strcmp(argv[1], "-fm")) { |
| headerlen = 24; |
| iname = argv[2]; |
| oname = argv[3]; |
| version = 4; |
| model_id = ARCHOS_FMRECORDER; |
| } |
| |
| else if(!strcmp(argv[1], "-v2")) { |
| headerlen = 24; |
| iname = argv[2]; |
| oname = argv[3]; |
| version = 2; |
| model_id = ARCHOS_V2RECORDER; |
| } |
| |
| else if(!strcmp(argv[1], "-ofm")) { |
| headerlen = 24; |
| iname = argv[2]; |
| oname = argv[3]; |
| version = 8; |
| model_id = ARCHOS_ONDIO_FM; |
| } |
| |
| else if(!strcmp(argv[1], "-osp")) { |
| headerlen = 24; |
| iname = argv[2]; |
| oname = argv[3]; |
| version = 16; |
| model_id = ARCHOS_ONDIO_SP; |
| } |
| |
| else if(!strcmp(argv[1], "-neo")) { |
| headerlen = 17; |
| iname = argv[2]; |
| oname = argv[3]; |
| method = none; |
| } |
| else if(!strncmp(argv[1], "-mm=", 4)) { |
| headerlen = 16; |
| iname = argv[2]; |
| oname = argv[3]; |
| method = xor; |
| version = argv[1][4]; |
| if (argc > 4) |
| xorstring = argv[4]; |
| else { |
| printf("Multimedia needs an xor string\n"); |
| return -1; |
| } |
| } |
| else if(!strncmp(argv[1], "-add=", 5)) { |
| iname = argv[2]; |
| oname = argv[3]; |
| method = add; |
| |
| if(!strcmp(&argv[1][5], "h120")) |
| irivernum = 0; |
| else if(!strcmp(&argv[1][5], "h140")) |
| irivernum = 0; /* the same as the h120 */ |
| else if(!strcmp(&argv[1][5], "h100")) |
| irivernum = 1; |
| else if(!strcmp(&argv[1][5], "h300")) |
| irivernum = 2; |
| else { |
| fprintf(stderr, "unsupported model: %s\n", &argv[1][5]); |
| return 2; |
| } |
| /* we store a 4-letter model name too, for humans */ |
| strcpy(irivermodel, &argv[1][5]); |
| chksum = irivernum; /* start checksum calcs with this */ |
| } |
| |
| else if(!strcmp(argv[1], "-iriver")) { |
| /* iRiver code dealt with in the iriver.c code */ |
| iname = argv[2]; |
| oname = argv[3]; |
| iriver_encode(iname, oname, FALSE); |
| return 0; |
| } |
| |
| /* open file */ |
| file = fopen(iname,"rb"); |
| if (!file) { |
| perror(iname); |
| return -1; |
| } |
| fseek(file,0,SEEK_END); |
| length = ftell(file); |
| length = (length + 3) & ~3; /* Round up to nearest 4 byte boundary */ |
| |
| if ((method == scramble) && |
| ((length + headerlen) >= size_limit[model_id])) { |
| printf("error: max firmware size is %dKB!\n", |
| size_limit[model_id]/1024); |
| fclose(file); |
| return -1; |
| } |
| |
| fseek(file,0,SEEK_SET); |
| inbuf = malloc(length); |
| if (method == xor) |
| outbuf = malloc(length*2); |
| else if(method == add) |
| outbuf = malloc(length + 8); |
| else |
| outbuf = malloc(length); |
| if ( !inbuf || !outbuf ) { |
| printf("out of memory!\n"); |
| return -1; |
| } |
| if(length> 4) { |
| /* zero-fill the last 4 bytes to make sure there's no rubbish there |
| when we write the size-aligned file later */ |
| memset(outbuf+length-4, 0, 4); |
| } |
| |
| /* read file */ |
| i=fread(inbuf,1,length,file); |
| if ( !i ) { |
| perror(iname); |
| return -1; |
| } |
| fclose(file); |
| |
| switch (method) |
| { |
| case add: |
| for (i = 0; i < length; i++) { |
| /* add 8 unsigned bits but keep a 32 bit sum */ |
| chksum += inbuf[i]; |
| } |
| break; |
| case scramble: |
| slen = length/4; |
| for (i = 0; i < length; i++) { |
| unsigned long addr = (i >> 2) + ((i % 4) * slen); |
| unsigned char data = inbuf[i]; |
| data = ~((data << 1) | ((data >> 7) & 1)); /* poor man's ROL */ |
| outbuf[addr] = data; |
| } |
| break; |
| |
| case xor: |
| /* "compress" */ |
| slen = 0; |
| for (i=0; i<length; i++) { |
| if (!(i&7)) |
| outbuf[slen++] = 0xff; /* all data is uncompressed */ |
| outbuf[slen++] = inbuf[i]; |
| } |
| break; |
| } |
| |
| if(method != add) { |
| /* calculate checksum */ |
| for (i=0;i<length;i++) |
| crc += inbuf[i]; |
| } |
| |
| memset(header, 0, sizeof header); |
| switch (method) |
| { |
| case add: |
| { |
| int2be(chksum, header); /* checksum, big-endian */ |
| memcpy(&header[4], irivermodel, 4); /* 4 bytes model name */ |
| memcpy(outbuf, inbuf, length); /* the input buffer to output*/ |
| headerlen = 8; |
| } |
| break; |
| case scramble: |
| if (headerlen == 6) { |
| int2be(length, header); |
| header[4] = (crc >> 8) & 0xff; |
| header[5] = crc & 0xff; |
| } |
| else { |
| header[0] = |
| header[1] = |
| header[2] = |
| header[3] = 0xff; /* ??? */ |
| |
| header[6] = (crc >> 8) & 0xff; |
| header[7] = crc & 0xff; |
| |
| header[11] = version; |
| |
| header[15] = headerlen; /* really? */ |
| |
| int2be(length, &header[20]); |
| } |
| break; |
| |
| case xor: |
| { |
| int xorlen = strlen(xorstring); |
| |
| /* xor data */ |
| for (i=0; i<slen; i++) |
| outbuf[i] ^= xorstring[i & (xorlen-1)]; |
| |
| /* calculate checksum */ |
| for (i=0; i<slen; i++) |
| crc += outbuf[i]; |
| |
| header[0] = header[2] = 'Z'; |
| header[1] = header[3] = version; |
| int2le(length, &header[4]); |
| int2le(slen, &header[8]); |
| int2le(crc, &header[12]); |
| length = slen; |
| break; |
| } |
| |
| #define MY_FIRMWARE_TYPE "Rockbox" |
| #define MY_HEADER_VERSION 1 |
| default: |
| strncpy(header,MY_FIRMWARE_TYPE,9); |
| header[9]='\0'; /*shouldn't have to, but to be SURE */ |
| header[10]=MY_HEADER_VERSION&0xFF; |
| header[11]=(crc>>8)&0xFF; |
| header[12]=crc&0xFF; |
| int2be(sizeof(header), &header[12]); |
| break; |
| } |
| |
| /* write file */ |
| file = fopen(oname,"wb"); |
| if ( !file ) { |
| perror(oname); |
| return -1; |
| } |
| if ( !fwrite(header,headerlen,1,file) ) { |
| perror(oname); |
| return -1; |
| } |
| if ( !fwrite(outbuf,length,1,file) ) { |
| perror(oname); |
| return -1; |
| } |
| fclose(file); |
| |
| free(inbuf); |
| free(outbuf); |
| |
| return 0; |
| } |