From adaae249ef397a65d7d77c378e2050f0d61144a7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 24 Jan 2005 09:01:48 +0000 Subject: [PATCH] iRiver scramble and descramble code from Dave Cooper git-svn-id: svn://svn.rockbox.org/rockbox/trunk@5649 a1c6a512-1295-4272-9138-f99709370657 --- docs/CREDITS | 1 + firmware/COPYING | 33 ++++ tools/Makefile | 7 +- tools/configure | 2 +- tools/descramble.c | 26 +++- tools/iriver.c | 381 +++++++++++++++++++++++++++++++++++++++++++++ tools/iriver.h | 37 +++++ tools/scramble.c | 37 +++-- 8 files changed, 508 insertions(+), 16 deletions(-) create mode 100644 tools/iriver.c create mode 100644 tools/iriver.h diff --git a/docs/CREDITS b/docs/CREDITS index ed660274e6..1eaaae013b 100644 --- a/docs/CREDITS +++ b/docs/CREDITS @@ -95,3 +95,4 @@ Brian King Jiri Jurecek Jacob Erlbeck Jean-Philippe Bernardy +Dave Hooper diff --git a/firmware/COPYING b/firmware/COPYING index d60c31a97a..d3ed837e93 100644 --- a/firmware/COPYING +++ b/firmware/COPYING @@ -338,3 +338,36 @@ proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. + +------------------------ + +ihpfirm (source code in the tools/iriver.c file) + +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 2004 Dave Hooper (@spc) + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, +provided that the above copyright notice(s) and this permission notice appear +in all copies of the Software and that both the above copyright notice(s) and +this permission notice appear in supporting documentation. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN +NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE +LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY +DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall not +be used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization of the copyright holder. + diff --git a/tools/Makefile b/tools/Makefile index 43218d99f6..8e82d0f8ee 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -13,9 +13,12 @@ TARGETS := scramble descramble sh2d bmp2rb convbdf generate_rocklatin all: $(TARGETS) @echo "tools done" -scramble: scramble.c +scramble: scramble.o iriver.o +descramble: descramble.o iriver.o -descramble: descramble.c +scramble.o: scramble.c iriver.h +descramble.o: descramble.c iriver.h +iriver.o: iriver.c iriver.h sh2d: sh2d.c diff --git a/tools/configure b/tools/configure index 4834fa3e30..1c58a39e4a 100755 --- a/tools/configure +++ b/tools/configure @@ -352,7 +352,7 @@ if [ -z "$archos" ]; then target="-DIRIVER_H100" memory=32 # always coldfirecc - tool="cp" + tool="$rootdir/tools/scramble -iriver" output="rockbox.iriver" appextra="recorder" archosrom="" diff --git a/tools/descramble.c b/tools/descramble.c index 7ed32139c6..7376190c69 100644 --- a/tools/descramble.c +++ b/tools/descramble.c @@ -20,6 +20,20 @@ #include #include +#include "iriver.h" + +void usage(void) +{ + printf("usage: descramble [options] \n"); + printf("options:\n" + "\t-fm Archos FM recorder format\n" + "\t-v2 Archos V2 recorder format\n" + "\t-mm=X Archos Multimedia format (X values: A=JBMM, B=AV1xx, C=AV3xx)\n" + "\t-iriver iRiver format\n" + "\nNo option assumes Archos standard player/recorder format.\n"); + exit(1); +} + int main (int argc, char** argv) { unsigned long length,i,slen; @@ -32,9 +46,7 @@ int main (int argc, char** argv) FILE* file; if (argc < 3) { - printf("usage: %s [-fm] [-v2] [-mm] \n", - argv[0]); - return -1; + usage(); } if (!strcmp(argv[1], "-fm") || !strcmp(argv[1], "-v2")) { @@ -49,6 +61,14 @@ int main (int argc, char** argv) oname = argv[3]; descramble = 0; } + + if(!strcmp(argv[1], "-iriver")) { + /* iRiver code dealt with in the iriver.c code */ + iname = argv[2]; + oname = argv[3]; + iriver_decode(iname, oname, FALSE, STRIP_NONE); + return 0; + } /* open file and check size */ file = fopen(iname,"rb"); diff --git a/tools/iriver.c b/tools/iriver.c new file mode 100644 index 0000000000..0beab80cb2 --- /dev/null +++ b/tools/iriver.c @@ -0,0 +1,381 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2004 by Dave Hooper + * + * This particular source code file is licensed under the X11 license. See the + * bottom of the COPYING file for details on this license. + * + * Original code from http://www.beermex.com/@spc/ihpfirm.src.zip + * Details at http://www.rockbox.org/twiki/bin/view/Main/IriverToolsGuide + * + ****************************************************************************/ +#include +#include +#include + +#include "iriver.h" + +const unsigned char munge[] = { + 0x7a, 0x36, 0xc4, 0x43, 0x49, 0x6b, 0x35, 0x4e, 0xa3, 0x46, 0x25, 0x84, + 0x4d, 0x73, 0x74, 0x61 +}; + +const unsigned char header_modify[] = "* IHPFIRM-DECODED "; + +const char * const models[] = { "iHP-100", "iHP-120/iHP-140", "H300 series", + NULL }; + +/* aligns with models array; expected min firmware size */ +const unsigned int firmware_minsize[] = { 1000000, 1000000, 2000000 }; +/* aligns with models array; expected max firmware size */ +const unsigned int firmware_maxsize[] = { 2000000, 2000000, 4000000 }; + +const unsigned char header[][16] = { + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0x20, 0x03, 0x08, 0x27, 0x24, 0x00, 0x02, 0x30, 0x19, 0x17, 0x65, 0x73, + 0x85, 0x32, 0x83, 0x22 }, + { 0x20, 0x04, 0x03, 0x27, 0x20, 0x50, 0x01, 0x70, 0x80, 0x30, 0x80, 0x06, + 0x30, 0x19, 0x17, 0x65 } +}; + +static int testheader( const unsigned char * const data ) +{ + const unsigned char * const d = data+16; + const char * const * m = models; + int index = 0; + while( *m ) + { + if( memcmp( header[ index ], d, 16 ) == 0 ) + return index; + index++; + m++; + }; + return -1; +}; + +static void modifyheader( unsigned char * data ) +{ + const unsigned char * h = header_modify; + int i; + for( i=0; i<512; i++ ) + { + if( *h == '\0' ) + h = header_modify; + *data++ ^= *h++; + }; +}; + +static FILE * openinfile( const char * filename ) +{ + FILE * F = fopen( filename, "rb" ); + if( F == NULL ) + { + fprintf( stderr, "Couldn't open input file %s\n", filename ); + perror( "Error was " ); + exit( -1 ); + }; + return F; +}; + +static FILE * openoutfile( const char * filename ) +{ + FILE * F = fopen( filename, "wb" ); + if( F == NULL ) + { + fprintf( stderr, "Couldn't open output file %s\n", filename ); + perror( "Error was " ); + exit( -1 ); + }; + return F; +}; + +int iriver_decode(char *infile_name, char *outfile_name, BOOL modify, + enum striptype stripmode ) +{ + FILE * infile = NULL; + FILE * outfile = NULL; + int i = -1; + unsigned char headerdata[512]; + unsigned long dwLength1, dwLength2, dwLength3, fp = 0; + unsigned char blockdata[16+16]; + unsigned char out[16]; + unsigned char newmunge; + signed long lenread; + int s = 0; + unsigned char * pChecksums, * ppChecksums = 0; + unsigned char ck; + + infile = openinfile(infile_name); + outfile = openoutfile(outfile_name); + + lenread = fread( headerdata, 1, 512, infile ); + if( lenread != 512 ) + { + fprintf( stderr, "This doesn't look like a valid encrypted iHP " + "firmware - reason: header length\n" ); + exit( -1 ); + }; + + i = testheader( headerdata ); + if( i == -1 ) + { + fprintf( stderr, "This firmware is for an unknown model, or is not" + " a valid encrypted iHP firmware\n" ); + exit( -1 ); + }; + fprintf( stderr, "Model %s\n", models[ i ] ); + + dwLength1 = headerdata[0] | (headerdata[1]<<8) | + (headerdata[2]<<16) | (headerdata[3]<<24); + dwLength2 = headerdata[4] | (headerdata[5]<<8) | + (headerdata[6]<<16) | (headerdata[7]<<24); + dwLength3 = headerdata[8] | (headerdata[9]<<8) | + (headerdata[10]<<16) | (headerdata[11]<<24); + + if( dwLength1 < firmware_minsize[ i ] || + dwLength1 > firmware_maxsize[ i ] || + dwLength2 < firmware_minsize[ i ] || + dwLength2 > dwLength1 || + dwLength3 > dwLength1 || + dwLength2>>9 != dwLength3 || + dwLength2+dwLength3+512 != dwLength1 ) + { + fprintf( stderr, "This doesn't look like a valid encrypted " + "iHP firmware - reason: file 'length' data\n" ); + exit( -1 ); + }; + + pChecksums = ppChecksums = (unsigned char *)( malloc( dwLength3 ) ); + + if( modify ) + { + modifyheader( headerdata ); + }; + + if( stripmode == STRIP_NONE ) + fwrite( headerdata, 512, 1, outfile ); + + memset( blockdata, 0, 16 ); + + ck = 0; + while( ( fp < dwLength2 ) && + ( lenread = fread( blockdata+16, 1, 16, infile ) ) == 16 ) + { + fp += 16; + + for( i=0; i<16; ++i ) + { + newmunge = blockdata[16+i] ^ munge[i]; + out[i] = newmunge ^ blockdata[i]; + blockdata[i] = newmunge; + ck += out[i]; + } + + if( fp > ESTF_SIZE || stripmode != STRIP_HEADER_CHECKSUM_ESTF ) + { + fwrite( out+4, 1, 12, outfile ); + fwrite( out, 1, 4, outfile ); + } + else + { + if( ESTF_SIZE - fp < 16 ) + { + memcpy( out+4, blockdata+16, 12 ); + memcpy( out, blockdata+28, 4 ); + fwrite( blockdata+16+ESTF_SIZE-fp, 1, ESTF_SIZE-fp, outfile ); + } + } + + + if( s == 496 ) + { + s = 0; + memset( blockdata, 0, 16 ); + *ppChecksums++ = ck; + ck = 0; + } + else + s+=16; + }; + + if( fp != dwLength2 ) + { + fprintf( stderr, "This doesn't look like a valid encrypted " + "iHP firmware - reason: 'length2' mismatch\n" ); + exit( -1 ); + }; + + fp = 0; + ppChecksums = pChecksums; + while( ( fp < dwLength3 ) && + ( lenread = fread( blockdata, 1, 32, infile ) ) > 0 ) + { + fp += lenread; + if( stripmode == STRIP_NONE ) + fwrite( blockdata, 1, lenread, outfile ); + if( memcmp( ppChecksums, blockdata, lenread ) != 0 ) + { + fprintf( stderr, "This doesn't look like a valid encrypted " + "iHP firmware - reason: Checksum mismatch!" ); + exit( -1 ); + }; + ppChecksums += lenread; + }; + + if( fp != dwLength3 ) + { + fprintf( stderr, "This doesn't look like a valid encrypted " + "iHP firmware - reason: 'length3' mismatch\n" ); + exit( -1 ); + }; + + + fprintf( stderr, "File decoded correctly and all checksums matched!\n" ); + switch( stripmode ) + { + default: + case STRIP_NONE: + fprintf(stderr, "Output file contains all headers and " + "checksums\n"); + break; + case STRIP_HEADER_CHECKSUM: + fprintf( stderr, "NB: output file contains only ESTFBINR header" + " and decoded firmware code\n" ); + break; + case STRIP_HEADER_CHECKSUM_ESTF: + fprintf( stderr, "NB: output file contains only raw decoded " + "firmware code\n" ); + break; + }; + + return 0; +}; + +int iriver_encode(char *infile_name, char *outfile_name, BOOL modify ) +{ + FILE * infile = NULL; + FILE * outfile = NULL; + int i = -1; + unsigned char headerdata[512]; + unsigned long dwLength1, dwLength2, dwLength3, fp = 0; + unsigned char blockdata[16+16]; + unsigned char out[16]; + unsigned char newmunge; + signed long lenread; + int s = 0; + unsigned char * pChecksums, * ppChecksums; + unsigned char ck; + + enum striptype stripmode = STRIP_NONE; + + infile = openinfile(infile_name); + outfile = openoutfile(outfile_name); + + lenread = fread( headerdata, 1, 512, infile ); + if( lenread != 512 ) + { + fprintf( stderr, "This doesn't look like a valid decoded " + "iHP firmware - reason: header length\n" ); + exit( -1 ); + }; + + if( modify ) + { + modifyheader( headerdata ); /* reversible */ + }; + + i = testheader( headerdata ); + if( i == -1 ) + { + fprintf( stderr, "This firmware is for an unknown model, or is not" + " a valid decoded iHP firmware\n" ); + exit( -1 ); + }; + fprintf( stderr, "Model %s\n", models[ i ] ); + + dwLength1 = headerdata[0] | (headerdata[1]<<8) | + (headerdata[2]<<16) | (headerdata[3]<<24); + dwLength2 = headerdata[4] | (headerdata[5]<<8) | + (headerdata[6]<<16) | (headerdata[7]<<24); + dwLength3 = headerdata[8] | (headerdata[9]<<8) | + (headerdata[10]<<16) | (headerdata[11]<<24); + + if( dwLength1 < firmware_minsize[i] || + dwLength1 > firmware_maxsize[i] || + dwLength2 < firmware_minsize[i] || + dwLength2 > dwLength1 || + dwLength3 > dwLength1 || + dwLength2+dwLength3+512 != dwLength1 ) + { + fprintf( stderr, "This doesn't look like a valid decoded iHP" + " firmware - reason: file 'length' data\n" ); + exit( -1 ); + }; + + pChecksums = ppChecksums = (unsigned char *)( malloc( dwLength3 ) ); + + fwrite( headerdata, 512, 1, outfile ); + + memset( blockdata, 0, 16 ); + ck = 0; + while( ( fp < dwLength2 ) && + ( lenread = fread( blockdata+16, 1, 16, infile ) ) == 16 ) + { + fp += 16; + for( i=0; i<16; ++i ) + { + newmunge = blockdata[16+((12+i)&0xf)] ^ blockdata[i]; + out[i] = newmunge ^ munge[i]; + ck += blockdata[16+i]; + blockdata[i] = newmunge; + }; + fwrite( out, 1, 16, outfile ); + + if( s == 496 ) + { + s = 0; + memset( blockdata, 0, 16 ); + *ppChecksums++ = ck; + ck = 0; + } + else + s+=16; + }; + + if( fp != dwLength2 ) + { + fprintf( stderr, "This doesn't look like a valid decoded " + "iHP firmware - reason: 'length1' mismatch\n" ); + exit( -1 ); + }; + + /* write out remainder w/out applying descrambler */ + fp = 0; + lenread = dwLength3; + ppChecksums = pChecksums; + while( ( fp < dwLength3) && + ( lenread = fwrite( ppChecksums, 1, lenread, outfile ) ) > 0 ) + { + fp += lenread; + ppChecksums += lenread; + lenread = dwLength3 - fp; + }; + + if( fp != dwLength3 ) + { + fprintf( stderr, "This doesn't look like a valid decoded " + "iHP firmware - reason: 'length2' mismatch\n" ); + exit( -1 ); + }; + + fprintf( stderr, "File encoded successfully and checksum table built!\n" ); + + return 0; +}; diff --git a/tools/iriver.h b/tools/iriver.h new file mode 100644 index 0000000000..96326c4e48 --- /dev/null +++ b/tools/iriver.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2005 by Daniel 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. + * + ****************************************************************************/ + +#define TRUE 1 +#define FALSE 0 + +#define BOOL unsigned int + +#define ESTF_SIZE 32 + +enum striptype +{ + STRIP_NONE, + STRIP_HEADER_CHECKSUM, + STRIP_HEADER_CHECKSUM_ESTF +}; + +/* protos for iriver.c */ +int iriver_decode(char *infile, char *outfile, BOOL modify, + enum striptype stripmode ); +int iriver_encode(char *infile_name, char *outfile_name, BOOL modify ); diff --git a/tools/scramble.c b/tools/scramble.c index 4e3addfc26..df6c5ac084 100644 --- a/tools/scramble.c +++ b/tools/scramble.c @@ -20,6 +20,8 @@ #include #include +#include "iriver.h" + void int2le(unsigned int val, unsigned char* addr) { addr[0] = val & 0xFF; @@ -36,6 +38,22 @@ void int2be(unsigned int val, unsigned char* addr) addr[3] = val & 0xFF; } +void usage(void) +{ + printf("usage: scramble [options] [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" + "\nNo option results in Archos standard player/recorder format.\n"); + + exit(1); +} + int main (int argc, char** argv) { unsigned long length,i,slen; @@ -51,16 +69,7 @@ int main (int argc, char** argv) enum { none, scramble, xor } method = scramble; if (argc < 3) { - printf("usage: %s [options] [xor string]\n",argv[0]); - printf("options:\n"); - printf("\t-fm Archos FM recorder format\n"); - printf("\t-v2 Archos V2 recorder format\n"); - printf("\t-ofm Archos Ondio FM recorder format\n"); - printf("\t-osp Archos Ondio SP format\n"); - printf("\t-neo SSI Neo format\n"); - printf("\t-mm=X Archos Multimedia format (X values: A=JBMM, B=AV1xx, C=AV3xx)\n"); - printf("\nNo option results in Archos standard player/recorder format.\n"); - return -1; + usage(); } if(!strcmp(argv[1], "-fm")) { @@ -111,6 +120,14 @@ int main (int argc, char** argv) } } + 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) {