woops, menus stopped getting redrawn when the setting screen exited
[Rockbox.git] / rbutil / irivertools.cpp
blob1e6341923d7d184dd44300e8a0e23deda0e594b3
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * Module: rbutil
9 * File: irivertools.cpp
11 * Copyright (C) 2007 Dominik Wenger
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
19 ****************************************************************************/
21 #include "irivertools.h"
22 #include "md5sum.h"
25 const unsigned char munge[] = {
26 0x7a, 0x36, 0xc4, 0x43, 0x49, 0x6b, 0x35, 0x4e, 0xa3, 0x46, 0x25, 0x84,
27 0x4d, 0x73, 0x74, 0x61
30 const unsigned char header_modify[] = "* IHPFIRM-DECODED ";
32 const char * const models[] = { "iHP-100", "iHP-120/iHP-140", "H300 series",
33 NULL };
35 /* aligns with models array; expected min firmware size */
36 const unsigned int firmware_minsize[] = { 0x100000, 0x100000, 0x200000 };
37 /* aligns with models array; expected max firmware size */
38 const unsigned int firmware_maxsize[] = { 0x200000, 0x200000, 0x400000 };
40 const unsigned char header[][16] = {
41 { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
42 { 0x20, 0x03, 0x08, 0x27, 0x24, 0x00, 0x02, 0x30, 0x19, 0x17, 0x65, 0x73,
43 0x85, 0x32, 0x83, 0x22 },
44 { 0x20, 0x04, 0x03, 0x27, 0x20, 0x50, 0x01, 0x70, 0x80, 0x30, 0x80, 0x06,
45 0x30, 0x19, 0x17, 0x65 }
48 /* begin mkboot.c excerpt */
49 unsigned char image[0x400000 + 0x220 + 0x400000/0x200];
51 bool mkboot(wxString infile, wxString outfile,wxString bootloader,int origin)
53 wxString err;
54 int i;
55 int len,bllen;
56 int actual_length, total_length, binary_length, num_chksums;
58 memset(image, 0xff, sizeof(image));
60 /* First, read the iriver original firmware into the image */
61 wxFile f;
62 if(!f.Open(infile))
64 ERR_DIALOG(wxT("Could not open: ") + infile, wxT("mkboot"));
65 return false;
67 i = f.Read(image,16);
68 if(i < 16) {
69 ERR_DIALOG(wxT("reading header failed"), wxT("mkboot"));
70 return false;
73 /* This is the length of the binary image without the scrambling
74 overhead (but including the ESTFBINR header) */
75 binary_length = image[4] + (image[5] << 8) +
76 (image[6] << 16) + (image[7] << 24);
78 /* Read the rest of the binary data, but not the checksum block */
79 len = binary_length+0x200-16;
80 i = f.Read(image+16, len);
81 if(i < len) {
82 ERR_DIALOG(wxT("reading firmware failed"),wxT("mkboot"));
83 return false;
86 f.Close();
87 /* Now, read the boot loader into the image */
88 if(!f.Open(bootloader))
90 ERR_DIALOG(wxT("Could not open: ") + bootloader, wxT("mkboot"));
91 return false;
94 bllen = f.Length();
96 i = f.Read(image+0x220 + origin, bllen);
97 if(i < bllen) {
98 ERR_DIALOG(wxT("reading bootloader failed"), wxT("mkboot"));
99 return false;
102 f.Close();
104 if(!f.Open(outfile,wxFile::write))
106 ERR_DIALOG(wxT("Could not open: ") + outfile, wxT("mkboot"));
107 return false;
110 /* Patch the reset vector to start the boot loader */
111 image[0x220 + 4] = image[origin + 0x220 + 4];
112 image[0x220 + 5] = image[origin + 0x220 + 5];
113 image[0x220 + 6] = image[origin + 0x220 + 6];
114 image[0x220 + 7] = image[origin + 0x220 + 7];
116 /* This is the actual length of the binary, excluding all headers */
117 actual_length = origin + bllen;
119 /* Patch the ESTFBINR header */
120 image[0x20c] = (actual_length >> 24) & 0xff;
121 image[0x20d] = (actual_length >> 16) & 0xff;
122 image[0x20e] = (actual_length >> 8) & 0xff;
123 image[0x20f] = actual_length & 0xff;
125 image[0x21c] = (actual_length >> 24) & 0xff;
126 image[0x21d] = (actual_length >> 16) & 0xff;
127 image[0x21e] = (actual_length >> 8) & 0xff;
128 image[0x21f] = actual_length & 0xff;
130 /* This is the length of the binary, including the ESTFBINR header and
131 rounded up to the nearest 0x200 boundary */
132 binary_length = (actual_length + 0x20 + 0x1ff) & 0xfffffe00;
134 /* The number of checksums, i.e number of 0x200 byte blocks */
135 num_chksums = binary_length / 0x200;
137 /* The total file length, including all headers and checksums */
138 total_length = binary_length + num_chksums + 0x200;
140 /* Patch the scrambler header with the new length info */
141 image[0] = total_length & 0xff;
142 image[1] = (total_length >> 8) & 0xff;
143 image[2] = (total_length >> 16) & 0xff;
144 image[3] = (total_length >> 24) & 0xff;
146 image[4] = binary_length & 0xff;
147 image[5] = (binary_length >> 8) & 0xff;
148 image[6] = (binary_length >> 16) & 0xff;
149 image[7] = (binary_length >> 24) & 0xff;
151 image[8] = num_chksums & 0xff;
152 image[9] = (num_chksums >> 8) & 0xff;
153 image[10] = (num_chksums >> 16) & 0xff;
154 image[11] = (num_chksums >> 24) & 0xff;
156 i = f.Write(image,total_length);
157 if(i < total_length) {
158 ERR_DIALOG(wxT("writing bootloader failed"), wxT("mkboot"));
159 return false;
162 f.Close();
164 return true;
167 /* end mkboot.c excerpt */
170 int intable(char *md5, struct sumpairs *table, int len)
172 int i;
173 for (i = 0; i < len; i++) {
174 if (strncmp(md5, table[i].unpatched, 32) == 0) {
175 return i;
178 return -1;
184 static int testheader( const unsigned char * const data )
186 const unsigned char * const d = data+16;
187 const char * const * m = models;
188 int index = 0;
189 while( *m )
191 if( memcmp( header[ index ], d, 16 ) == 0 )
192 return index;
193 index++;
194 m++;
196 return -1;
199 static void modifyheader( unsigned char * data )
201 const unsigned char * h = header_modify;
202 int i;
203 for( i=0; i<512; i++ )
205 if( *h == '\0' )
206 h = header_modify;
207 *data++ ^= *h++;
211 int iriver_decode(wxString infile_name, wxString outfile_name, unsigned int modify,
212 enum striptype stripmode )
214 wxString err;
215 wxFile infile;
216 wxFile outfile;
217 int i = -1;
218 unsigned char headerdata[512];
219 unsigned long dwLength1, dwLength2, dwLength3, fp = 0;
220 unsigned char blockdata[16+16];
221 unsigned char out[16];
222 unsigned char newmunge;
223 signed long lenread;
224 int s = 0;
225 unsigned char * pChecksums, * ppChecksums = 0;
226 unsigned char ck;
228 if(!infile.Open(infile_name))
230 ERR_DIALOG(wxT("Could not open: ") + infile_name, wxT("iriver_decode"));
231 return -1;
233 if(!outfile.Open(outfile_name,wxFile::write))
235 ERR_DIALOG(wxT("Could not open: ") + outfile_name,
236 wxT("iriver_decode"));
237 return -1;
239 lenread = infile.Read( headerdata, 512);
240 if( lenread != 512 )
242 ERR_DIALOG(wxT("This doesn't look like a valid encrypted iHP "
243 "firmware - reason: header length\n"),wxT("iriver_decode"));
245 infile.Close();
246 outfile.Close();
247 return -1;
250 i = testheader( headerdata );
251 if( i == -1 )
253 ERR_DIALOG( wxT( "This firmware is for an unknown model, or is not"
254 " a valid encrypted iHP firmware\n" ),wxT("iriver_decode"));
255 infile.Close();
256 outfile.Close();
257 return -1;
259 fprintf( stderr, "Model %s\n", models[ i ] );
261 dwLength1 = headerdata[0] | (headerdata[1]<<8) |
262 (headerdata[2]<<16) | (headerdata[3]<<24);
263 dwLength2 = headerdata[4] | (headerdata[5]<<8) |
264 (headerdata[6]<<16) | (headerdata[7]<<24);
265 dwLength3 = headerdata[8] | (headerdata[9]<<8) |
266 (headerdata[10]<<16) | (headerdata[11]<<24);
268 if( dwLength1 < firmware_minsize[ i ] ||
269 dwLength1 > firmware_maxsize[ i ] ||
270 dwLength2 < firmware_minsize[ i ] ||
271 dwLength2 > dwLength1 ||
272 dwLength3 > dwLength1 ||
273 dwLength2>>9 != dwLength3 ||
274 dwLength2+dwLength3+512 != dwLength1 )
276 ERR_DIALOG( wxT( "This doesn't look like a valid encrypted "
277 "iHP firmware - reason: file 'length' data\n" ),wxT("iriver_decode"));
278 infile.Close();
279 outfile.Close();
280 return -1;
283 pChecksums = ppChecksums = (unsigned char *)( malloc( dwLength3 ) );
285 if( modify )
287 modifyheader( headerdata );
290 if( stripmode == STRIP_NONE )
291 outfile.Write( headerdata, 512);
293 memset( blockdata, 0, 16 );
295 ck = 0;
296 while( ( fp < dwLength2 ) &&
297 ( lenread = infile.Read( blockdata+16, 16) == 16) )
299 fp += 16;
301 for( i=0; i<16; ++i )
303 newmunge = blockdata[16+i] ^ munge[i];
304 out[i] = newmunge ^ blockdata[i];
305 blockdata[i] = newmunge;
306 ck += out[i];
309 if( fp > ESTF_SIZE || stripmode != STRIP_HEADER_CHECKSUM_ESTF )
311 outfile.Write( out+4, 12);
312 outfile.Write( out, 4);
314 else
316 if( ESTF_SIZE - fp < 16 )
318 memcpy( out+4, blockdata+16, 12 );
319 memcpy( out, blockdata+28, 4 );
320 outfile.Write( blockdata+16+ESTF_SIZE-fp, ESTF_SIZE-fp);
325 if( s == 496 )
327 s = 0;
328 memset( blockdata, 0, 16 );
329 *ppChecksums++ = ck;
330 ck = 0;
332 else
333 s+=16;
336 if( fp != dwLength2 )
338 ERR_DIALOG( wxT( "This doesn't look like a valid encrypted "
339 "iHP firmware - reason: 'length2' mismatch\n" ),wxT("iriver_decode"));
340 infile.Close();
341 outfile.Close();
342 return -1;
345 fp = 0;
346 ppChecksums = pChecksums;
347 while( ( fp < dwLength3 ) &&
348 ( lenread = infile.Read( blockdata, 32 ) ) > 0 )
350 fp += lenread;
351 if( stripmode == STRIP_NONE )
352 outfile.Write( blockdata, lenread );
353 if( memcmp( ppChecksums, blockdata, lenread ) != 0 )
355 ERR_DIALOG( wxT( "This doesn't look like a valid encrypted "
356 "iHP firmware - reason: Checksum mismatch!" ),wxT("iriver_decode"));
357 infile.Close();
358 outfile.Close();
359 return -1;
361 ppChecksums += lenread;
364 if( fp != dwLength3 )
366 ERR_DIALOG(wxT( "This doesn't look like a valid encrypted "
367 "iHP firmware - reason: 'length3' mismatch\n" ),wxT("iriver_decode"));
368 infile.Close();
369 outfile.Close();
370 return -1;
374 fprintf( stderr, "File decoded correctly and all checksums matched!\n" );
375 switch( stripmode )
377 default:
378 case STRIP_NONE:
379 fprintf(stderr, "Output file contains all headers and "
380 "checksums\n");
381 break;
382 case STRIP_HEADER_CHECKSUM:
383 fprintf( stderr, "NB: output file contains only ESTFBINR header"
384 " and decoded firmware code\n" );
385 break;
386 case STRIP_HEADER_CHECKSUM_ESTF:
387 fprintf( stderr, "NB: output file contains only raw decoded "
388 "firmware code\n" );
389 break;
392 infile.Close();
393 outfile.Close();
394 return 0;
398 int iriver_encode(wxString infile_name, wxString outfile_name, unsigned int modify )
400 wxString err;
401 wxFile infile;
402 wxFile outfile;
403 int i = -1;
404 unsigned char headerdata[512];
405 unsigned long dwLength1, dwLength2, dwLength3, fp = 0;
406 unsigned char blockdata[16+16];
407 unsigned char out[16];
408 unsigned char newmunge;
409 signed long lenread;
410 int s = 0;
411 unsigned char * pChecksums, * ppChecksums;
412 unsigned char ck;
414 if(!infile.Open(infile_name,wxFile::read))
416 ERR_DIALOG(wxT("Could not open: ") + infile_name, wxT("iriver_decode"));
417 return -1;
419 if(!outfile.Open(outfile_name,wxFile::write))
421 ERR_DIALOG(wxT("Could not open: ") + outfile_name,
422 wxT("iriver_decode"));
423 return -1;
426 lenread = infile.Read( headerdata, 512 );
427 if( lenread != 512 )
429 ERR_DIALOG(wxT("This doesn't look like a valid decoded "
430 "iHP firmware - reason: header length\n"), wxT("iriver_decode"));
431 infile.Close();
432 outfile.Close();
435 if( modify )
437 modifyheader( headerdata ); /* reversible */
440 i = testheader( headerdata );
441 if( i == -1 )
443 ERR_DIALOG(wxT("This firmware is for an unknown model, or is not"
444 " a valid decoded iHP firmware\n"), wxT("iriver_decode"));
445 infile.Close();
446 outfile.Close();
448 fprintf( stderr, "Model %s\n", models[ i ] );
450 dwLength1 = headerdata[0] | (headerdata[1]<<8) |
451 (headerdata[2]<<16) | (headerdata[3]<<24);
452 dwLength2 = headerdata[4] | (headerdata[5]<<8) |
453 (headerdata[6]<<16) | (headerdata[7]<<24);
454 dwLength3 = headerdata[8] | (headerdata[9]<<8) |
455 (headerdata[10]<<16) | (headerdata[11]<<24);
457 if( dwLength1 < firmware_minsize[i] ||
458 dwLength1 > firmware_maxsize[i] ||
459 dwLength2 < firmware_minsize[i] ||
460 dwLength2 > dwLength1 ||
461 dwLength3 > dwLength1 ||
462 dwLength2+dwLength3+512 != dwLength1 )
464 ERR_DIALOG(wxT("This doesn't look like a valid decoded iHP"
465 " firmware - reason: file 'length' data\n"), wxT("iriver_decode"));
466 infile.Close();
467 outfile.Close();
470 pChecksums = ppChecksums = (unsigned char *)( malloc( dwLength3 ) );
472 outfile.Write( headerdata, 512);
474 memset( blockdata, 0, 16 );
475 ck = 0;
476 while( ( fp < dwLength2 ) &&
477 ( lenread = infile.Read( blockdata+16, 16) ) == 16 )
479 fp += 16;
480 for( i=0; i<16; ++i )
482 newmunge = blockdata[16+((12+i)&0xf)] ^ blockdata[i];
483 out[i] = newmunge ^ munge[i];
484 ck += blockdata[16+i];
485 blockdata[i] = newmunge;
487 outfile.Write( out, 16);
489 if( s == 496 )
491 s = 0;
492 memset( blockdata, 0, 16 );
493 *ppChecksums++ = ck;
494 ck = 0;
496 else
497 s+=16;
500 if( fp != dwLength2 )
502 ERR_DIALOG(wxT("This doesn't look like a valid decoded "
503 "iHP firmware - reason: 'length1' mismatch\n"), wxT("iriver_decode"));
504 infile.Close();
505 outfile.Close();
508 /* write out remainder w/out applying descrambler */
509 fp = 0;
510 lenread = dwLength3;
511 ppChecksums = pChecksums;
512 while( ( fp < dwLength3) &&
513 ( lenread = outfile.Write( ppChecksums, lenread) ) > 0 )
515 fp += lenread;
516 ppChecksums += lenread;
517 lenread = dwLength3 - fp;
520 if( fp != dwLength3 )
522 ERR_DIALOG(wxT("This doesn't look like a valid decoded "
523 "iHP firmware - reason: 'length2' mismatch\n"), wxT("iriver_decode"));
524 infile.Close();
525 outfile.Close();
528 fprintf( stderr, "File encoded successfully and checksum table built!\n" );
530 infile.Close();
531 outfile.Close();
532 return 0;
536 bool PatchFirmware(wxString firmware,wxString bootloader,int series, int table_entry)
538 wxString name1, name2, name3;
540 char md5sum_str[32];
541 struct sumpairs *sums;
542 int origin;
544 /* get pointer to the correct bootloader.bin */
545 switch(series) {
546 case 100:
547 sums = &h100pairs[0];
548 origin = 0x1f0000;
549 break;
550 case 120:
551 sums = &h120pairs[0];
552 origin = 0x1f0000;
553 break;
554 case 300:
555 sums = &h300pairs[0];
556 origin = 0x3f0000;
557 break;
560 name1 = gv->stdpaths->GetUserDataDir()
561 + wxT("" PATH_SEP "download" PATH_SEP "firmware.bin"),
562 /* descrambled file */
563 name2 = gv->stdpaths->GetUserDataDir()
564 + wxT("" PATH_SEP "download" PATH_SEP "new.bin");
565 /* patched file */
566 name3 = gv->stdpaths->GetUserDataDir()
567 + wxT("" PATH_SEP "download" PATH_SEP "new.hex");
568 if (iriver_decode(firmware, name1, FALSE, STRIP_NONE) == -1) {
569 ERR_DIALOG(wxT("Error in descramble"), wxT("Descramble Firmware"));
570 wxRemoveFile(name1);
571 wxRemoveFile(name2);
572 wxRemoveFile(name3);
573 return false;
575 if (!mkboot(name1, name2, bootloader, origin)) {
576 ERR_DIALOG(wxT("Error in patching"),wxT("Patching Firmware"));
577 wxRemoveFile(name1);
578 wxRemoveFile(name2);
579 wxRemoveFile(name3);
580 return false;
582 if (iriver_encode(name2, name3, FALSE) == -1) {
583 ERR_DIALOG(wxT("Error in scramble"),wxT("Scramble Firmware"));
584 wxRemoveFile(name1);
585 wxRemoveFile(name2);
586 wxRemoveFile(name3);
587 return false;
589 /* now md5sum it */
590 if (!FileMD5(name3, md5sum_str)) {
591 ERR_DIALOG(wxT("Error in checksumming"),wxT("Checksumming Firmware"));
592 wxRemoveFile(name1);
593 wxRemoveFile(name2);
594 wxRemoveFile(name3);
595 return false;
597 if (strncmp(sums[table_entry].patched, md5sum_str, 32) == 0) {
598 /* delete temp files */
599 wxRemoveFile(name1);
600 wxRemoveFile(name2);
603 return true;