4fd1e4364b
Change-Id: I87b71654ce8669904cfb587cb0673a46455bcbd1
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;
|
|
}
|
|
|