1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2005 by Thom Johansen
12 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
13 * KIND, either express or implied.
15 ****************************************************************************/
17 /* TODO: integrate the iriver.c and mkboot stuff better, they're pretty much
18 * intended to be called from a command line tool, and i haven't changed that.
29 #define WINDOW_WIDTH 280
30 #define WINDOW_HEIGHT 130
32 #define IDM_RESTORE 1000
35 #define LABEL_FILENAME 0
36 #define EDIT_FILENAME 1
37 #define BUTTON_BROWSE 2
38 #define BUTTON_PATCH 3
39 #define WM_COMMANDDRIVEN WM_APP
43 TCHAR appsversion
[] = TEXT("Rockbox firmware patcher V" APPSVERSION
);
50 /* precalculated checksums for H110/H115 */
51 static struct sumpairs h100pairs
[] = {
55 /* precalculated checksums for H120/H140 */
56 static struct sumpairs h120pairs
[] = {
60 /* precalculated checksums for H320/H340 */
61 static struct sumpairs h300pairs
[] = {
67 HWND controls
[CTL_NUM
];
69 /* begin mkboot.c excerpt */
71 unsigned char image
[0x400000 + 0x220 + 0x400000/0x200];
73 int mkboot(TCHAR
*infile
, TCHAR
*outfile
, unsigned char *bldata
, int bllen
,
79 int actual_length
, total_length
, binary_length
, num_chksums
;
81 memset(image
, 0xff, sizeof(image
));
83 /* First, read the iriver original firmware into the image */
84 f
= _tfopen(infile
, TEXT("rb"));
90 i
= fread(image
, 1, 16, f
);
96 /* This is the length of the binary image without the scrambling
97 overhead (but including the ESTFBINR header) */
98 binary_length
= image
[4] + (image
[5] << 8) +
99 (image
[6] << 16) + (image
[7] << 24);
101 /* Read the rest of the binary data, but not the checksum block */
102 len
= binary_length
+0x200-16;
103 i
= fread(image
+16, 1, len
, f
);
111 memcpy(image
+ 0x220 + origin
, bldata
, bllen
);
113 f
= _tfopen(outfile
, TEXT("wb"));
119 /* Patch the reset vector to start the boot loader */
120 image
[0x220 + 4] = image
[origin
+ 0x220 + 4];
121 image
[0x220 + 5] = image
[origin
+ 0x220 + 5];
122 image
[0x220 + 6] = image
[origin
+ 0x220 + 6];
123 image
[0x220 + 7] = image
[origin
+ 0x220 + 7];
125 /* This is the actual length of the binary, excluding all headers */
126 actual_length
= origin
+ bllen
;
128 /* Patch the ESTFBINR header */
129 image
[0x20c] = (actual_length
>> 24) & 0xff;
130 image
[0x20d] = (actual_length
>> 16) & 0xff;
131 image
[0x20e] = (actual_length
>> 8) & 0xff;
132 image
[0x20f] = actual_length
& 0xff;
134 image
[0x21c] = (actual_length
>> 24) & 0xff;
135 image
[0x21d] = (actual_length
>> 16) & 0xff;
136 image
[0x21e] = (actual_length
>> 8) & 0xff;
137 image
[0x21f] = actual_length
& 0xff;
139 /* This is the length of the binary, including the ESTFBINR header and
140 rounded up to the nearest 0x200 boundary */
141 binary_length
= (actual_length
+ 0x20 + 0x1ff) & 0xfffffe00;
143 /* The number of checksums, i.e number of 0x200 byte blocks */
144 num_chksums
= binary_length
/ 0x200;
146 /* The total file length, including all headers and checksums */
147 total_length
= binary_length
+ num_chksums
+ 0x200;
149 /* Patch the scrambler header with the new length info */
150 image
[0] = total_length
& 0xff;
151 image
[1] = (total_length
>> 8) & 0xff;
152 image
[2] = (total_length
>> 16) & 0xff;
153 image
[3] = (total_length
>> 24) & 0xff;
155 image
[4] = binary_length
& 0xff;
156 image
[5] = (binary_length
>> 8) & 0xff;
157 image
[6] = (binary_length
>> 16) & 0xff;
158 image
[7] = (binary_length
>> 24) & 0xff;
160 image
[8] = num_chksums
& 0xff;
161 image
[9] = (num_chksums
>> 8) & 0xff;
162 image
[10] = (num_chksums
>> 16) & 0xff;
163 image
[11] = (num_chksums
>> 24) & 0xff;
165 i
= fwrite(image
, 1, total_length
, f
);
166 if(i
< total_length
) {
176 /* end mkboot.c excerpt */
178 int intable(char *md5
, struct sumpairs
*table
, int len
)
181 for (i
= 0; i
< len
; i
++) {
182 if (strncmp(md5
, table
[i
].unpatched
, 32) == 0) {
189 int FileMD5(TCHAR
*name
, char *md5
)
193 unsigned char md5sum
[16];
194 unsigned char block
[32768];
197 file
= _tfopen(name
, TEXT("rb"));
200 TEXT("Could not open patched firmware for checksum check"),
201 TEXT("Error"), MB_ICONERROR
);
205 while ((read
= fread(block
, 1, sizeof(block
), file
)) > 0) {
206 md5_update(&ctx
, block
, read
);
209 md5_finish(&ctx
, md5sum
);
210 for (i
= 0; i
< 16; ++i
)
211 sprintf(md5
+ 2*i
, "%02x", md5sum
[i
]);
215 int PatchFirmware(int series
, int table_entry
)
218 TCHAR name1
[MAX_PATH
], name2
[MAX_PATH
], name3
[MAX_PATH
];
221 unsigned char *bootloader
;
222 unsigned char md5sum_str
[256];
225 struct sumpairs
*sums
;
228 /* get pointer to the correct bootloader.bin */
231 res
= FindResource(NULL
, MAKEINTRESOURCE(IDI_BOOTLOADERH100
), TEXT("BIN"));
232 sums
= &h100pairs
[0];
236 res
= FindResource(NULL
, MAKEINTRESOURCE(IDI_BOOTLOADERH120
), TEXT("BIN"));
237 sums
= &h120pairs
[0];
241 res
= FindResource(NULL
, MAKEINTRESOURCE(IDI_BOOTLOADERH300
), TEXT("BIN"));
242 sums
= &h300pairs
[0];
246 resload
= LoadResource(NULL
, res
);
247 bootloader
= (unsigned char *)LockResource(resload
);
248 blsize
= SizeofResource(NULL
, res
);
250 /* get filename from edit box */
251 GetWindowText(controls
[EDIT_FILENAME
], fn
, MAX_PATH
);
252 /* store temp files in temp directory */
253 GetTempPath(MAX_PATH
, name1
);
254 GetTempPath(MAX_PATH
, name2
);
255 GetTempPath(MAX_PATH
, name3
);
256 _tcscat(name1
, TEXT("firmware.bin")); /* descrambled file */
257 _tcscat(name2
, TEXT("new.bin")); /* patched file */
258 _tcscat(name3
, TEXT("new.hex")); /* patched and scrambled file */
259 if (iriver_decode(fn
, name1
, FALSE
, STRIP_NONE
) == -1) {
260 MessageBox(NULL
, TEXT("Error in descramble"),
261 TEXT("Error"), MB_ICONERROR
);
264 if (!mkboot(name1
, name2
, bootloader
, blsize
, origin
)) {
265 MessageBox(NULL
, TEXT("Error in patching"),
266 TEXT("Error"), MB_ICONERROR
);
269 if (iriver_encode(name2
, name3
, FALSE
) == -1) {
270 MessageBox(NULL
, TEXT("Error in scramble"),
271 TEXT("Error"), MB_ICONERROR
);
275 if (!FileMD5(name3
, md5sum_str
)) {
276 MessageBox(NULL
, TEXT("Error in checksumming"),
277 TEXT("Error"), MB_ICONERROR
);
280 if (strncmp(sums
[table_entry
].patched
, md5sum_str
, 32) == 0) {
281 /* delete temp files */
284 /* all is fine, rename the patched file to original name of the firmware */
285 if (DeleteFile(fn
) && MoveFile(name3
, fn
)) {
289 DeleteFile(name3
); /* Deleting a perfectly good firmware here really */
291 TEXT("Couldn't modify existing file.\n")
292 TEXT("Check if file is write protected, then try again."),
293 TEXT("Error"), MB_ICONERROR
);
298 TEXT("Checksum doesn't match known good patched firmware.\n")
299 TEXT("Download another firmware image, then try again."),
300 TEXT("Error"), MB_ICONERROR
);
302 /* delete all temp files, don't care if some aren't created yet */
309 int FileDialog(TCHAR
*fn
)
312 TCHAR filename
[MAX_PATH
];
314 ZeroMemory(&ofn
, sizeof(ofn
));
315 ofn
.lStructSize
= sizeof(ofn
);
316 ofn
.lpstrFile
= filename
;
317 ofn
.lpstrFile
[0] = '\0'; // no default filename
318 ofn
.nMaxFile
= sizeof(filename
);
319 ofn
.lpstrFilter
= TEXT("Firmware\0*.HEX\0");
320 ofn
.nFilterIndex
= 1;
321 ofn
.lpstrFileTitle
= NULL
;
322 ofn
.nMaxFileTitle
= 0;
323 ofn
.lpstrInitialDir
= NULL
;
324 ofn
.Flags
= OFN_PATHMUSTEXIST
| OFN_FILEMUSTEXIST
;
326 if (GetOpenFileName(&ofn
) == TRUE
) {
327 _tcscpy(fn
, filename
);
333 LRESULT CALLBACK
WndProc(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
335 int i
, series
, table_entry
;
336 unsigned char md5sum_str
[256];
341 controls
[LABEL_FILENAME
] =
342 CreateWindowEx(0, TEXT("STATIC"), TEXT("Firmware file name:"),
343 WS_CHILD
| WS_VISIBLE
, 10, 14,
344 100, 32, hwnd
, 0, 0, 0);
345 /* text field for inputing file name */
346 controls
[EDIT_FILENAME
] =
347 CreateWindowEx(WS_EX_CLIENTEDGE
, TEXT("EDIT"), TEXT(""),
348 WS_CHILD
| WS_TABSTOP
| WS_VISIBLE
| ES_AUTOHSCROLL
,
349 10, 35, 180, 20, hwnd
, 0, 0, 0);
351 controls
[BUTTON_BROWSE
] =
352 CreateWindowEx(0, TEXT("BUTTON"), TEXT("Browse"),
353 WS_CHILD
| WS_TABSTOP
| WS_VISIBLE
, 200, 32, 70, 25,
356 controls
[BUTTON_PATCH
] =
357 CreateWindowEx(0, TEXT("BUTTON"), TEXT("Patch"),
358 WS_CHILD
| WS_TABSTOP
| WS_VISIBLE
, 90, 70, 90, 25,
360 /* set default font on all controls, will be ugly if we don't do this */
361 deffont
= GetStockObject(DEFAULT_GUI_FONT
);
362 for (i
= 0; i
< CTL_NUM
; ++i
)
363 SendMessage(controls
[i
], WM_SETFONT
, (WPARAM
)deffont
,
364 MAKELPARAM(FALSE
, 0));
375 /* user pressed browse button */
376 if (((HWND
)lParam
== controls
[BUTTON_BROWSE
])) {
379 SetWindowText(controls
[EDIT_FILENAME
], buf
);
381 /* user pressed patch button */
382 if (((HWND
)lParam
== controls
[BUTTON_PATCH
])) {
383 GetWindowText(controls
[EDIT_FILENAME
], fn
, MAX_PATH
);
384 if (!FileMD5(fn
, md5sum_str
)) {
385 MessageBox(NULL
, TEXT("Couldn't open firmware"), TEXT("Fail"), MB_OK
);
388 /* Check firmware against md5sums in h120sums and h100sums */
390 table_entry
= intable(md5sum_str
, &h120pairs
[0],
391 sizeof(h120pairs
)/sizeof(struct sumpairs
));
392 if (table_entry
>= 0) {
396 table_entry
= intable(md5sum_str
, &h100pairs
[0],
397 sizeof(h100pairs
)/sizeof(struct sumpairs
));
398 if (table_entry
>= 0) {
403 intable(md5sum_str
, &h300pairs
[0],
404 sizeof(h300pairs
)/sizeof(struct sumpairs
));
405 if (table_entry
>= 0)
410 MessageBox(NULL
, TEXT("Unrecognised firmware"), TEXT("Fail"), MB_OK
);
412 else if (PatchFirmware(series
, table_entry
))
413 MessageBox(NULL
, TEXT("Firmware patched successfully"),
414 TEXT("Success"), MB_OK
);
418 case WM_COMMANDDRIVEN
:
419 /* command line driven patch button */
420 SetWindowText(controls
[EDIT_FILENAME
], (LPCTSTR
)wParam
);
421 SendMessage(hwnd
, WM_COMMAND
, 0, (LPARAM
)(controls
[BUTTON_PATCH
]) );
424 return DefWindowProc(hwnd
, msg
, wParam
, lParam
);
429 void getargs(LPTSTR p
, int * argc
, LPCTSTR
* argv
, int MAXARGS
)
431 int quote
=FALSE
,whitespace
=FALSE
;
436 if((*p
==TEXT(' ') || *p
==TEXT('\0')) && !quote
)
445 break; // end of cmd line
471 int WINAPI
WinMain(HINSTANCE instance
, HINSTANCE prev_instance
,
472 LPSTR command_line
, int command_show
)
478 LPCTSTR argv
[MAXARGC
] = { NULL
};
479 LPTSTR cmdline
= GetCommandLine();
481 getargs(cmdline
, &argc
, argv
, MAXARGC
);
483 command_show
= SW_HIDE
;
485 rbicon
= LoadIcon(instance
, MAKEINTRESOURCE(IDI_RBICON
));
486 ZeroMemory(&wc
, sizeof(wc
));
487 wc
.cbSize
= sizeof(wc
);
488 wc
.lpfnWndProc
= WndProc
;
489 wc
.hInstance
= instance
;
491 wc
.hCursor
= LoadCursor(0, IDC_ARROW
);
492 wc
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
493 wc
.lpszClassName
= TEXT("patcher");
494 RegisterClassEx(&wc
);
496 window
= CreateWindowEx(0, TEXT("patcher"), appsversion
,
497 WS_OVERLAPPED
| WS_CAPTION
| WS_SYSMENU
|
498 WS_MINIMIZEBOX
, CW_USEDEFAULT
, CW_USEDEFAULT
,
499 WINDOW_WIDTH
, WINDOW_HEIGHT
, 0, 0, instance
, NULL
);
500 if (!window
) return 0;
502 ShowWindow(window
, command_show
);
505 SendMessage(window
, WM_COMMANDDRIVEN
, (WPARAM
)(argv
[1]), 0);
506 SendMessage(window
, WM_CLOSE
, 0, 0);
509 while (GetMessage(&msg
, 0, 0, 0) > 0) {
510 if (!IsDialogMessage(window
, &msg
)) {
511 TranslateMessage(&msg
);
512 DispatchMessage(&msg
);