diff options
Diffstat (limited to 'tools/fwpatcher/main.c')
-rw-r--r-- | tools/fwpatcher/main.c | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/tools/fwpatcher/main.c b/tools/fwpatcher/main.c new file mode 100644 index 0000000000..c8fa11ea95 --- /dev/null +++ b/tools/fwpatcher/main.c | |||
@@ -0,0 +1,367 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 by Thom Johansen | ||
11 | * | ||
12 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
13 | * KIND, either express or implied. | ||
14 | * | ||
15 | ****************************************************************************/ | ||
16 | |||
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. | ||
19 | */ | ||
20 | |||
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" | ||
28 | |||
29 | #define WINDOW_WIDTH 280 | ||
30 | #define WINDOW_HEIGHT 130 | ||
31 | |||
32 | #define IDM_RESTORE 1000 | ||
33 | #define IDM_EXIT 1010 | ||
34 | |||
35 | #define LABEL_FILENAME 0 | ||
36 | #define EDIT_FILENAME 1 | ||
37 | #define BUTTON_BROWSE 2 | ||
38 | #define BUTTON_PATCH 3 | ||
39 | |||
40 | #define CTL_NUM 4 | ||
41 | |||
42 | /* include precalculated checksums */ | ||
43 | static char *checksums[] = { | ||
44 | #include "checksums.h" | ||
45 | }; | ||
46 | |||
47 | HICON rbicon; | ||
48 | HFONT deffont; | ||
49 | HWND controls[CTL_NUM]; | ||
50 | |||
51 | /* begin mkboot.c excerpt */ | ||
52 | |||
53 | char image[0x200000 + 0x220 + 0x200000/0x200]; | ||
54 | |||
55 | int mkboot(TCHAR *infile, TCHAR *outfile, unsigned char *bldata, int bllen) | ||
56 | { | ||
57 | FILE *f; | ||
58 | int i; | ||
59 | int len; | ||
60 | int actual_length, total_length, binary_length, num_chksums; | ||
61 | |||
62 | memset(image, 0xff, sizeof(image)); | ||
63 | |||
64 | /* First, read the iriver original firmware into the image */ | ||
65 | f = _tfopen(infile, TEXT("rb")); | ||
66 | if(!f) { | ||
67 | perror(infile); | ||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | i = fread(image, 1, 16, f); | ||
72 | if(i < 16) { | ||
73 | perror(infile); | ||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | /* This is the length of the binary image without the scrambling | ||
78 | overhead (but including the ESTFBINR header) */ | ||
79 | binary_length = image[4] + (image[5] << 8) + | ||
80 | (image[6] << 16) + (image[7] << 24); | ||
81 | |||
82 | /* Read the rest of the binary data, but not the checksum block */ | ||
83 | len = binary_length+0x200-16; | ||
84 | i = fread(image+16, 1, len, f); | ||
85 | if(i < len) { | ||
86 | perror(infile); | ||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | fclose(f); | ||
91 | |||
92 | memcpy(image + 0x220 + 0x1f0000, bldata, bllen); | ||
93 | |||
94 | f = _tfopen(outfile, TEXT("wb")); | ||
95 | if(!f) { | ||
96 | perror(outfile); | ||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | /* Patch the reset vector to start the boot loader */ | ||
101 | image[0x220 + 4] = image[0x1f0000 + 0x220 + 4]; | ||
102 | image[0x220 + 5] = image[0x1f0000 + 0x220 + 5]; | ||
103 | image[0x220 + 6] = image[0x1f0000 + 0x220 + 6]; | ||
104 | image[0x220 + 7] = image[0x1f0000 + 0x220 + 7]; | ||
105 | |||
106 | /* This is the actual length of the binary, excluding all headers */ | ||
107 | actual_length = 0x1f0000 + bllen; | ||
108 | |||
109 | /* Patch the ESTFBINR header */ | ||
110 | image[0x20c] = (actual_length >> 24) & 0xff; | ||
111 | image[0x20d] = (actual_length >> 16) & 0xff; | ||
112 | image[0x20e] = (actual_length >> 8) & 0xff; | ||
113 | image[0x20f] = actual_length & 0xff; | ||
114 | |||
115 | image[0x21c] = (actual_length >> 24) & 0xff; | ||
116 | image[0x21d] = (actual_length >> 16) & 0xff; | ||
117 | image[0x21e] = (actual_length >> 8) & 0xff; | ||
118 | image[0x21f] = actual_length & 0xff; | ||
119 | |||
120 | /* This is the length of the binary, including the ESTFBINR header and | ||
121 | rounded up to the nearest 0x200 boundary */ | ||
122 | binary_length = (actual_length + 0x20 + 0x1ff) & 0xfffffe00; | ||
123 | |||
124 | /* The number of checksums, i.e number of 0x200 byte blocks */ | ||
125 | num_chksums = binary_length / 0x200; | ||
126 | |||
127 | /* The total file length, including all headers and checksums */ | ||
128 | total_length = binary_length + num_chksums + 0x200; | ||
129 | |||
130 | /* Patch the scrambler header with the new length info */ | ||
131 | image[0] = total_length & 0xff; | ||
132 | image[1] = (total_length >> 8) & 0xff; | ||
133 | image[2] = (total_length >> 16) & 0xff; | ||
134 | image[3] = (total_length >> 24) & 0xff; | ||
135 | |||
136 | image[4] = binary_length & 0xff; | ||
137 | image[5] = (binary_length >> 8) & 0xff; | ||
138 | image[6] = (binary_length >> 16) & 0xff; | ||
139 | image[7] = (binary_length >> 24) & 0xff; | ||
140 | |||
141 | image[8] = num_chksums & 0xff; | ||
142 | image[9] = (num_chksums >> 8) & 0xff; | ||
143 | image[10] = (num_chksums >> 16) & 0xff; | ||
144 | image[11] = (num_chksums >> 24) & 0xff; | ||
145 | |||
146 | i = fwrite(image, 1, total_length, f); | ||
147 | if(i < total_length) { | ||
148 | perror(outfile); | ||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | fclose(f); | ||
153 | |||
154 | return 1; | ||
155 | } | ||
156 | |||
157 | /* end mkboot.c excerpt */ | ||
158 | |||
159 | int FileMD5(TCHAR *name, char *md5) | ||
160 | { | ||
161 | int i, read; | ||
162 | md5_context ctx; | ||
163 | unsigned char md5sum[16]; | ||
164 | unsigned char block[32768]; | ||
165 | FILE *file; | ||
166 | |||
167 | file = _tfopen(name, TEXT("rb")); | ||
168 | if (!file) { | ||
169 | MessageBox(NULL, | ||
170 | TEXT("Could not open patched firmware for checksum check"), | ||
171 | TEXT("Error"), MB_ICONERROR); | ||
172 | return 0; | ||
173 | } | ||
174 | md5_starts(&ctx); | ||
175 | while ((read = fread(block, 1, sizeof(block), file)) > 0) { | ||
176 | md5_update(&ctx, block, read); | ||
177 | } | ||
178 | fclose(file); | ||
179 | md5_finish(&ctx, md5sum); | ||
180 | for (i = 0; i < 16; ++i) | ||
181 | sprintf(md5 + 2*i, "%02x", md5sum[i]); | ||
182 | return 1; | ||
183 | } | ||
184 | |||
185 | int PatchFirmware() | ||
186 | { | ||
187 | TCHAR fn[MAX_PATH]; | ||
188 | TCHAR name1[MAX_PATH], name2[MAX_PATH], name3[MAX_PATH]; | ||
189 | HRSRC res; | ||
190 | HGLOBAL resload; | ||
191 | unsigned char *bootloader; | ||
192 | unsigned char md5sum_str[256]; | ||
193 | DWORD blsize; | ||
194 | int i; | ||
195 | |||
196 | /* get pointer to bootloader.bin */ | ||
197 | res = FindResource(NULL, MAKEINTRESOURCE(IDI_BOOTLOADER), TEXT("BIN")); | ||
198 | resload = LoadResource(NULL, res); | ||
199 | bootloader = (unsigned char *)LockResource(resload); | ||
200 | blsize = SizeofResource(NULL, res); | ||
201 | |||
202 | /* get filename from edit box */ | ||
203 | GetWindowText(controls[EDIT_FILENAME], fn, MAX_PATH); | ||
204 | /* store temp files in temp directory */ | ||
205 | GetTempPath(MAX_PATH, name1); | ||
206 | GetTempPath(MAX_PATH, name2); | ||
207 | GetTempPath(MAX_PATH, name3); | ||
208 | _tcscat(name1, TEXT("firmware.bin")); /* descrambled file */ | ||
209 | _tcscat(name2, TEXT("new.bin")); /* patched file */ | ||
210 | _tcscat(name3, TEXT("new.hex")); /* patched and scrambled file */ | ||
211 | if (iriver_decode(fn, name1, FALSE, STRIP_NONE) == -1) { | ||
212 | MessageBox(NULL, TEXT("Error in descramble"), | ||
213 | TEXT("Error"), MB_ICONERROR); | ||
214 | goto error; | ||
215 | } | ||
216 | if (!mkboot(name1, name2, bootloader, blsize)) { | ||
217 | MessageBox(NULL, TEXT("Error in patching"), | ||
218 | TEXT("Error"), MB_ICONERROR); | ||
219 | goto error; | ||
220 | } | ||
221 | if (iriver_encode(name2, name3, FALSE) == -1) { | ||
222 | MessageBox(NULL, TEXT("Error in scramble"), | ||
223 | TEXT("Error"), MB_ICONERROR); | ||
224 | goto error; | ||
225 | } | ||
226 | /* now md5sum it */ | ||
227 | if (!FileMD5(name3, md5sum_str)) goto error; | ||
228 | for (i = 0; i < sizeof(checksums)/sizeof(char *); ++i) { | ||
229 | if (strncmp(checksums[i], md5sum_str, 32) != 0) { | ||
230 | MessageBox(NULL, | ||
231 | TEXT("Checksum doesn't match known good patched firmware.\n") | ||
232 | TEXT("Download another firmware image, then try again."), | ||
233 | TEXT("Error"), MB_ICONERROR); | ||
234 | goto error; | ||
235 | } | ||
236 | } | ||
237 | /* all is fine, rename the patched file to original name of the firmware */ | ||
238 | MoveFileEx(name3, fn, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING); | ||
239 | /* delete temp files */ | ||
240 | DeleteFile(name1); | ||
241 | DeleteFile(name2); | ||
242 | return 1; | ||
243 | error: | ||
244 | /* delete all temp files, don't care if some aren't created yet */ | ||
245 | DeleteFile(name1); | ||
246 | DeleteFile(name2); | ||
247 | DeleteFile(name3); | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | int FileDialog(TCHAR *fn) | ||
252 | { | ||
253 | OPENFILENAME ofn; | ||
254 | TCHAR filename[MAX_PATH]; | ||
255 | |||
256 | ZeroMemory(&ofn, sizeof(ofn)); | ||
257 | ofn.lStructSize = sizeof(ofn); | ||
258 | ofn.lpstrFile = filename; | ||
259 | ofn.lpstrFile[0] = '\0'; // no default filename | ||
260 | ofn.nMaxFile = sizeof(filename); | ||
261 | ofn.lpstrFilter = TEXT("Firmware\0*.HEX\0"); | ||
262 | ofn.nFilterIndex = 1; | ||
263 | ofn.lpstrFileTitle = NULL; | ||
264 | ofn.nMaxFileTitle = 0; | ||
265 | ofn.lpstrInitialDir = NULL; | ||
266 | ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; | ||
267 | |||
268 | if (GetOpenFileName(&ofn) == TRUE) { | ||
269 | _tcscpy(fn, filename); | ||
270 | return 1; | ||
271 | } | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) | ||
276 | { | ||
277 | int i; | ||
278 | switch (msg) { | ||
279 | case WM_CREATE: | ||
280 | /* text label */ | ||
281 | controls[LABEL_FILENAME] = | ||
282 | CreateWindowEx(0, TEXT("STATIC"), TEXT("Firmware file name:"), | ||
283 | WS_CHILD | WS_VISIBLE, 10, 14, | ||
284 | 100, 32, hwnd, 0, 0, 0); | ||
285 | /* text field for inputing file name */ | ||
286 | controls[EDIT_FILENAME] = | ||
287 | CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("EDIT"), TEXT(""), | ||
288 | WS_CHILD | WS_TABSTOP | WS_VISIBLE | ES_AUTOHSCROLL, | ||
289 | 10, 35, 180, 20, hwnd, 0, 0, 0); | ||
290 | /* browse button */ | ||
291 | controls[BUTTON_BROWSE] = | ||
292 | CreateWindowEx(0, TEXT("BUTTON"), TEXT("Browse"), | ||
293 | WS_CHILD | WS_TABSTOP | WS_VISIBLE, 200, 32, 70, 25, | ||
294 | hwnd, 0, 0, 0); | ||
295 | /* patch button */ | ||
296 | controls[BUTTON_PATCH] = | ||
297 | CreateWindowEx(0, TEXT("BUTTON"), TEXT("Patch"), | ||
298 | WS_CHILD | WS_TABSTOP | WS_VISIBLE, 90, 70, 90, 25, | ||
299 | hwnd, 0, 0, 0); | ||
300 | /* set default font on all controls, will be ugly if we don't do this */ | ||
301 | deffont = GetStockObject(DEFAULT_GUI_FONT); | ||
302 | for (i = 0; i < CTL_NUM; ++i) | ||
303 | SendMessage(controls[i], WM_SETFONT, (WPARAM)deffont, | ||
304 | MAKELPARAM(FALSE, 0)); | ||
305 | break; | ||
306 | case WM_CLOSE: | ||
307 | DestroyWindow(hwnd); | ||
308 | break; | ||
309 | case WM_DESTROY: | ||
310 | PostQuitMessage(0); | ||
311 | break; | ||
312 | case WM_SIZE: | ||
313 | break; | ||
314 | case WM_COMMAND: | ||
315 | /* user pressed browse button */ | ||
316 | if (((HWND)lParam == controls[BUTTON_BROWSE])) { | ||
317 | TCHAR buf[MAX_PATH]; | ||
318 | if (FileDialog(buf)) | ||
319 | SetWindowText(controls[EDIT_FILENAME], buf); | ||
320 | } | ||
321 | /* user pressed patch button */ | ||
322 | if (((HWND)lParam == controls[BUTTON_PATCH])) { | ||
323 | if (PatchFirmware()) | ||
324 | MessageBox(NULL, TEXT("Firmware patched successfully"), | ||
325 | TEXT("Success"), MB_OK); | ||
326 | } | ||
327 | break; | ||
328 | default: | ||
329 | return DefWindowProc(hwnd, msg, wParam, lParam); | ||
330 | } | ||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | int WINAPI WinMain(HINSTANCE instance, HINSTANCE prev_instance, | ||
335 | LPSTR command_line, int command_show) | ||
336 | { | ||
337 | HWND window; | ||
338 | WNDCLASSEX wc; | ||
339 | MSG msg; | ||
340 | |||
341 | rbicon = LoadIcon(instance, MAKEINTRESOURCE(IDI_RBICON)); | ||
342 | ZeroMemory(&wc, sizeof(wc)); | ||
343 | wc.cbSize = sizeof(wc); | ||
344 | wc.lpfnWndProc = WndProc; | ||
345 | wc.hInstance = instance; | ||
346 | wc.hIcon = rbicon; | ||
347 | wc.hCursor = LoadCursor(0, IDC_ARROW); | ||
348 | wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); | ||
349 | wc.lpszClassName = TEXT("patcher"); | ||
350 | RegisterClassEx(&wc); | ||
351 | |||
352 | window = CreateWindowEx(0, TEXT("patcher"), TEXT("Rockbox firmware patcher"), | ||
353 | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | | ||
354 | WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, | ||
355 | WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0, instance, NULL); | ||
356 | if (!window) return 0; | ||
357 | |||
358 | ShowWindow(window, command_show); | ||
359 | while (GetMessage(&msg, 0, 0, 0) > 0) { | ||
360 | if (!IsDialogMessage(window, &msg)) { | ||
361 | TranslateMessage(&msg); | ||
362 | DispatchMessage(&msg); | ||
363 | } | ||
364 | } | ||
365 | return 0; | ||
366 | } | ||
367 | |||