forked from len0rd/rockbox
		
	
		
			
				
	
	
		
			517 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			517 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /***************************************************************************
 | |
|  *             __________               __   ___.
 | |
|  *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 | |
|  *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 | |
|  *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 | |
|  *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 | |
|  *                     \/            \/     \/    \/            \/
 | |
|  * $Id$
 | |
|  *
 | |
|  * Copyright (C) 2005 by Thom Johansen
 | |
|  *
 | |
|  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 | |
|  * KIND, either express or implied.
 | |
|  *
 | |
|  ****************************************************************************/
 | |
| 
 | |
| /* TODO: integrate the iriver.c and mkboot stuff better, they're pretty much
 | |
|  * intended to be called from a command line tool, and i haven't changed that.
 | |
|  */
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <string.h>
 | |
| #include <tchar.h>
 | |
| #include <windows.h>
 | |
| #include "iriver.h"
 | |
| #include "md5.h"
 | |
| #include "resource.h"
 | |
| 
 | |
| #define WINDOW_WIDTH 280 
 | |
| #define WINDOW_HEIGHT 130 
 | |
| 
 | |
| #define IDM_RESTORE 1000
 | |
| #define IDM_EXIT 1010
 | |
| 
 | |
| #define LABEL_FILENAME 0
 | |
| #define EDIT_FILENAME 1
 | |
| #define BUTTON_BROWSE 2
 | |
| #define BUTTON_PATCH 3
 | |
| #define WM_COMMANDDRIVEN WM_APP
 | |
| 
 | |
| #define CTL_NUM 4
 | |
| 
 | |
| TCHAR appsversion[] = TEXT("Rockbox firmware patcher V" APPSVERSION);
 | |
| 
 | |
| struct sumpairs {
 | |
|     char *unpatched;
 | |
|     char *patched;
 | |
| };
 | |
| 
 | |
| /* precalculated checksums for H110/H115 */
 | |
| static struct sumpairs h100pairs[] = {
 | |
| #include "h100sums.h"
 | |
| };
 | |
| 
 | |
| /* precalculated checksums for H120/H140 */
 | |
| static struct sumpairs h120pairs[] = {
 | |
| #include "h120sums.h"
 | |
| };
 | |
| 
 | |
| /* precalculated checksums for H320/H340 */
 | |
| static struct sumpairs h300pairs[] = {
 | |
| #include "h300sums.h"
 | |
| };
 | |
| 
 | |
| HICON rbicon;
 | |
| HFONT deffont;
 | |
| HWND controls[CTL_NUM];
 | |
| 
 | |
| /* begin mkboot.c excerpt */
 | |
| 
 | |
| unsigned char image[0x400000 + 0x220 + 0x400000/0x200];
 | |
| 
 | |
| int mkboot(TCHAR *infile, TCHAR *outfile, unsigned char *bldata, int bllen,
 | |
|            int origin)
 | |
| {
 | |
|     FILE *f;
 | |
|     int i;
 | |
|     int len;
 | |
|     int actual_length, total_length, binary_length, num_chksums;
 | |
| 
 | |
|     memset(image, 0xff, sizeof(image));
 | |
| 
 | |
|     /* First, read the iriver original firmware into the image */
 | |
|     f = _tfopen(infile, TEXT("rb"));
 | |
|     if(!f) {
 | |
|         _tperror(infile);
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     i = fread(image, 1, 16, f);
 | |
|     if(i < 16) {
 | |
|         _tperror(infile);
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     /* This is the length of the binary image without the scrambling
 | |
|        overhead (but including the ESTFBINR header) */
 | |
|     binary_length = image[4] + (image[5] << 8) +
 | |
|         (image[6] << 16) + (image[7] << 24);
 | |
| 
 | |
|     /* Read the rest of the binary data, but not the checksum block */
 | |
|     len = binary_length+0x200-16;
 | |
|     i = fread(image+16, 1, len, f);
 | |
|     if(i < len) {
 | |
|         _tperror(infile);
 | |
|         return 0;
 | |
|     }
 | |
|     
 | |
|     fclose(f);
 | |
| 
 | |
|     memcpy(image + 0x220 + origin, bldata, bllen);
 | |
|     
 | |
|     f = _tfopen(outfile, TEXT("wb"));
 | |
|     if(!f) {
 | |
|         _tperror(outfile);
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     /* Patch the reset vector to start the boot loader */
 | |
|     image[0x220 + 4] = image[origin + 0x220 + 4];
 | |
|     image[0x220 + 5] = image[origin + 0x220 + 5];
 | |
|     image[0x220 + 6] = image[origin + 0x220 + 6];
 | |
|     image[0x220 + 7] = image[origin + 0x220 + 7];
 | |
| 
 | |
|     /* This is the actual length of the binary, excluding all headers */
 | |
|     actual_length = origin + bllen;
 | |
| 
 | |
|     /* Patch the ESTFBINR header */
 | |
|     image[0x20c] = (actual_length >> 24) & 0xff;
 | |
|     image[0x20d] = (actual_length >> 16) & 0xff;
 | |
|     image[0x20e] = (actual_length >> 8) & 0xff;
 | |
|     image[0x20f] = actual_length & 0xff;
 | |
|     
 | |
|     image[0x21c] = (actual_length >> 24) & 0xff;
 | |
|     image[0x21d] = (actual_length >> 16) & 0xff;
 | |
|     image[0x21e] = (actual_length >> 8) & 0xff;
 | |
|     image[0x21f] = actual_length & 0xff;
 | |
| 
 | |
|     /* This is the length of the binary, including the ESTFBINR header and
 | |
|        rounded up to the nearest 0x200 boundary */
 | |
|     binary_length = (actual_length + 0x20 + 0x1ff) & 0xfffffe00;
 | |
| 
 | |
|     /* The number of checksums, i.e number of 0x200 byte blocks */
 | |
|     num_chksums = binary_length / 0x200;
 | |
| 
 | |
|     /* The total file length, including all headers and checksums */
 | |
|     total_length = binary_length + num_chksums + 0x200;
 | |
| 
 | |
|     /* Patch the scrambler header with the new length info */
 | |
|     image[0] = total_length & 0xff;
 | |
|     image[1] = (total_length >> 8) & 0xff;
 | |
|     image[2] = (total_length >> 16) & 0xff;
 | |
|     image[3] = (total_length >> 24) & 0xff;
 | |
|     
 | |
|     image[4] = binary_length & 0xff;
 | |
|     image[5] = (binary_length >> 8) & 0xff;
 | |
|     image[6] = (binary_length >> 16) & 0xff;
 | |
|     image[7] = (binary_length >> 24) & 0xff;
 | |
|     
 | |
|     image[8] = num_chksums & 0xff;
 | |
|     image[9] = (num_chksums >> 8) & 0xff;
 | |
|     image[10] = (num_chksums >> 16) & 0xff;
 | |
|     image[11] = (num_chksums >> 24) & 0xff;
 | |
|     
 | |
|     i = fwrite(image, 1, total_length, f);
 | |
|     if(i < total_length) {
 | |
|         _tperror(outfile);
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     fclose(f);
 | |
|     
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| /* end mkboot.c excerpt */
 | |
| 
 | |
| int intable(char *md5, struct sumpairs *table, int len)
 | |
| {
 | |
|     int i;
 | |
|     for (i = 0; i < len; i++) {
 | |
|         if (strncmp(md5, table[i].unpatched, 32) == 0) {
 | |
|             return i;
 | |
|         }
 | |
|     }
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| int FileMD5(TCHAR *name, char *md5)
 | |
| {
 | |
|     int i, read;
 | |
|     md5_context ctx;
 | |
|     unsigned char md5sum[16];
 | |
|     unsigned char block[32768];
 | |
|     FILE *file;
 | |
|     
 | |
|     file = _tfopen(name, TEXT("rb"));
 | |
|     if (!file) {
 | |
|         MessageBox(NULL,
 | |
|                    TEXT("Could not open patched firmware for checksum check"),
 | |
|                    TEXT("Error"), MB_ICONERROR);
 | |
|         return 0;
 | |
|     }
 | |
|     md5_starts(&ctx);
 | |
|     while ((read = fread(block, 1, sizeof(block), file)) > 0) {
 | |
|         md5_update(&ctx, block, read);
 | |
|     }
 | |
|     fclose(file);
 | |
|     md5_finish(&ctx, md5sum);
 | |
|     for (i = 0; i < 16; ++i)
 | |
|         sprintf(md5 + 2*i, "%02x", md5sum[i]);
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| int PatchFirmware(int series, int table_entry)
 | |
| {
 | |
|     TCHAR fn[MAX_PATH];
 | |
|     TCHAR name1[MAX_PATH], name2[MAX_PATH], name3[MAX_PATH];
 | |
|     HRSRC res;
 | |
|     HGLOBAL resload;
 | |
|     unsigned char *bootloader;
 | |
|     unsigned char md5sum_str[256];
 | |
|     DWORD blsize;
 | |
|     int i;
 | |
|     struct sumpairs *sums;
 | |
|     int origin;
 | |
|     
 | |
|     /* get pointer to the correct bootloader.bin */
 | |
|     switch(series) {
 | |
|         case 100:
 | |
|             res = FindResource(NULL, MAKEINTRESOURCE(IDI_BOOTLOADERH100), TEXT("BIN"));
 | |
|             sums = &h100pairs[0];
 | |
|             origin = 0x1f0000;
 | |
|             break;
 | |
|         case 120:
 | |
|             res = FindResource(NULL, MAKEINTRESOURCE(IDI_BOOTLOADERH120), TEXT("BIN"));
 | |
|             sums = &h120pairs[0];
 | |
|             origin = 0x1f0000;
 | |
|             break;
 | |
|         case 300:
 | |
|             res = FindResource(NULL, MAKEINTRESOURCE(IDI_BOOTLOADERH300), TEXT("BIN"));
 | |
|             sums = &h300pairs[0];
 | |
|             origin = 0x3f0000;
 | |
|             break;
 | |
|     }
 | |
|     resload = LoadResource(NULL, res);
 | |
|     bootloader = (unsigned char *)LockResource(resload);
 | |
|     blsize = SizeofResource(NULL, res);
 | |
|     
 | |
|     /* get filename from edit box */
 | |
|     GetWindowText(controls[EDIT_FILENAME], fn, MAX_PATH);
 | |
|     /* store temp files in temp directory */
 | |
|     GetTempPath(MAX_PATH, name1);
 | |
|     GetTempPath(MAX_PATH, name2);
 | |
|     GetTempPath(MAX_PATH, name3);
 | |
|     _tcscat(name1, TEXT("firmware.bin")); /* descrambled file */
 | |
|     _tcscat(name2, TEXT("new.bin"));      /* patched file */
 | |
|     _tcscat(name3, TEXT("new.hex"));      /* patched and scrambled file */
 | |
|     if (iriver_decode(fn, name1, FALSE, STRIP_NONE) == -1) {
 | |
|         MessageBox(NULL, TEXT("Error in descramble"),
 | |
|                    TEXT("Error"), MB_ICONERROR);
 | |
|         goto error;
 | |
|     }
 | |
|     if (!mkboot(name1, name2, bootloader, blsize, origin)) {
 | |
|         MessageBox(NULL, TEXT("Error in patching"),
 | |
|                    TEXT("Error"), MB_ICONERROR);
 | |
|         goto error;
 | |
|     }
 | |
|     if (iriver_encode(name2, name3, FALSE) == -1) {
 | |
|         MessageBox(NULL, TEXT("Error in scramble"),
 | |
|                    TEXT("Error"), MB_ICONERROR);
 | |
|         goto error;
 | |
|     }
 | |
|     /* now md5sum it */
 | |
|     if (!FileMD5(name3, md5sum_str)) {
 | |
|         MessageBox(NULL, TEXT("Error in checksumming"),
 | |
|                    TEXT("Error"), MB_ICONERROR);
 | |
|         goto error;
 | |
|     }
 | |
|     if (strncmp(sums[table_entry].patched, md5sum_str, 32) == 0) {
 | |
|         /* delete temp files */
 | |
|         DeleteFile(name1);
 | |
|         DeleteFile(name2);
 | |
|         /* all is fine, rename the patched file to original name of the firmware */
 | |
|         if (DeleteFile(fn) && MoveFile(name3, fn)) {
 | |
|             return 1;
 | |
|         }
 | |
|         else {
 | |
|             DeleteFile(name3); /* Deleting a perfectly good firmware here really */
 | |
|             MessageBox(NULL,
 | |
|                        TEXT("Couldn't modify existing file.\n")
 | |
|                        TEXT("Check if file is write protected, then try again."),
 | |
|                        TEXT("Error"), MB_ICONERROR);
 | |
|             return 0;
 | |
|         }
 | |
|     }
 | |
|     MessageBox(NULL,
 | |
|                TEXT("Checksum doesn't match known good patched firmware.\n")
 | |
|                TEXT("Download another firmware image, then try again."),
 | |
|                TEXT("Error"), MB_ICONERROR);
 | |
| error:
 | |
|     /* delete all temp files, don't care if some aren't created yet */
 | |
|     DeleteFile(name1);
 | |
|     DeleteFile(name2);
 | |
|     DeleteFile(name3);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int FileDialog(TCHAR *fn)
 | |
| {
 | |
|     OPENFILENAME ofn;
 | |
|     TCHAR filename[MAX_PATH];
 | |
| 
 | |
|     ZeroMemory(&ofn, sizeof(ofn));
 | |
|     ofn.lStructSize = sizeof(ofn);
 | |
|     ofn.lpstrFile = filename;
 | |
|     ofn.lpstrFile[0] = '\0'; // no default filename
 | |
|     ofn.nMaxFile = sizeof(filename);
 | |
|     ofn.lpstrFilter = TEXT("Firmware\0*.HEX\0");
 | |
|     ofn.nFilterIndex = 1;
 | |
|     ofn.lpstrFileTitle = NULL;
 | |
|     ofn.nMaxFileTitle = 0;
 | |
|     ofn.lpstrInitialDir = NULL;
 | |
|     ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
 | |
|     
 | |
|     if (GetOpenFileName(&ofn) == TRUE) {
 | |
|         _tcscpy(fn, filename);
 | |
|         return 1;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 | |
| {
 | |
|    int i, series, table_entry;
 | |
|    unsigned char md5sum_str[256];
 | |
|    switch (msg) {
 | |
|    TCHAR fn[MAX_PATH];
 | |
|    case WM_CREATE:
 | |
|        /* text label */
 | |
|        controls[LABEL_FILENAME] = 
 | |
|            CreateWindowEx(0, TEXT("STATIC"), TEXT("Firmware file name:"),
 | |
|                           WS_CHILD | WS_VISIBLE, 10, 14,
 | |
|                           100, 32, hwnd, 0, 0, 0);
 | |
|        /* text field for inputing file name */
 | |
|        controls[EDIT_FILENAME] = 
 | |
|            CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("EDIT"), TEXT(""),
 | |
|                           WS_CHILD | WS_TABSTOP | WS_VISIBLE | ES_AUTOHSCROLL,
 | |
|                           10, 35, 180, 20, hwnd, 0, 0, 0);
 | |
|        /* browse button */
 | |
|        controls[BUTTON_BROWSE] =
 | |
|            CreateWindowEx(0, TEXT("BUTTON"), TEXT("Browse"),
 | |
|                           WS_CHILD | WS_TABSTOP | WS_VISIBLE, 200, 32, 70, 25,
 | |
|                           hwnd, 0, 0, 0);        
 | |
|        /* patch button */
 | |
|        controls[BUTTON_PATCH] =
 | |
|            CreateWindowEx(0, TEXT("BUTTON"), TEXT("Patch"),
 | |
|                           WS_CHILD | WS_TABSTOP | WS_VISIBLE, 90, 70, 90, 25,
 | |
|                           hwnd, 0, 0, 0);
 | |
|        /* set default font on all controls, will be ugly if we don't do this */
 | |
|        deffont = GetStockObject(DEFAULT_GUI_FONT);
 | |
|        for (i = 0; i < CTL_NUM; ++i) 
 | |
|            SendMessage(controls[i], WM_SETFONT, (WPARAM)deffont,
 | |
|                        MAKELPARAM(FALSE, 0));
 | |
|         break;
 | |
|     case WM_CLOSE:
 | |
|         DestroyWindow(hwnd);
 | |
|         break;
 | |
|     case WM_DESTROY:
 | |
|         PostQuitMessage(0);
 | |
|         break;
 | |
|     case WM_SIZE:
 | |
|         break;
 | |
|     case WM_COMMAND:
 | |
|         /* user pressed browse button */
 | |
|         if (((HWND)lParam == controls[BUTTON_BROWSE])) {
 | |
|             TCHAR buf[MAX_PATH];
 | |
|             if (FileDialog(buf))
 | |
|                 SetWindowText(controls[EDIT_FILENAME], buf);
 | |
|         }
 | |
|         /* user pressed patch button */
 | |
|         if (((HWND)lParam == controls[BUTTON_PATCH])) {
 | |
|             GetWindowText(controls[EDIT_FILENAME], fn, MAX_PATH);
 | |
|             if (!FileMD5(fn, md5sum_str)) {
 | |
|                 MessageBox(NULL, TEXT("Couldn't open firmware"), TEXT("Fail"), MB_OK);
 | |
|             }
 | |
|             else {
 | |
|                 /* Check firmware against md5sums in h120sums and h100sums */
 | |
|                 series = 0;
 | |
|                 table_entry = intable(md5sum_str, &h120pairs[0],
 | |
|                                       sizeof(h120pairs)/sizeof(struct sumpairs));
 | |
|                 if (table_entry >= 0) {
 | |
|                     series = 120;
 | |
|                 }
 | |
|                 else {
 | |
|                     table_entry = intable(md5sum_str, &h100pairs[0],
 | |
|                                           sizeof(h100pairs)/sizeof(struct sumpairs));
 | |
|                     if (table_entry >= 0) {
 | |
|                         series = 100;
 | |
|                     }
 | |
|                     else {
 | |
|                         table_entry =
 | |
|                             intable(md5sum_str, &h300pairs[0],
 | |
|                                     sizeof(h300pairs)/sizeof(struct sumpairs));
 | |
|                         if (table_entry >= 0)
 | |
|                             series = 300;
 | |
|                     }
 | |
|                 }
 | |
|                 if (series == 0) {
 | |
|                     MessageBox(NULL, TEXT("Unrecognised firmware"), TEXT("Fail"), MB_OK);
 | |
|                 }
 | |
|                 else if (PatchFirmware(series, table_entry))
 | |
|                     MessageBox(NULL, TEXT("Firmware patched successfully"),
 | |
|                                TEXT("Success"), MB_OK);
 | |
|             }
 | |
|         }
 | |
|         break;
 | |
|     case WM_COMMANDDRIVEN:
 | |
|         /* command line driven patch button */
 | |
|         SetWindowText(controls[EDIT_FILENAME], (LPCTSTR)wParam);
 | |
|         SendMessage(hwnd, WM_COMMAND, 0, (LPARAM)(controls[BUTTON_PATCH]) );
 | |
|         break;
 | |
|     default:
 | |
|         return DefWindowProc(hwnd, msg, wParam, lParam);
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| void getargs(LPTSTR p, int * argc, LPCTSTR * argv, int MAXARGS)
 | |
| {
 | |
|     int quote=FALSE,whitespace=FALSE;
 | |
|     LPCTSTR tok=p;
 | |
|     *argc=0;
 | |
|     while(*argc<MAXARGS)
 | |
|     {
 | |
|         if((*p==TEXT(' ') || *p==TEXT('\0')) && !quote)
 | |
|         {
 | |
|             if(!whitespace)
 | |
|             {
 | |
|                 whitespace=TRUE;
 | |
|                 *argv++ = tok;
 | |
|                 (*argc)++;
 | |
|             };
 | |
|             if(*p==TEXT('\0'))
 | |
|                 break;  // end of cmd line
 | |
|             *p=TEXT('\0');
 | |
|             p++;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             if(whitespace)
 | |
|                 tok=p;
 | |
|             whitespace=FALSE;
 | |
|             if(*p==TEXT('\"'))
 | |
|             {
 | |
|                 *p=TEXT(' ');
 | |
|                 if(!quote)
 | |
|                 {
 | |
|                     p++;
 | |
|                     tok=p;
 | |
|                 } 
 | |
|                 quote = !quote;
 | |
|             }
 | |
|             else p++;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| #define MAXARGC 4
 | |
| 
 | |
| int WINAPI WinMain(HINSTANCE instance, HINSTANCE prev_instance,
 | |
|                    LPSTR command_line, int command_show) 
 | |
| {
 | |
|     HWND window;
 | |
|     WNDCLASSEX wc;
 | |
|     MSG msg;
 | |
|     int argc;
 | |
|     LPCTSTR argv[MAXARGC] = { NULL };
 | |
|     LPTSTR cmdline = GetCommandLine();
 | |
| 
 | |
|     getargs(cmdline, &argc, argv, MAXARGC);
 | |
|     if (argc > 1)
 | |
|         command_show = SW_HIDE;
 | |
|     
 | |
|     rbicon = LoadIcon(instance, MAKEINTRESOURCE(IDI_RBICON));
 | |
|     ZeroMemory(&wc, sizeof(wc));
 | |
|     wc.cbSize = sizeof(wc);
 | |
|     wc.lpfnWndProc = WndProc;
 | |
|     wc.hInstance = instance;
 | |
|     wc.hIcon = rbicon;
 | |
|     wc.hCursor = LoadCursor(0, IDC_ARROW);
 | |
|     wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
 | |
|     wc.lpszClassName = TEXT("patcher");
 | |
|     RegisterClassEx(&wc);
 | |
| 
 | |
|     window = CreateWindowEx(0, TEXT("patcher"), appsversion,
 | |
|                             WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU |
 | |
|                             WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT,
 | |
|                             WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0, instance, NULL);
 | |
|     if (!window) return 0;
 | |
| 
 | |
|     ShowWindow(window, command_show);
 | |
| 
 | |
|     if (argc > 1) {
 | |
|         SendMessage(window, WM_COMMANDDRIVEN, (WPARAM)(argv[1]), 0);
 | |
|         SendMessage(window, WM_CLOSE, 0, 0);
 | |
|     }
 | |
| 
 | |
|     while (GetMessage(&msg, 0, 0, 0) > 0) {
 | |
|         if (!IsDialogMessage(window, &msg)) {
 | |
|             TranslateMessage(&msg);
 | |
|             DispatchMessage(&msg);
 | |
|         }
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 |