Extend HttpGet class to allow downloading a file to a buffer instead of a file.
[Rockbox.git] / tools / fwpatcher / main.c
blobd6b798637ca7221a36681da0c06ec15a86d1bf29
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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.
21 #include <stdio.h>
22 #include <string.h>
23 #include <tchar.h>
24 #include <windows.h>
25 #include "iriver.h"
26 #include "md5.h"
27 #include "resource.h"
29 #define WINDOW_WIDTH 280
30 #define WINDOW_HEIGHT 130
32 #define IDM_RESTORE 1000
33 #define IDM_EXIT 1010
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
41 #define CTL_NUM 4
43 TCHAR appsversion[] = TEXT("Rockbox firmware patcher V" APPSVERSION);
45 struct sumpairs {
46 char *unpatched;
47 char *patched;
50 /* precalculated checksums for H110/H115 */
51 static struct sumpairs h100pairs[] = {
52 #include "h100sums.h"
55 /* precalculated checksums for H120/H140 */
56 static struct sumpairs h120pairs[] = {
57 #include "h120sums.h"
60 /* precalculated checksums for H320/H340 */
61 static struct sumpairs h300pairs[] = {
62 #include "h300sums.h"
65 HICON rbicon;
66 HFONT deffont;
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,
74 int origin)
76 FILE *f;
77 int i;
78 int len;
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"));
85 if(!f) {
86 perror(infile);
87 return 0;
90 i = fread(image, 1, 16, f);
91 if(i < 16) {
92 perror(infile);
93 return 0;
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);
104 if(i < len) {
105 perror(infile);
106 return 0;
109 fclose(f);
111 memcpy(image + 0x220 + origin, bldata, bllen);
113 f = _tfopen(outfile, TEXT("wb"));
114 if(!f) {
115 perror(outfile);
116 return 0;
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) {
167 perror(outfile);
168 return 0;
171 fclose(f);
173 return 1;
176 /* end mkboot.c excerpt */
178 int intable(char *md5, struct sumpairs *table, int len)
180 int i;
181 for (i = 0; i < len; i++) {
182 if (strncmp(md5, table[i].unpatched, 32) == 0) {
183 return i;
186 return -1;
189 int FileMD5(TCHAR *name, char *md5)
191 int i, read;
192 md5_context ctx;
193 unsigned char md5sum[16];
194 unsigned char block[32768];
195 FILE *file;
197 file = _tfopen(name, TEXT("rb"));
198 if (!file) {
199 MessageBox(NULL,
200 TEXT("Could not open patched firmware for checksum check"),
201 TEXT("Error"), MB_ICONERROR);
202 return 0;
204 md5_starts(&ctx);
205 while ((read = fread(block, 1, sizeof(block), file)) > 0) {
206 md5_update(&ctx, block, read);
208 fclose(file);
209 md5_finish(&ctx, md5sum);
210 for (i = 0; i < 16; ++i)
211 sprintf(md5 + 2*i, "%02x", md5sum[i]);
212 return 1;
215 int PatchFirmware(int series, int table_entry)
217 TCHAR fn[MAX_PATH];
218 TCHAR name1[MAX_PATH], name2[MAX_PATH], name3[MAX_PATH];
219 HRSRC res;
220 HGLOBAL resload;
221 unsigned char *bootloader;
222 unsigned char md5sum_str[256];
223 DWORD blsize;
224 int i;
225 struct sumpairs *sums;
226 int origin;
228 /* get pointer to the correct bootloader.bin */
229 switch(series) {
230 case 100:
231 res = FindResource(NULL, MAKEINTRESOURCE(IDI_BOOTLOADERH100), TEXT("BIN"));
232 sums = &h100pairs[0];
233 origin = 0x1f0000;
234 break;
235 case 120:
236 res = FindResource(NULL, MAKEINTRESOURCE(IDI_BOOTLOADERH120), TEXT("BIN"));
237 sums = &h120pairs[0];
238 origin = 0x1f0000;
239 break;
240 case 300:
241 res = FindResource(NULL, MAKEINTRESOURCE(IDI_BOOTLOADERH300), TEXT("BIN"));
242 sums = &h300pairs[0];
243 origin = 0x3f0000;
244 break;
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);
262 goto error;
264 if (!mkboot(name1, name2, bootloader, blsize, origin)) {
265 MessageBox(NULL, TEXT("Error in patching"),
266 TEXT("Error"), MB_ICONERROR);
267 goto error;
269 if (iriver_encode(name2, name3, FALSE) == -1) {
270 MessageBox(NULL, TEXT("Error in scramble"),
271 TEXT("Error"), MB_ICONERROR);
272 goto error;
274 /* now md5sum it */
275 if (!FileMD5(name3, md5sum_str)) {
276 MessageBox(NULL, TEXT("Error in checksumming"),
277 TEXT("Error"), MB_ICONERROR);
278 goto error;
280 if (strncmp(sums[table_entry].patched, md5sum_str, 32) == 0) {
281 /* delete temp files */
282 DeleteFile(name1);
283 DeleteFile(name2);
284 /* all is fine, rename the patched file to original name of the firmware */
285 if (DeleteFile(fn) && MoveFile(name3, fn)) {
286 return 1;
288 else {
289 DeleteFile(name3); /* Deleting a perfectly good firmware here really */
290 MessageBox(NULL,
291 TEXT("Couldn't modify existing file.\n")
292 TEXT("Check if file is write protected, then try again."),
293 TEXT("Error"), MB_ICONERROR);
294 return 0;
297 MessageBox(NULL,
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);
301 error:
302 /* delete all temp files, don't care if some aren't created yet */
303 DeleteFile(name1);
304 DeleteFile(name2);
305 DeleteFile(name3);
306 return 0;
309 int FileDialog(TCHAR *fn)
311 OPENFILENAME ofn;
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);
328 return 1;
330 return 0;
333 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
335 int i, series, table_entry;
336 unsigned char md5sum_str[256];
337 switch (msg) {
338 TCHAR fn[MAX_PATH];
339 case WM_CREATE:
340 /* text label */
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);
350 /* browse button */
351 controls[BUTTON_BROWSE] =
352 CreateWindowEx(0, TEXT("BUTTON"), TEXT("Browse"),
353 WS_CHILD | WS_TABSTOP | WS_VISIBLE, 200, 32, 70, 25,
354 hwnd, 0, 0, 0);
355 /* patch button */
356 controls[BUTTON_PATCH] =
357 CreateWindowEx(0, TEXT("BUTTON"), TEXT("Patch"),
358 WS_CHILD | WS_TABSTOP | WS_VISIBLE, 90, 70, 90, 25,
359 hwnd, 0, 0, 0);
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));
365 break;
366 case WM_CLOSE:
367 DestroyWindow(hwnd);
368 break;
369 case WM_DESTROY:
370 PostQuitMessage(0);
371 break;
372 case WM_SIZE:
373 break;
374 case WM_COMMAND:
375 /* user pressed browse button */
376 if (((HWND)lParam == controls[BUTTON_BROWSE])) {
377 TCHAR buf[MAX_PATH];
378 if (FileDialog(buf))
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);
387 else {
388 /* Check firmware against md5sums in h120sums and h100sums */
389 series = 0;
390 table_entry = intable(md5sum_str, &h120pairs[0],
391 sizeof(h120pairs)/sizeof(struct sumpairs));
392 if (table_entry >= 0) {
393 series = 120;
395 else {
396 table_entry = intable(md5sum_str, &h100pairs[0],
397 sizeof(h100pairs)/sizeof(struct sumpairs));
398 if (table_entry >= 0) {
399 series = 100;
401 else {
402 table_entry =
403 intable(md5sum_str, &h300pairs[0],
404 sizeof(h300pairs)/sizeof(struct sumpairs));
405 if (table_entry >= 0)
406 series = 300;
409 if (series == 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);
417 break;
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]) );
422 break;
423 default:
424 return DefWindowProc(hwnd, msg, wParam, lParam);
426 return 0;
429 void getargs(LPTSTR p, int * argc, LPCTSTR * argv, int MAXARGS)
431 int quote=FALSE,whitespace=FALSE;
432 LPCTSTR tok=p;
433 *argc=0;
434 while(*argc<MAXARGS)
436 if((*p==TEXT(' ') || *p==TEXT('\0')) && !quote)
438 if(!whitespace)
440 whitespace=TRUE;
441 *argv++ = tok;
442 (*argc)++;
444 if(*p==TEXT('\0'))
445 break; // end of cmd line
446 *p=TEXT('\0');
447 p++;
449 else
451 if(whitespace)
452 tok=p;
453 whitespace=FALSE;
454 if(*p==TEXT('\"'))
456 *p=TEXT(' ');
457 if(!quote)
459 p++;
460 tok=p;
462 quote = !quote;
464 else p++;
469 #define MAXARGC 4
471 int WINAPI WinMain(HINSTANCE instance, HINSTANCE prev_instance,
472 LPSTR command_line, int command_show)
474 HWND window;
475 WNDCLASSEX wc;
476 MSG msg;
477 int argc;
478 LPCTSTR argv[MAXARGC] = { NULL };
479 LPTSTR cmdline = GetCommandLine();
481 getargs(cmdline, &argc, argv, MAXARGC);
482 if (argc > 1)
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;
490 wc.hIcon = rbicon;
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);
504 if (argc > 1) {
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);
515 return 0;