diff options
Diffstat (limited to 'utils/sansapatcher')
-rw-r--r-- | utils/sansapatcher/Makefile | 52 | ||||
-rw-r--r-- | utils/sansapatcher/README | 36 | ||||
-rw-r--r-- | utils/sansapatcher/main.c | 420 | ||||
-rw-r--r-- | utils/sansapatcher/parttypes.h | 109 | ||||
-rw-r--r-- | utils/sansapatcher/sansaio-posix.c | 157 | ||||
-rw-r--r-- | utils/sansapatcher/sansaio-win32.c | 217 | ||||
-rw-r--r-- | utils/sansapatcher/sansaio.h | 88 | ||||
-rw-r--r-- | utils/sansapatcher/sansapatcher.c | 975 | ||||
-rw-r--r-- | utils/sansapatcher/sansapatcher.h | 67 | ||||
-rw-r--r-- | utils/sansapatcher/sansapatcher.manifest | 13 | ||||
-rw-r--r-- | utils/sansapatcher/sansapatcher.pro | 38 | ||||
-rw-r--r-- | utils/sansapatcher/sansapatcher.rc | 1 |
12 files changed, 2173 insertions, 0 deletions
diff --git a/utils/sansapatcher/Makefile b/utils/sansapatcher/Makefile new file mode 100644 index 0000000000..85c36c6d14 --- /dev/null +++ b/utils/sansapatcher/Makefile | |||
@@ -0,0 +1,52 @@ | |||
1 | # __________ __ ___. | ||
2 | # Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
3 | # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
4 | # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
5 | # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
6 | # \/ \/ \/ \/ \/ | ||
7 | |||
8 | TARGET_DIR ?= $(shell pwd)/ | ||
9 | CFLAGS += -Wall -W -D_LARGEFILE64_SOURCE | ||
10 | |||
11 | OUTPUT = sansapatcher | ||
12 | |||
13 | # inputs | ||
14 | LIBSOURCES := sansapatcher.c sansaio-posix.c sansaio-win32.c | ||
15 | SOURCES := main.c | ||
16 | # additional link dependencies for the standalone executable | ||
17 | EXTRADEPS := | ||
18 | |||
19 | # Releases of sansapatcher are created with "make RELEASE=1". This | ||
20 | # enables BOOTOBJS and uses the VERSION string defined in main.c | ||
21 | ifdef RELEASE | ||
22 | CFLAGS += -DRELEASE | ||
23 | BOOTOBJS=1 | ||
24 | endif | ||
25 | |||
26 | ifdef WITH_BOOTOBJS | ||
27 | BOOTSRC = bootimg_c200.c bootimg_e200.c | ||
28 | SOURCES += $(BOOTSRC) | ||
29 | CFLAGS += -DWITH_BOOTOBJS | ||
30 | endif | ||
31 | |||
32 | include ../libtools.make | ||
33 | |||
34 | # find out if we need to link the manifest resource. | ||
35 | # Since libtools.make sets up BINARY we check it for the file extension .exe. | ||
36 | ifeq ($(findstring exe,$(BINARY)),exe) | ||
37 | $(BINARY): $(OBJDIR)sansapatcher-rc.o | ||
38 | endif | ||
39 | |||
40 | $(OBJDIR)main.o: $(BOOTSRC) | ||
41 | $(OBJDIR)sansapatcher-rc.o: sansapatcher.rc sansapatcher.manifest | ||
42 | @echo WINDRES $(notdir $<) | ||
43 | $(SILENT)$(CROSS)$(WINDRES) -i sansapatcher.rc -o $@ | ||
44 | |||
45 | bootimg_c200.c: firmware.mi4 $(BIN2C) | ||
46 | @echo BIN2C $< | ||
47 | $(SILENT)$(BIN2C) $< $* | ||
48 | |||
49 | bootimg_e200.c: PP5022.mi4 $(BIN2C) | ||
50 | @echo BIN2C $< $* | ||
51 | $(SILENT)$(BIN2C) $< $* | ||
52 | |||
diff --git a/utils/sansapatcher/README b/utils/sansapatcher/README new file mode 100644 index 0000000000..c62f676110 --- /dev/null +++ b/utils/sansapatcher/README | |||
@@ -0,0 +1,36 @@ | |||
1 | sansapatcher | ||
2 | ------------ | ||
3 | |||
4 | To compile sansapatcher, you need both the C200 and E200 Rockbox | ||
5 | bootloaders. The latest bootloaders can always be found here: | ||
6 | |||
7 | http://download.rockbox.org/bootloader/sandisk-sansa/e200/PP5022.mi4 | ||
8 | http://download.rockbox.org/bootloader/sandisk-sansa/c200/firmware.mi4 | ||
9 | |||
10 | Place both these files in the sansapatcher source directory, and type "make". | ||
11 | |||
12 | |||
13 | Building your own bootloaders | ||
14 | ----------------------------- | ||
15 | |||
16 | If you would like to compile the bootloaders yourself, they are the output of | ||
17 | running the "Bootloader" build for the E200 and C200 targets. | ||
18 | |||
19 | NOTE: Unless you know what you are doing, it is recommended that you | ||
20 | use the official pre-built binary bootloaders linked to above. | ||
21 | Bootloaders compiled from current Rockbox SVN are untested and | ||
22 | may contain bugs preventing you from starting the device (or | ||
23 | worse...). | ||
24 | |||
25 | In the Rockbox source directory, do: | ||
26 | |||
27 | mkdir build-e200-bootloader | ||
28 | cd build-e200-bootloader | ||
29 | ../tools/configure | ||
30 | [Select E200, then B for bootloader] | ||
31 | make | ||
32 | |||
33 | This will create PP5022.mi4 which you should copy to the sansapatcher | ||
34 | build directory. | ||
35 | |||
36 | A similar process for the C200 will create firmware.mi4. | ||
diff --git a/utils/sansapatcher/main.c b/utils/sansapatcher/main.c new file mode 100644 index 0000000000..d12e33c22b --- /dev/null +++ b/utils/sansapatcher/main.c | |||
@@ -0,0 +1,420 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006-2007 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include <stdio.h> | ||
23 | #include <unistd.h> | ||
24 | #include <fcntl.h> | ||
25 | #include <string.h> | ||
26 | #include <stdlib.h> | ||
27 | #include <inttypes.h> | ||
28 | #include <sys/types.h> | ||
29 | #include <sys/stat.h> | ||
30 | |||
31 | #include "sansapatcher.h" | ||
32 | #include "sansaio.h" | ||
33 | #include "parttypes.h" | ||
34 | #ifdef WITH_BOOTOBJS | ||
35 | #include "bootimg_c200.h" | ||
36 | #include "bootimg_e200.h" | ||
37 | #endif | ||
38 | |||
39 | #ifndef VERSION | ||
40 | #define VERSION "0.8 with v6.0 bootloaders" | ||
41 | #endif | ||
42 | |||
43 | enum { | ||
44 | NONE, | ||
45 | INSTALL, | ||
46 | INTERACTIVE, | ||
47 | SHOW_INFO, | ||
48 | LIST_IMAGES, | ||
49 | DELETE_BOOTLOADER, | ||
50 | ADD_BOOTLOADER, | ||
51 | READ_FIRMWARE, | ||
52 | WRITE_FIRMWARE, | ||
53 | READ_PARTITION, | ||
54 | WRITE_PARTITION, | ||
55 | UPDATE_OF, | ||
56 | UPDATE_PPBL | ||
57 | }; | ||
58 | |||
59 | static void print_usage(void) | ||
60 | { | ||
61 | fprintf(stderr,"Usage: sansapatcher --scan\n"); | ||
62 | #ifdef __WIN32__ | ||
63 | fprintf(stderr," or sansapatcher [DISKNO] [action]\n"); | ||
64 | #else | ||
65 | fprintf(stderr," or sansapatcher [device] [action]\n"); | ||
66 | #endif | ||
67 | fprintf(stderr,"\n"); | ||
68 | fprintf(stderr,"Where [action] is one of the following options:\n"); | ||
69 | fprintf(stderr," --install\n"); | ||
70 | fprintf(stderr," -l, --list\n"); | ||
71 | fprintf(stderr," -rf, --read-firmware filename.mi4\n"); | ||
72 | fprintf(stderr," -a, --add-bootloader filename.mi4\n"); | ||
73 | fprintf(stderr," -d, --delete-bootloader\n"); | ||
74 | fprintf(stderr," -of --update-original-firmware filename.mi4\n"); | ||
75 | fprintf(stderr," -bl --update-ppbl filename.bin\n"); | ||
76 | fprintf(stderr,"\n"); | ||
77 | |||
78 | #ifdef __WIN32__ | ||
79 | fprintf(stderr,"DISKNO is the number (e.g. 2) Windows has assigned to your sansa's hard disk.\n"); | ||
80 | fprintf(stderr,"The first hard disk in your computer (i.e. C:\\) will be disk 0, the next disk\n"); | ||
81 | fprintf(stderr,"will be disk 1 etc. sansapatcher will refuse to access a disk unless it\n"); | ||
82 | fprintf(stderr,"can identify it as being an E200 or C200.\n"); | ||
83 | fprintf(stderr,"\n"); | ||
84 | #else | ||
85 | #if defined(linux) || defined (__linux) | ||
86 | fprintf(stderr,"\"device\" is the device node (e.g. /dev/sda) assigned to your sansa.\n"); | ||
87 | #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) | ||
88 | fprintf(stderr,"\"device\" is the device node (e.g. /dev/da1) assigned to your sansa.\n"); | ||
89 | #elif defined(__APPLE__) && defined(__MACH__) | ||
90 | fprintf(stderr,"\"device\" is the device node (e.g. /dev/disk1) assigned to your sansa.\n"); | ||
91 | #endif | ||
92 | fprintf(stderr,"sansapatcher will refuse to access a disk unless it can identify it as being\n"); | ||
93 | fprintf(stderr,"an E200 or C200.\n"); | ||
94 | #endif | ||
95 | } | ||
96 | |||
97 | static const char* get_parttype(int pt) | ||
98 | { | ||
99 | int i; | ||
100 | static const char unknown[]="Unknown"; | ||
101 | |||
102 | if (pt == -1) { | ||
103 | return "HFS/HFS+"; | ||
104 | } | ||
105 | |||
106 | i=0; | ||
107 | while (parttypes[i].name != NULL) { | ||
108 | if (parttypes[i].type == pt) { | ||
109 | return (parttypes[i].name); | ||
110 | } | ||
111 | i++; | ||
112 | } | ||
113 | |||
114 | return unknown; | ||
115 | } | ||
116 | |||
117 | static void display_partinfo(struct sansa_t* sansa) | ||
118 | { | ||
119 | int i; | ||
120 | double sectors_per_MB = (1024.0*1024.0)/sansa->sector_size; | ||
121 | |||
122 | printf("[INFO] Part Start Sector End Sector Size (MB) Type\n"); | ||
123 | for ( i = 0; i < 4; i++ ) { | ||
124 | if (sansa->pinfo[i].start != 0) { | ||
125 | printf("[INFO] %d %10ld %10ld %10.1f %s (0x%02x)\n", | ||
126 | i, | ||
127 | sansa->pinfo[i].start, | ||
128 | sansa->pinfo[i].start+sansa->pinfo[i].size-1, | ||
129 | sansa->pinfo[i].size/sectors_per_MB, | ||
130 | get_parttype(sansa->pinfo[i].type), | ||
131 | sansa->pinfo[i].type); | ||
132 | } | ||
133 | } | ||
134 | } | ||
135 | |||
136 | |||
137 | int main(int argc, char* argv[]) | ||
138 | { | ||
139 | char yesno[4]; | ||
140 | int i; | ||
141 | int n; | ||
142 | char* filename; | ||
143 | int action = SHOW_INFO; | ||
144 | struct sansa_t sansa; | ||
145 | int res = 0; | ||
146 | unsigned char* buf = NULL; | ||
147 | unsigned int len; | ||
148 | |||
149 | fprintf(stderr,"sansapatcher v" VERSION " - (C) Dave Chapman 2006-2007\n"); | ||
150 | fprintf(stderr,"This is free software; see the source for copying conditions. There is NO\n"); | ||
151 | fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"); | ||
152 | |||
153 | if ((argc > 1) && ((strcmp(argv[1],"-h")==0) || (strcmp(argv[1],"--help")==0))) { | ||
154 | print_usage(); | ||
155 | return SANSA_OK; | ||
156 | } | ||
157 | |||
158 | if (sansa_alloc_buffer(&sansa, BUFFER_SIZE) < 0) { | ||
159 | fprintf(stderr,"Failed to allocate memory buffer\n"); | ||
160 | return SANSA_INTERNAL_ERROR; | ||
161 | } | ||
162 | |||
163 | if ((argc > 1) && (strcmp(argv[1],"--scan")==0)) { | ||
164 | if (sansa_scan(&sansa) == 0) | ||
165 | fprintf(stderr,"[ERR] No E200s or C200s found.\n"); | ||
166 | return SANSA_NOT_FOUND; | ||
167 | } | ||
168 | |||
169 | /* If the first parameter doesn't start with -, then we interpret it as a device */ | ||
170 | if ((argc > 1) && (argv[1][0] != '-')) { | ||
171 | sansa.diskname[0]=0; | ||
172 | #ifdef __WIN32__ | ||
173 | snprintf(sansa.diskname,sizeof(sansa.diskname),"\\\\.\\PhysicalDrive%s",argv[1]); | ||
174 | #else | ||
175 | strncpy(sansa.diskname,argv[1],sizeof(sansa.diskname)); | ||
176 | #endif | ||
177 | i = 2; | ||
178 | } else { | ||
179 | /* Autoscan for C200/E200s */ | ||
180 | n = sansa_scan(&sansa); | ||
181 | if (n==0) { | ||
182 | fprintf(stderr,"[ERR] No E200s or C200s found, aborting\n"); | ||
183 | fprintf(stderr,"[ERR] Please connect your sansa and ensure it is in UMS mode\n"); | ||
184 | #if defined(__APPLE__) && defined(__MACH__) | ||
185 | fprintf(stderr,"[ERR] Also ensure that your Sansa's main partition is not mounted.\n"); | ||
186 | #elif !defined(__WIN32__) | ||
187 | if (geteuid()!=0) { | ||
188 | fprintf(stderr,"[ERR] You may also need to run sansapatcher as root.\n"); | ||
189 | } | ||
190 | #endif | ||
191 | fprintf(stderr,"[ERR] Please refer to the Rockbox manual if you continue to have problems.\n"); | ||
192 | } else if (n > 1) { | ||
193 | fprintf(stderr,"[ERR] %d Sansas found, aborting\n",n); | ||
194 | fprintf(stderr,"[ERR] Please connect only one Sansa and re-run sansapatcher.\n"); | ||
195 | } | ||
196 | |||
197 | if (n != 1) { | ||
198 | #ifdef WITH_BOOTOBJS | ||
199 | if (argc==1) { | ||
200 | printf("\nPress ENTER to exit sansapatcher :"); | ||
201 | fgets(yesno,4,stdin); | ||
202 | } | ||
203 | #endif | ||
204 | return n > 1 ? SANSA_MULTIPLE_DEVICES : SANSA_NOT_FOUND; | ||
205 | } | ||
206 | |||
207 | i = 1; | ||
208 | } | ||
209 | |||
210 | #ifdef WITH_BOOTOBJS | ||
211 | action = INTERACTIVE; | ||
212 | #endif | ||
213 | |||
214 | while (i < argc) { | ||
215 | if ((strcmp(argv[i],"-l")==0) || (strcmp(argv[i],"--list")==0)) { | ||
216 | action = LIST_IMAGES; | ||
217 | i++; | ||
218 | } else if (strcmp(argv[i],"--install")==0) { | ||
219 | action = INSTALL; | ||
220 | i++; | ||
221 | } else if ((strcmp(argv[i],"-d")==0) || | ||
222 | (strcmp(argv[i],"--delete-bootloader")==0)) { | ||
223 | action = DELETE_BOOTLOADER; | ||
224 | i++; | ||
225 | } else if ((strcmp(argv[i],"-a")==0) || | ||
226 | (strcmp(argv[i],"--add-bootloader")==0)) { | ||
227 | action = ADD_BOOTLOADER; | ||
228 | i++; | ||
229 | if (i == argc) { print_usage(); return SANSA_WRONG_ARGUMENTS; } | ||
230 | filename=argv[i]; | ||
231 | i++; | ||
232 | } else if ((strcmp(argv[i],"-of")==0) || | ||
233 | (strcmp(argv[i],"--update-original-firmware")==0)) { | ||
234 | action = UPDATE_OF; | ||
235 | i++; | ||
236 | if (i == argc) { print_usage(); return SANSA_WRONG_ARGUMENTS; } | ||
237 | filename=argv[i]; | ||
238 | i++; | ||
239 | } else if ((strcmp(argv[i],"-bl")==0) || | ||
240 | (strcmp(argv[i],"--update-ppbl")==0)) { | ||
241 | action = UPDATE_PPBL; | ||
242 | i++; | ||
243 | if (i == argc) { print_usage(); return SANSA_WRONG_ARGUMENTS; } | ||
244 | filename=argv[i]; | ||
245 | i++; | ||
246 | } else if ((strcmp(argv[i],"-rf")==0) || | ||
247 | (strcmp(argv[i],"--read-firmware")==0)) { | ||
248 | action = READ_FIRMWARE; | ||
249 | i++; | ||
250 | if (i == argc) { print_usage(); return SANSA_WRONG_ARGUMENTS; } | ||
251 | filename=argv[i]; | ||
252 | i++; | ||
253 | } | ||
254 | } | ||
255 | |||
256 | if (sansa.diskname[0]==0) { | ||
257 | print_usage(); | ||
258 | return SANSA_WRONG_ARGUMENTS; | ||
259 | } | ||
260 | |||
261 | if (sansa_open(&sansa, 0) < 0) { | ||
262 | return SANSA_ACCESS_DENIED; | ||
263 | } | ||
264 | |||
265 | fprintf(stderr,"[INFO] Reading partition table from %s\n",sansa.diskname); | ||
266 | fprintf(stderr,"[INFO] Sector size is %d bytes\n",sansa.sector_size); | ||
267 | |||
268 | if (sansa_read_partinfo(&sansa,0) < 0) { | ||
269 | return SANSA_PARTITION_ERROR; | ||
270 | } | ||
271 | |||
272 | display_partinfo(&sansa); | ||
273 | |||
274 | i = is_sansa(&sansa); | ||
275 | if (i < 0) { | ||
276 | fprintf(stderr,"[ERR] Disk is not an E200 or C200 (%d), aborting.\n",i); | ||
277 | return SANSA_WRONG_TYPE; | ||
278 | } | ||
279 | |||
280 | if (sansa.hasoldbootloader) { | ||
281 | printf("[ERR] ************************************************************************\n"); | ||
282 | printf("[ERR] *** OLD ROCKBOX INSTALLATION DETECTED, ABORTING.\n"); | ||
283 | printf("[ERR] *** You must reinstall the original Sansa firmware before running\n"); | ||
284 | printf("[ERR] *** sansapatcher for the first time.\n"); | ||
285 | printf("[ERR] *** See http://www.rockbox.org/wiki/SansaE200Install\n"); | ||
286 | printf("[ERR] ************************************************************************\n"); | ||
287 | res = SANSA_OLD_INSTALL; | ||
288 | } else { | ||
289 | if (action==LIST_IMAGES) { | ||
290 | sansa_list_images(&sansa); | ||
291 | #ifdef WITH_BOOTOBJS | ||
292 | } else if (action==INTERACTIVE) { | ||
293 | |||
294 | printf("Enter i to install the Rockbox bootloader, u to uninstall\n or c to cancel and do nothing (i/u/c) :"); | ||
295 | |||
296 | if (fgets(yesno,4,stdin)) { | ||
297 | if (yesno[0]=='i') { | ||
298 | if (sansa_reopen_rw(&sansa) < 0) { | ||
299 | res = SANSA_CANNOT_REOPEN; | ||
300 | } | ||
301 | if (strcmp(sansa.targetname,"c200") == 0) { | ||
302 | len = LEN_bootimg_c200; | ||
303 | buf = bootimg_c200; | ||
304 | } else { | ||
305 | len = LEN_bootimg_e200; | ||
306 | buf = bootimg_e200; | ||
307 | } | ||
308 | if (sansa_add_bootloader(&sansa, buf, len)==0) { | ||
309 | fprintf(stderr,"[INFO] Bootloader installed successfully.\n"); | ||
310 | } else { | ||
311 | fprintf(stderr,"[ERR] --install failed.\n"); | ||
312 | res = SANSA_INSTALL_FAILED; | ||
313 | } | ||
314 | } else if (yesno[0]=='u') { | ||
315 | if (sansa_reopen_rw(&sansa) < 0) { | ||
316 | res = SANSA_CANNOT_REOPEN; | ||
317 | } | ||
318 | |||
319 | if (sansa_delete_bootloader(&sansa)==0) { | ||
320 | fprintf(stderr,"[INFO] Bootloader removed.\n"); | ||
321 | } else { | ||
322 | fprintf(stderr,"[ERR] Bootloader removal failed.\n"); | ||
323 | res = SANSA_UNINSTALL_FAILED; | ||
324 | } | ||
325 | } | ||
326 | } | ||
327 | #endif | ||
328 | } else if (action==READ_FIRMWARE) { | ||
329 | if (sansa_read_firmware(&sansa, filename)==0) { | ||
330 | fprintf(stderr,"[INFO] Firmware read to file %s.\n",filename); | ||
331 | } else { | ||
332 | fprintf(stderr,"[ERR] --read-firmware failed.\n"); | ||
333 | } | ||
334 | #ifdef WITH_BOOTOBJS | ||
335 | } else if (action==INSTALL) { | ||
336 | if (sansa_reopen_rw(&sansa) < 0) { | ||
337 | return SANSA_CANNOT_REOPEN; | ||
338 | } | ||
339 | |||
340 | if (strcmp(sansa.targetname,"c200") == 0) { | ||
341 | len = LEN_bootimg_c200; | ||
342 | buf = bootimg_c200; | ||
343 | } else { | ||
344 | len = LEN_bootimg_e200; | ||
345 | buf = bootimg_e200; | ||
346 | } | ||
347 | |||
348 | if (sansa_add_bootloader(&sansa, buf, len)==0) { | ||
349 | fprintf(stderr,"[INFO] Bootloader installed successfully.\n"); | ||
350 | } else { | ||
351 | fprintf(stderr,"[ERR] --install failed.\n"); | ||
352 | } | ||
353 | #endif | ||
354 | } else if (action==ADD_BOOTLOADER) { | ||
355 | if (sansa_reopen_rw(&sansa) < 0) { | ||
356 | return SANSA_CANNOT_REOPEN; | ||
357 | } | ||
358 | |||
359 | len = sansa_read_bootloader(&sansa, filename, &buf); | ||
360 | if (len > 0) { | ||
361 | if (sansa_add_bootloader(&sansa, buf, len)==0) { | ||
362 | fprintf(stderr,"[INFO] Bootloader %s written to device.\n",filename); | ||
363 | } else { | ||
364 | fprintf(stderr,"[ERR] --add-bootloader failed.\n"); | ||
365 | } | ||
366 | } | ||
367 | } else if (action==DELETE_BOOTLOADER) { | ||
368 | if (sansa_reopen_rw(&sansa) < 0) { | ||
369 | return SANSA_CANNOT_REOPEN; | ||
370 | } | ||
371 | |||
372 | if (sansa_delete_bootloader(&sansa)==0) { | ||
373 | fprintf(stderr,"[INFO] Bootloader removed successfully.\n"); | ||
374 | } else { | ||
375 | fprintf(stderr,"[ERR] --delete-bootloader failed.\n"); | ||
376 | } | ||
377 | } else if (action==UPDATE_OF) { | ||
378 | if (sansa_reopen_rw(&sansa) < 0) { | ||
379 | return SANSA_CANNOT_REOPEN; | ||
380 | } | ||
381 | |||
382 | if (sansa_update_of(&sansa, filename)==0) { | ||
383 | fprintf(stderr,"[INFO] OF updated successfully.\n"); | ||
384 | } else { | ||
385 | fprintf(stderr,"[ERR] --update-original-firmware failed.\n"); | ||
386 | } | ||
387 | } else if (action==UPDATE_PPBL) { | ||
388 | printf("[WARN] PPBL installation will overwrite your bootloader. This will lead to a\n"); | ||
389 | printf(" Sansa that won't boot if the bootloader file is invalid. Only continue if\n"); | ||
390 | printf(" you're sure you know what you're doing.\n"); | ||
391 | printf(" Continue (y/n)? "); | ||
392 | |||
393 | if (fgets(yesno,4,stdin)) { | ||
394 | if (yesno[0]=='y') { | ||
395 | if (sansa_reopen_rw(&sansa) < 0) { | ||
396 | return SANSA_CANNOT_REOPEN; | ||
397 | } | ||
398 | |||
399 | if (sansa_update_ppbl(&sansa, filename)==0) { | ||
400 | fprintf(stderr,"[INFO] PPBL updated successfully.\n"); | ||
401 | } else { | ||
402 | fprintf(stderr,"[ERR] --update-ppbl failed.\n"); | ||
403 | } | ||
404 | } | ||
405 | } | ||
406 | } | ||
407 | } | ||
408 | |||
409 | sansa_close(&sansa); | ||
410 | sansa_dealloc_buffer(&sansa); | ||
411 | |||
412 | #ifdef WITH_BOOTOBJS | ||
413 | if (action==INTERACTIVE) { | ||
414 | printf("Press ENTER to exit sansapatcher :"); | ||
415 | fgets(yesno,4,stdin); | ||
416 | } | ||
417 | #endif | ||
418 | |||
419 | return res; | ||
420 | } | ||
diff --git a/utils/sansapatcher/parttypes.h b/utils/sansapatcher/parttypes.h new file mode 100644 index 0000000000..f8de303553 --- /dev/null +++ b/utils/sansapatcher/parttypes.h | |||
@@ -0,0 +1,109 @@ | |||
1 | /* DOS partition types - taken from fdisk */ | ||
2 | |||
3 | struct parttype { | ||
4 | unsigned char type; | ||
5 | char *name; | ||
6 | }; | ||
7 | |||
8 | struct parttype parttypes[] = { | ||
9 | {0x00, "Empty"}, | ||
10 | {0x01, "FAT12"}, | ||
11 | {0x02, "XENIX root"}, | ||
12 | {0x03, "XENIX usr"}, | ||
13 | {0x04, "FAT16 <32M"}, | ||
14 | {0x05, "Extended"}, /* DOS 3.3+ extended partition */ | ||
15 | {0x06, "FAT16"}, /* DOS 16-bit >=32M */ | ||
16 | {0x07, "HPFS/NTFS"}, /* OS/2 IFS, eg, HPFS or NTFS or QNX */ | ||
17 | {0x08, "AIX"}, /* AIX boot (AIX -- PS/2 port) or SplitDrive */ | ||
18 | {0x09, "AIX bootable"}, /* AIX data or Coherent */ | ||
19 | {0x0a, "OS/2 Boot Manager"},/* OS/2 Boot Manager */ | ||
20 | {0x0b, "W95 FAT32"}, | ||
21 | {0x0c, "W95 FAT32 (LBA)"},/* LBA really is `Extended Int 13h' */ | ||
22 | {0x0e, "W95 FAT16 (LBA)"}, | ||
23 | {0x0f, "W95 Ext'd (LBA)"}, | ||
24 | {0x10, "OPUS"}, | ||
25 | {0x11, "Hidden FAT12"}, | ||
26 | {0x12, "Compaq diagnostics"}, | ||
27 | {0x14, "Hidden FAT16 <32M"}, | ||
28 | {0x16, "Hidden FAT16"}, | ||
29 | {0x17, "Hidden HPFS/NTFS"}, | ||
30 | {0x18, "AST SmartSleep"}, | ||
31 | {0x1b, "Hidden W95 FAT32"}, | ||
32 | {0x1c, "Hidden W95 FAT32 (LBA)"}, | ||
33 | {0x1e, "Hidden W95 FAT16 (LBA)"}, | ||
34 | {0x24, "NEC DOS"}, | ||
35 | {0x39, "Plan 9"}, | ||
36 | {0x3c, "PartitionMagic recovery"}, | ||
37 | {0x40, "Venix 80286"}, | ||
38 | {0x41, "PPC PReP Boot"}, | ||
39 | {0x42, "SFS"}, | ||
40 | {0x4d, "QNX4.x"}, | ||
41 | {0x4e, "QNX4.x 2nd part"}, | ||
42 | {0x4f, "QNX4.x 3rd part"}, | ||
43 | {0x50, "OnTrack DM"}, | ||
44 | {0x51, "OnTrack DM6 Aux1"}, /* (or Novell) */ | ||
45 | {0x52, "CP/M"}, /* CP/M or Microport SysV/AT */ | ||
46 | {0x53, "OnTrack DM6 Aux3"}, | ||
47 | {0x54, "OnTrackDM6"}, | ||
48 | {0x55, "EZ-Drive"}, | ||
49 | {0x56, "Golden Bow"}, | ||
50 | {0x5c, "Priam Edisk"}, | ||
51 | {0x61, "SpeedStor"}, | ||
52 | {0x63, "GNU HURD or SysV"}, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */ | ||
53 | {0x64, "Novell Netware 286"}, | ||
54 | {0x65, "Novell Netware 386"}, | ||
55 | {0x70, "DiskSecure Multi-Boot"}, | ||
56 | {0x75, "PC/IX"}, | ||
57 | {0x80, "Old Minix"}, /* Minix 1.4a and earlier */ | ||
58 | {0x81, "Minix / old Linux"},/* Minix 1.4b and later */ | ||
59 | {0x82, "Linux swap / Solaris"}, | ||
60 | {0x83, "Linux"}, | ||
61 | {0x84, "OS/2 hidden C: drive"}, | ||
62 | {0x85, "Linux extended"}, | ||
63 | {0x86, "NTFS volume set"}, | ||
64 | {0x87, "NTFS volume set"}, | ||
65 | {0x88, "Linux plaintext"}, | ||
66 | {0x8e, "Linux LVM"}, | ||
67 | {0x93, "Amoeba"}, | ||
68 | {0x94, "Amoeba BBT"}, /* (bad block table) */ | ||
69 | {0x9f, "BSD/OS"}, /* BSDI */ | ||
70 | {0xa0, "IBM Thinkpad hibernation"}, | ||
71 | {0xa5, "FreeBSD"}, /* various BSD flavours */ | ||
72 | {0xa6, "OpenBSD"}, | ||
73 | {0xa7, "NeXTSTEP"}, | ||
74 | {0xa8, "Darwin UFS"}, | ||
75 | {0xa9, "NetBSD"}, | ||
76 | {0xab, "Darwin boot"}, | ||
77 | {0xb7, "BSDI fs"}, | ||
78 | {0xb8, "BSDI swap"}, | ||
79 | {0xbb, "Boot Wizard hidden"}, | ||
80 | {0xbe, "Solaris boot"}, | ||
81 | {0xbf, "Solaris"}, | ||
82 | {0xc1, "DRDOS/sec (FAT-12)"}, | ||
83 | {0xc4, "DRDOS/sec (FAT-16 < 32M)"}, | ||
84 | {0xc6, "DRDOS/sec (FAT-16)"}, | ||
85 | {0xc7, "Syrinx"}, | ||
86 | {0xda, "Non-FS data"}, | ||
87 | {0xdb, "CP/M / CTOS / ..."},/* CP/M or Concurrent CP/M or | ||
88 | Concurrent DOS or CTOS */ | ||
89 | {0xde, "Dell Utility"}, /* Dell PowerEdge Server utilities */ | ||
90 | {0xdf, "BootIt"}, /* BootIt EMBRM */ | ||
91 | {0xe1, "DOS access"}, /* DOS access or SpeedStor 12-bit FAT | ||
92 | extended partition */ | ||
93 | {0xe3, "DOS R/O"}, /* DOS R/O or SpeedStor */ | ||
94 | {0xe4, "SpeedStor"}, /* SpeedStor 16-bit FAT extended | ||
95 | partition < 1024 cyl. */ | ||
96 | {0xeb, "BeOS fs"}, | ||
97 | {0xee, "EFI GPT"}, /* Intel EFI GUID Partition Table */ | ||
98 | {0xef, "EFI (FAT-12/16/32)"},/* Intel EFI System Partition */ | ||
99 | {0xf0, "Linux/PA-RISC boot"},/* Linux/PA-RISC boot loader */ | ||
100 | {0xf1, "SpeedStor"}, | ||
101 | {0xf4, "SpeedStor"}, /* SpeedStor large partition */ | ||
102 | {0xf2, "DOS secondary"}, /* DOS 3.3+ secondary */ | ||
103 | {0xfd, "Linux raid autodetect"},/* New (2.2.x) raid partition with | ||
104 | autodetect using persistent | ||
105 | superblock */ | ||
106 | {0xfe, "LANstep"}, /* SpeedStor >1024 cyl. or LANstep */ | ||
107 | {0xff, "BBT"}, /* Xenix Bad Block Table */ | ||
108 | { 0, 0 } | ||
109 | }; | ||
diff --git a/utils/sansapatcher/sansaio-posix.c b/utils/sansapatcher/sansaio-posix.c new file mode 100644 index 0000000000..44c4dcc95c --- /dev/null +++ b/utils/sansapatcher/sansaio-posix.c | |||
@@ -0,0 +1,157 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006-2007 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #if !defined(_WIN32) /* all non-Windows platforms supported are POSIX. */ | ||
23 | #include <stdio.h> | ||
24 | #include <unistd.h> | ||
25 | #include <fcntl.h> | ||
26 | #include <string.h> | ||
27 | #include <stdlib.h> | ||
28 | #include <sys/types.h> | ||
29 | #include <sys/stat.h> | ||
30 | #include <sys/ioctl.h> | ||
31 | #include <errno.h> | ||
32 | |||
33 | #if defined(linux) || defined (__linux) | ||
34 | #include <sys/mount.h> | ||
35 | #define SANSA_SECTORSIZE_IOCTL BLKSSZGET | ||
36 | #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ | ||
37 | || defined(__bsdi__) || defined(__DragonFly__) | ||
38 | #include <sys/disk.h> | ||
39 | #define SANSA_SECTORSIZE_IOCTL DIOCGSECTORSIZE | ||
40 | #elif defined(__APPLE__) && defined(__MACH__) | ||
41 | #include <sys/disk.h> | ||
42 | #define SANSA_SECTORSIZE_IOCTL DKIOCGETBLOCKSIZE | ||
43 | #else | ||
44 | #error No sector-size detection implemented for this platform | ||
45 | #endif | ||
46 | |||
47 | #include "sansaio.h" | ||
48 | |||
49 | #if defined(__APPLE__) && defined(__MACH__) | ||
50 | static int sansa_unmount(struct sansa_t* sansa) | ||
51 | { | ||
52 | char cmd[4096]; | ||
53 | int res; | ||
54 | |||
55 | sprintf(cmd, "/usr/sbin/diskutil unmount \"%ss1\"",sansa->diskname); | ||
56 | fprintf(stderr,"[INFO] "); | ||
57 | res = system(cmd); | ||
58 | |||
59 | if (res==0) { | ||
60 | return 0; | ||
61 | } else { | ||
62 | perror("Unmount failed"); | ||
63 | return -1; | ||
64 | } | ||
65 | } | ||
66 | #endif | ||
67 | |||
68 | |||
69 | void sansa_print_error(char* msg) | ||
70 | { | ||
71 | perror(msg); | ||
72 | } | ||
73 | |||
74 | int sansa_open(struct sansa_t* sansa, int silent) | ||
75 | { | ||
76 | sansa->dh=open(sansa->diskname,O_RDONLY); | ||
77 | if (sansa->dh < 0) { | ||
78 | if (!silent) perror(sansa->diskname); | ||
79 | if(errno == EACCES) return -2; | ||
80 | else return -1; | ||
81 | } | ||
82 | |||
83 | if(ioctl(sansa->dh,SANSA_SECTORSIZE_IOCTL,&sansa->sector_size) < 0) { | ||
84 | sansa->sector_size=512; | ||
85 | if (!silent) { | ||
86 | fprintf(stderr,"[ERR] ioctl() call to get sector size failed, defaulting to %d\n" | ||
87 | ,sansa->sector_size); | ||
88 | } | ||
89 | } | ||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | |||
94 | int sansa_reopen_rw(struct sansa_t* sansa) | ||
95 | { | ||
96 | #if defined(__APPLE__) && defined(__MACH__) | ||
97 | if (sansa_unmount(sansa) < 0) | ||
98 | return -1; | ||
99 | #endif | ||
100 | |||
101 | close(sansa->dh); | ||
102 | sansa->dh=open(sansa->diskname,O_RDWR); | ||
103 | if (sansa->dh < 0) { | ||
104 | perror(sansa->diskname); | ||
105 | return -1; | ||
106 | } | ||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | int sansa_close(struct sansa_t* sansa) | ||
111 | { | ||
112 | close(sansa->dh); | ||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | int sansa_alloc_buffer(struct sansa_t *sansa, int bufsize) | ||
117 | { | ||
118 | sansa->sectorbuf=malloc(bufsize); | ||
119 | if (sansa->sectorbuf == NULL) { | ||
120 | return -1; | ||
121 | } | ||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | int sansa_dealloc_buffer(struct sansa_t* sansa) | ||
126 | { | ||
127 | if (sansa->sectorbuf == NULL) { | ||
128 | return -1; | ||
129 | } | ||
130 | free(sansa->sectorbuf); | ||
131 | sansa->sectorbuf = NULL; | ||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | int sansa_seek(struct sansa_t* sansa, loff_t pos) | ||
136 | { | ||
137 | off_t res; | ||
138 | |||
139 | res = lseek64(sansa->dh, pos, SEEK_SET); | ||
140 | |||
141 | if (res == -1) { | ||
142 | return -1; | ||
143 | } | ||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | int sansa_read(struct sansa_t* sansa, unsigned char* buf, int nbytes) | ||
148 | { | ||
149 | return read(sansa->dh, buf, nbytes); | ||
150 | } | ||
151 | |||
152 | int sansa_write(struct sansa_t* sansa, int nbytes) | ||
153 | { | ||
154 | return write(sansa->dh, sansa->sectorbuf, nbytes); | ||
155 | } | ||
156 | #endif | ||
157 | |||
diff --git a/utils/sansapatcher/sansaio-win32.c b/utils/sansapatcher/sansaio-win32.c new file mode 100644 index 0000000000..ee6a8cd93d --- /dev/null +++ b/utils/sansapatcher/sansaio-win32.c | |||
@@ -0,0 +1,217 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006-2007 Dave Chapman | ||
11 | * | ||
12 | * error(), lock_volume() and unlock_volume() functions and inspiration taken | ||
13 | * from: | ||
14 | * RawDisk - Direct Disk Read/Write Access for NT/2000/XP | ||
15 | * Copyright (c) 2003 Jan Kiszka | ||
16 | * http://www.stud.uni-hannover.de/user/73174/RawDisk/ | ||
17 | * | ||
18 | * This program is free software; you can redistribute it and/or | ||
19 | * modify it under the terms of the GNU General Public License | ||
20 | * as published by the Free Software Foundation; either version 2 | ||
21 | * of the License, or (at your option) any later version. | ||
22 | * | ||
23 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
24 | * KIND, either express or implied. | ||
25 | * | ||
26 | ****************************************************************************/ | ||
27 | |||
28 | #if defined(_WIN32) | ||
29 | #include <stdio.h> | ||
30 | #include <unistd.h> | ||
31 | #include <fcntl.h> | ||
32 | #include <string.h> | ||
33 | #include <stdlib.h> | ||
34 | #include <sys/types.h> | ||
35 | #include <sys/stat.h> | ||
36 | #ifdef __WIN32__ | ||
37 | #include <windows.h> | ||
38 | #include <winioctl.h> | ||
39 | #endif | ||
40 | |||
41 | #include "sansaio.h" | ||
42 | |||
43 | static int lock_volume(HANDLE hDisk) | ||
44 | { | ||
45 | DWORD dummy; | ||
46 | |||
47 | return DeviceIoControl(hDisk, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, | ||
48 | &dummy, NULL); | ||
49 | } | ||
50 | |||
51 | static int unlock_volume(HANDLE hDisk) | ||
52 | { | ||
53 | DWORD dummy; | ||
54 | |||
55 | return DeviceIoControl(hDisk, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, | ||
56 | &dummy, NULL); | ||
57 | } | ||
58 | |||
59 | void sansa_print_error(char* msg) | ||
60 | { | ||
61 | LPSTR pMsgBuf = NULL; | ||
62 | |||
63 | printf(msg); | ||
64 | FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | | ||
65 | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), | ||
66 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), pMsgBuf, | ||
67 | 0, NULL); | ||
68 | printf(pMsgBuf); | ||
69 | LocalFree(pMsgBuf); | ||
70 | } | ||
71 | |||
72 | int sansa_open(struct sansa_t* sansa, int silent) | ||
73 | { | ||
74 | DISK_GEOMETRY_EX diskgeometry_ex; | ||
75 | DISK_GEOMETRY diskgeometry; | ||
76 | unsigned long n; | ||
77 | |||
78 | sansa->dh = CreateFileA(sansa->diskname, GENERIC_READ, | ||
79 | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, | ||
80 | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL); | ||
81 | |||
82 | if (sansa->dh == INVALID_HANDLE_VALUE) { | ||
83 | if (!silent) sansa_print_error(" Error opening disk: "); | ||
84 | if(GetLastError() == ERROR_ACCESS_DENIED) | ||
85 | return -2; | ||
86 | else | ||
87 | return -1; | ||
88 | } | ||
89 | |||
90 | if (!lock_volume(sansa->dh)) { | ||
91 | if (!silent) sansa_print_error(" Error locking disk: "); | ||
92 | return -1; | ||
93 | } | ||
94 | |||
95 | if (!DeviceIoControl(sansa->dh, | ||
96 | IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, | ||
97 | NULL, | ||
98 | 0, | ||
99 | &diskgeometry_ex, | ||
100 | sizeof(diskgeometry_ex), | ||
101 | &n, | ||
102 | NULL)) { | ||
103 | if (!DeviceIoControl(sansa->dh, | ||
104 | IOCTL_DISK_GET_DRIVE_GEOMETRY, | ||
105 | NULL, | ||
106 | 0, | ||
107 | &diskgeometry, | ||
108 | sizeof(diskgeometry), | ||
109 | &n, | ||
110 | NULL)) { | ||
111 | if (!silent) sansa_print_error(" Error reading disk geometry: "); | ||
112 | return -1; | ||
113 | } else { | ||
114 | sansa->sector_size=diskgeometry.BytesPerSector; | ||
115 | } | ||
116 | } else { | ||
117 | sansa->sector_size=diskgeometry_ex.Geometry.BytesPerSector; | ||
118 | } | ||
119 | |||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | int sansa_reopen_rw(struct sansa_t* sansa) | ||
124 | { | ||
125 | /* Close existing file and re-open for writing */ | ||
126 | unlock_volume(sansa->dh); | ||
127 | CloseHandle(sansa->dh); | ||
128 | |||
129 | sansa->dh = CreateFileA(sansa->diskname, GENERIC_READ | GENERIC_WRITE, | ||
130 | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, | ||
131 | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL); | ||
132 | |||
133 | if (sansa->dh == INVALID_HANDLE_VALUE) { | ||
134 | sansa_print_error(" Error opening disk: "); | ||
135 | return -1; | ||
136 | } | ||
137 | |||
138 | if (!lock_volume(sansa->dh)) { | ||
139 | sansa_print_error(" Error locking disk: "); | ||
140 | return -1; | ||
141 | } | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | int sansa_close(struct sansa_t* sansa) | ||
147 | { | ||
148 | unlock_volume(sansa->dh); | ||
149 | CloseHandle(sansa->dh); | ||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | int sansa_alloc_buffer(struct sansa_t* sansa, int bufsize) | ||
154 | { | ||
155 | /* The ReadFile function requires a memory buffer aligned to a multiple of | ||
156 | the disk sector size. */ | ||
157 | sansa->sectorbuf = (unsigned char*)VirtualAlloc(NULL, bufsize, MEM_COMMIT, PAGE_READWRITE); | ||
158 | if (sansa->sectorbuf == NULL) { | ||
159 | sansa_print_error(" Error allocating a buffer: "); | ||
160 | return -1; | ||
161 | } | ||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | int sansa_dealloc_buffer(struct sansa_t* sansa) | ||
166 | { | ||
167 | if (sansa->sectorbuf == NULL) { | ||
168 | return -1; | ||
169 | } | ||
170 | if(!VirtualFree(sansa->sectorbuf, 0, MEM_RELEASE)) { | ||
171 | sansa_print_error(" Error releasing buffer "); | ||
172 | return -1; | ||
173 | } | ||
174 | sansa->sectorbuf = NULL; | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | int sansa_seek(struct sansa_t* sansa, loff_t pos) | ||
179 | { | ||
180 | LARGE_INTEGER li; | ||
181 | |||
182 | li.QuadPart = pos; | ||
183 | |||
184 | li.LowPart = SetFilePointer (sansa->dh, li.LowPart, &li.HighPart, FILE_BEGIN); | ||
185 | |||
186 | if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { | ||
187 | sansa_print_error(" Seek error "); | ||
188 | return -1; | ||
189 | } | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | int sansa_read(struct sansa_t* sansa, unsigned char* buf, int nbytes) | ||
194 | { | ||
195 | unsigned long count; | ||
196 | |||
197 | if (!ReadFile(sansa->dh, buf, nbytes, &count, NULL)) { | ||
198 | sansa_print_error(" Error reading from disk: "); | ||
199 | return -1; | ||
200 | } | ||
201 | |||
202 | return count; | ||
203 | } | ||
204 | |||
205 | int sansa_write(struct sansa_t* sansa, int nbytes) | ||
206 | { | ||
207 | unsigned long count; | ||
208 | |||
209 | if (!WriteFile(sansa->dh, sansa->sectorbuf, nbytes, &count, NULL)) { | ||
210 | sansa_print_error(" Error writing to disk: "); | ||
211 | return -1; | ||
212 | } | ||
213 | |||
214 | return count; | ||
215 | } | ||
216 | #endif | ||
217 | |||
diff --git a/utils/sansapatcher/sansaio.h b/utils/sansapatcher/sansaio.h new file mode 100644 index 0000000000..61e2f1d1b2 --- /dev/null +++ b/utils/sansapatcher/sansaio.h | |||
@@ -0,0 +1,88 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006-2007 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef __SANSAIO_H | ||
23 | #define __SANSAIO_H | ||
24 | |||
25 | #include <stdint.h> | ||
26 | #if !defined(_MSC_VER) | ||
27 | #include <unistd.h> /* not available on MSVC */ | ||
28 | #endif | ||
29 | |||
30 | #if defined(__WIN32__) || defined(_WIN32) | ||
31 | #include <windows.h> | ||
32 | #define loff_t int64_t | ||
33 | #else | ||
34 | #define HANDLE int | ||
35 | #define O_BINARY 0 | ||
36 | |||
37 | /* Only Linux seems to need lseek64 and loff_t */ | ||
38 | #if !defined(linux) && !defined (__linux) | ||
39 | #define loff_t off_t | ||
40 | #define lseek64 lseek | ||
41 | #endif | ||
42 | |||
43 | #endif | ||
44 | |||
45 | #ifdef __cplusplus | ||
46 | extern "C" { | ||
47 | #endif | ||
48 | |||
49 | struct sansa_partinfo_t { | ||
50 | unsigned long start; /* first sector (LBA) */ | ||
51 | unsigned long size; /* number of sectors */ | ||
52 | int type; | ||
53 | }; | ||
54 | |||
55 | struct mi4header_t { | ||
56 | uint32_t version; | ||
57 | uint32_t length; | ||
58 | uint32_t crc32; | ||
59 | uint32_t enctype; | ||
60 | uint32_t mi4size; | ||
61 | uint32_t plaintext; | ||
62 | }; | ||
63 | |||
64 | struct sansa_t { | ||
65 | HANDLE dh; | ||
66 | unsigned char* sectorbuf; | ||
67 | char diskname[4096]; | ||
68 | int sector_size; | ||
69 | struct sansa_partinfo_t pinfo[4]; | ||
70 | int hasoldbootloader; | ||
71 | char* targetname; /* "e200" or "c200" */ | ||
72 | loff_t start; /* Offset in bytes of firmware partition from start of disk */ | ||
73 | }; | ||
74 | |||
75 | void sansa_print_error(char* msg); | ||
76 | int sansa_open(struct sansa_t* sansa, int silent); | ||
77 | int sansa_reopen_rw(struct sansa_t* sansa); | ||
78 | int sansa_close(struct sansa_t* sansa); | ||
79 | int sansa_seek(struct sansa_t* sansa, loff_t pos); | ||
80 | int sansa_read(struct sansa_t* sansa, unsigned char* buf, int nbytes); | ||
81 | int sansa_write(struct sansa_t* sansa, int nbytes); | ||
82 | int sansa_alloc_buffer(struct sansa_t* sansa, int bufsize); | ||
83 | int sansa_dealloc_buffer(struct sansa_t* sansa); | ||
84 | |||
85 | #ifdef __cplusplus | ||
86 | } | ||
87 | #endif | ||
88 | #endif | ||
diff --git a/utils/sansapatcher/sansapatcher.c b/utils/sansapatcher/sansapatcher.c new file mode 100644 index 0000000000..e3b105dcca --- /dev/null +++ b/utils/sansapatcher/sansapatcher.c | |||
@@ -0,0 +1,975 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006-2007 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include <stdio.h> | ||
23 | #include <unistd.h> | ||
24 | #include <fcntl.h> | ||
25 | #include <string.h> | ||
26 | #include <stdlib.h> | ||
27 | #include <inttypes.h> | ||
28 | #include <sys/types.h> | ||
29 | #include <sys/stat.h> | ||
30 | |||
31 | #include "sansaio.h" | ||
32 | #include "sansapatcher.h" | ||
33 | |||
34 | /* The offset of the MI4 image header in the firmware partition */ | ||
35 | #define PPMI_OFFSET 0x80000 | ||
36 | #define NVPARAMS_OFFSET 0x780000 | ||
37 | #define NVPARAMS_SIZE (0x80000-0x200) | ||
38 | |||
39 | int sansa_verbose = 0; | ||
40 | |||
41 | /* Windows requires the buffer for disk I/O to be aligned in memory on a | ||
42 | multiple of the disk volume size - so we use a single global variable | ||
43 | and initialise it with sansa_alloc_buf() in main(). | ||
44 | */ | ||
45 | |||
46 | static off_t filesize(int fd) { | ||
47 | struct stat buf; | ||
48 | |||
49 | if (fstat(fd,&buf) < 0) { | ||
50 | perror("[ERR] Checking filesize of input file"); | ||
51 | return -1; | ||
52 | } else { | ||
53 | return(buf.st_size); | ||
54 | } | ||
55 | } | ||
56 | |||
57 | /* Partition table parsing code taken from Rockbox */ | ||
58 | |||
59 | #define MAX_SECTOR_SIZE 2048 | ||
60 | #define SECTOR_SIZE 512 | ||
61 | |||
62 | static inline int32_t le2int(const unsigned char* buf) | ||
63 | { | ||
64 | int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; | ||
65 | |||
66 | return res; | ||
67 | } | ||
68 | |||
69 | static inline uint32_t le2uint(const unsigned char* buf) | ||
70 | { | ||
71 | uint32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; | ||
72 | |||
73 | return res; | ||
74 | } | ||
75 | |||
76 | static inline void int2le(unsigned int val, unsigned char* addr) | ||
77 | { | ||
78 | addr[0] = val & 0xFF; | ||
79 | addr[1] = (val >> 8) & 0xff; | ||
80 | addr[2] = (val >> 16) & 0xff; | ||
81 | addr[3] = (val >> 24) & 0xff; | ||
82 | } | ||
83 | |||
84 | #define BYTES2INT32(array,pos)\ | ||
85 | ((long)array[pos] | ((long)array[pos+1] << 8 ) |\ | ||
86 | ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 )) | ||
87 | |||
88 | int sansa_read_partinfo(struct sansa_t* sansa, int silent) | ||
89 | { | ||
90 | int i; | ||
91 | unsigned long count; | ||
92 | |||
93 | count = sansa_read(sansa,sansa->sectorbuf, sansa->sector_size); | ||
94 | |||
95 | if (count <= 0) { | ||
96 | sansa_print_error(" Error reading from disk: "); | ||
97 | return -1; | ||
98 | } | ||
99 | |||
100 | if ((sansa->sectorbuf[510] == 0x55) && (sansa->sectorbuf[511] == 0xaa)) { | ||
101 | /* parse partitions */ | ||
102 | for ( i = 0; i < 4; i++ ) { | ||
103 | unsigned char* ptr = sansa->sectorbuf + 0x1be + 16*i; | ||
104 | sansa->pinfo[i].type = ptr[4]; | ||
105 | sansa->pinfo[i].start = BYTES2INT32(ptr, 8); | ||
106 | sansa->pinfo[i].size = BYTES2INT32(ptr, 12); | ||
107 | |||
108 | /* extended? */ | ||
109 | if ( sansa->pinfo[i].type == 5 ) { | ||
110 | /* not handled yet */ | ||
111 | } | ||
112 | } | ||
113 | } else if ((sansa->sectorbuf[0] == 'E') && (sansa->sectorbuf[1] == 'R')) { | ||
114 | if (!silent) fprintf(stderr,"[ERR] Bad boot sector signature\n"); | ||
115 | return -1; | ||
116 | } | ||
117 | |||
118 | /* Calculate the starting position of the firmware partition */ | ||
119 | sansa->start = (loff_t)sansa->pinfo[1].start*(loff_t)sansa->sector_size; | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | /* NOTE: memmem implementation copied from glibc-2.2.4 - it's a GNU | ||
124 | extension and is not universally. In addition, early versions of | ||
125 | memmem had a serious bug - the meaning of needle and haystack were | ||
126 | reversed. */ | ||
127 | |||
128 | /* Copyright (C) 1991,92,93,94,96,97,98,2000 Free Software Foundation, Inc. | ||
129 | This file is part of the GNU C Library. | ||
130 | |||
131 | The GNU C Library is free software; you can redistribute it and/or | ||
132 | modify it under the terms of the GNU Lesser General Public | ||
133 | License as published by the Free Software Foundation; either | ||
134 | version 2.1 of the License, or (at your option) any later version. | ||
135 | |||
136 | The GNU C Library is distributed in the hope that it will be useful, | ||
137 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
138 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
139 | Lesser General Public License for more details. | ||
140 | |||
141 | You should have received a copy of the GNU Lesser General Public | ||
142 | License along with the GNU C Library; if not, write to the Free | ||
143 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
144 | 02111-1307 USA. */ | ||
145 | |||
146 | /* Return the first occurrence of NEEDLE in HAYSTACK. */ | ||
147 | static void * | ||
148 | sansa_memmem (haystack, haystack_len, needle, needle_len) | ||
149 | const void *haystack; | ||
150 | size_t haystack_len; | ||
151 | const void *needle; | ||
152 | size_t needle_len; | ||
153 | { | ||
154 | const char *begin; | ||
155 | const char *const last_possible | ||
156 | = (const char *) haystack + haystack_len - needle_len; | ||
157 | |||
158 | if (needle_len == 0) | ||
159 | /* The first occurrence of the empty string is deemed to occur at | ||
160 | the beginning of the string. */ | ||
161 | return (void *) haystack; | ||
162 | |||
163 | /* Sanity check, otherwise the loop might search through the whole | ||
164 | memory. */ | ||
165 | if (__builtin_expect (haystack_len < needle_len, 0)) | ||
166 | return NULL; | ||
167 | |||
168 | for (begin = (const char *) haystack; begin <= last_possible; ++begin) | ||
169 | if (begin[0] == ((const char *) needle)[0] && | ||
170 | !memcmp ((const void *) &begin[1], | ||
171 | (const void *) ((const char *) needle + 1), | ||
172 | needle_len - 1)) | ||
173 | return (void *) begin; | ||
174 | |||
175 | return NULL; | ||
176 | } | ||
177 | |||
178 | /* | ||
179 | * CRC32 implementation taken from: | ||
180 | * | ||
181 | * efone - Distributed internet phone system. | ||
182 | * | ||
183 | * (c) 1999,2000 Krzysztof Dabrowski | ||
184 | * (c) 1999,2000 ElysiuM deeZine | ||
185 | * | ||
186 | * This program is free software; you can redistribute it and/or | ||
187 | * modify it under the terms of the GNU General Public License | ||
188 | * as published by the Free Software Foundation; either version | ||
189 | * 2 of the License, or (at your option) any later version. | ||
190 | * | ||
191 | */ | ||
192 | |||
193 | /* crc_tab[] -- this crcTable is being build by chksum_crc32GenTab(). | ||
194 | * so make sure, you call it before using the other | ||
195 | * functions! | ||
196 | */ | ||
197 | static unsigned int crc_tab[256]; | ||
198 | |||
199 | /* chksum_crc() -- to a given block, this one calculates the | ||
200 | * crc32-checksum until the length is | ||
201 | * reached. the crc32-checksum will be | ||
202 | * the result. | ||
203 | */ | ||
204 | static unsigned int chksum_crc32 (const unsigned char *block, unsigned int length) | ||
205 | { | ||
206 | register unsigned long crc; | ||
207 | unsigned long i; | ||
208 | |||
209 | crc = 0; | ||
210 | for (i = 0; i < length; i++) | ||
211 | { | ||
212 | crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF]; | ||
213 | } | ||
214 | return (crc); | ||
215 | } | ||
216 | |||
217 | /* chksum_crc32gentab() -- to a global crc_tab[256], this one will | ||
218 | * calculate the crcTable for crc32-checksums. | ||
219 | * it is generated to the polynom [..] | ||
220 | */ | ||
221 | |||
222 | static void chksum_crc32gentab (void) | ||
223 | { | ||
224 | unsigned long crc, poly; | ||
225 | int i, j; | ||
226 | |||
227 | poly = 0xEDB88320L; | ||
228 | for (i = 0; i < 256; i++) | ||
229 | { | ||
230 | crc = i; | ||
231 | for (j = 8; j > 0; j--) | ||
232 | { | ||
233 | if (crc & 1) | ||
234 | { | ||
235 | crc = (crc >> 1) ^ poly; | ||
236 | } | ||
237 | else | ||
238 | { | ||
239 | crc >>= 1; | ||
240 | } | ||
241 | } | ||
242 | crc_tab[i] = crc; | ||
243 | } | ||
244 | } | ||
245 | |||
246 | /* Known keys for Sansa E200 and C200 firmwares: */ | ||
247 | #define NUM_KEYS ((int)(sizeof(keys)/sizeof(keys[0]))) | ||
248 | static const uint32_t keys[][4] = { | ||
249 | { 0xe494e96e, 0x3ee32966, 0x6f48512b, 0xa93fbb42 }, /* "sansa" */ | ||
250 | { 0xd7b10538, 0xc662945b, 0x1b3fce68, 0xf389c0e6 }, /* "sansa_gh" */ | ||
251 | { 0x1d29ddc0, 0x2579c2cd, 0xce339e1a, 0x75465dfe }, /* sansa 103 */ | ||
252 | |||
253 | { 0x2a7968de, 0x15127979, 0x142e60a7, 0xe49c1893 }, /* c200 1.00.03 */ | ||
254 | { 0xbf2d06fa, 0xf0e23d59, 0x29738132, 0xe2d04ca7 }, /* c200 1.00.04 and up*/ | ||
255 | { 0xa913d139, 0xf842f398, 0x3e03f1a6, 0x060ee012 }, /* c200 1.01.05 and up*/ | ||
256 | { 0x0fe92902, 0xe8cc0f89, 0x6ff568ba, 0x1eff5161 }, /* c200 1.01.07 */ | ||
257 | }; | ||
258 | |||
259 | /* | ||
260 | |||
261 | tea_decrypt() from http://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm | ||
262 | |||
263 | "Following is an adaptation of the reference encryption and decryption | ||
264 | routines in C, released into the public domain by David Wheeler and | ||
265 | Roger Needham:" | ||
266 | |||
267 | */ | ||
268 | |||
269 | /* NOTE: The mi4 version of TEA uses a different initial value to sum compared | ||
270 | to the reference implementation and the main loop is 8 iterations, not | ||
271 | 32. | ||
272 | */ | ||
273 | |||
274 | static void tea_decrypt(uint32_t* v0, uint32_t* v1, const uint32_t* k) { | ||
275 | uint32_t sum=0xF1BBCDC8, i; /* set up */ | ||
276 | uint32_t delta=0x9E3779B9; /* a key schedule constant */ | ||
277 | uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */ | ||
278 | for(i=0; i<8; i++) { /* basic cycle start */ | ||
279 | *v1 -= ((*v0<<4) + k2) ^ (*v0 + sum) ^ ((*v0>>5) + k3); | ||
280 | *v0 -= ((*v1<<4) + k0) ^ (*v1 + sum) ^ ((*v1>>5) + k1); | ||
281 | sum -= delta; /* end cycle */ | ||
282 | } | ||
283 | } | ||
284 | |||
285 | /* mi4 files are encrypted in 64-bit blocks (two little-endian 32-bit | ||
286 | integers) and the key is incremented after each block | ||
287 | */ | ||
288 | |||
289 | static void tea_decrypt_buf(const unsigned char* src, unsigned char* dest, | ||
290 | size_t n, const uint32_t * initial_key) | ||
291 | { | ||
292 | uint32_t v0, v1; | ||
293 | unsigned int i; | ||
294 | uint32_t key[4]; | ||
295 | |||
296 | memcpy(key, initial_key, sizeof(key)); | ||
297 | for (i = 0; i < (n / 8); i++) { | ||
298 | v0 = le2int(src); | ||
299 | v1 = le2int(src+4); | ||
300 | |||
301 | tea_decrypt(&v0, &v1, key); | ||
302 | |||
303 | int2le(v0, dest); | ||
304 | int2le(v1, dest+4); | ||
305 | |||
306 | src += 8; | ||
307 | dest += 8; | ||
308 | |||
309 | /* Now increment the key */ | ||
310 | key[0]++; | ||
311 | if (key[0]==0) { | ||
312 | key[1]++; | ||
313 | if (key[1]==0) { | ||
314 | key[2]++; | ||
315 | if (key[2]==0) { | ||
316 | key[3]++; | ||
317 | } | ||
318 | } | ||
319 | } | ||
320 | } | ||
321 | } | ||
322 | |||
323 | static int get_mi4header(const unsigned char* buf,struct mi4header_t* mi4header) | ||
324 | { | ||
325 | if (memcmp(buf,"PPOS",4)!=0) | ||
326 | return -1; | ||
327 | |||
328 | mi4header->version = le2int(buf+0x04); | ||
329 | mi4header->length = le2int(buf+0x08); | ||
330 | mi4header->crc32 = le2int(buf+0x0c); | ||
331 | mi4header->enctype = le2int(buf+0x10); | ||
332 | mi4header->mi4size = le2int(buf+0x14); | ||
333 | mi4header->plaintext = le2int(buf+0x18); | ||
334 | |||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | static int set_mi4header(unsigned char* buf,const struct mi4header_t* mi4header) | ||
339 | { | ||
340 | if (memcmp(buf,"PPOS",4)!=0) | ||
341 | return -1; | ||
342 | |||
343 | int2le(mi4header->version ,buf+0x04); | ||
344 | int2le(mi4header->length ,buf+0x08); | ||
345 | int2le(mi4header->crc32 ,buf+0x0c); | ||
346 | int2le(mi4header->enctype ,buf+0x10); | ||
347 | int2le(mi4header->mi4size ,buf+0x14); | ||
348 | int2le(mi4header->plaintext ,buf+0x18); | ||
349 | |||
350 | /* Add a dummy DSA signature */ | ||
351 | memset(buf+0x1c,0,40); | ||
352 | buf[0x2f] = 1; | ||
353 | |||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | static int sansa_seek_and_read(struct sansa_t* sansa, loff_t pos, unsigned char* buf, int nbytes) | ||
358 | { | ||
359 | int n; | ||
360 | |||
361 | if (sansa_seek(sansa, pos) < 0) { | ||
362 | return -1; | ||
363 | } | ||
364 | |||
365 | if ((n = sansa_read(sansa,buf,nbytes)) < 0) { | ||
366 | return -1; | ||
367 | } | ||
368 | |||
369 | if (n < nbytes) { | ||
370 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", | ||
371 | nbytes,n); | ||
372 | return -1; | ||
373 | } | ||
374 | |||
375 | return 0; | ||
376 | } | ||
377 | |||
378 | |||
379 | /* We identify an E200 based on the following criteria: | ||
380 | |||
381 | 1) Exactly two partitions; | ||
382 | 2) First partition is type "W95 FAT32" (0x0b or 0x0c); | ||
383 | 3) Second partition is type "OS/2 hidden C: drive" (0x84); | ||
384 | 4) The "PPBL" string appears at offset 0 in the 2nd partition; | ||
385 | 5) The "PPMI" string appears at offset PPMI_OFFSET in the 2nd partition. | ||
386 | */ | ||
387 | |||
388 | int is_sansa(struct sansa_t* sansa) | ||
389 | { | ||
390 | struct mi4header_t mi4header; | ||
391 | int ppmi_length; | ||
392 | int ppbl_length; | ||
393 | |||
394 | /* Check partition layout */ | ||
395 | if (((sansa->pinfo[0].type != 0x06) && | ||
396 | (sansa->pinfo[0].type != 0x0b) && | ||
397 | (sansa->pinfo[0].type != 0x0c) && | ||
398 | (sansa->pinfo[0].type != 0x0e)) || | ||
399 | (sansa->pinfo[1].type != 0x84) || | ||
400 | (sansa->pinfo[2].type != 0x00) || | ||
401 | (sansa->pinfo[3].type != 0x00)) { | ||
402 | /* Bad partition layout, abort */ | ||
403 | return -1; | ||
404 | } | ||
405 | |||
406 | /* Check Bootloader header */ | ||
407 | if (sansa_seek_and_read(sansa, sansa->start, sansa->sectorbuf, 0x200) < 0) { | ||
408 | return -2; | ||
409 | } | ||
410 | if (memcmp(sansa->sectorbuf,"PPBL",4)!=0) { | ||
411 | /* No bootloader header, abort */ | ||
412 | return -4; | ||
413 | } | ||
414 | ppbl_length = (le2int(sansa->sectorbuf+4) + 0x1ff) & ~0x1ff; | ||
415 | |||
416 | /* Sanity/safety check - the bootloader can't be larger than PPMI_OFFSET */ | ||
417 | if (ppbl_length > PPMI_OFFSET) | ||
418 | { | ||
419 | return -5; | ||
420 | } | ||
421 | |||
422 | /* Load Sansa bootloader and check for "Sansa C200" magic string */ | ||
423 | if (sansa_seek_and_read(sansa, sansa->start + 0x200, sansa->sectorbuf, ppbl_length) < 0) { | ||
424 | fprintf(stderr,"[ERR] Seek and read to 0x%08"PRIx64" in is_sansa failed.\n", | ||
425 | sansa->start+0x200); | ||
426 | return -6; | ||
427 | } | ||
428 | if (sansa_memmem(sansa->sectorbuf, ppbl_length, "Sansa C200", 10) != NULL) { | ||
429 | /* C200 */ | ||
430 | sansa->targetname="c200"; | ||
431 | } else { | ||
432 | /* E200 */ | ||
433 | sansa->targetname="e200"; | ||
434 | } | ||
435 | |||
436 | /* Check Main firmware header */ | ||
437 | if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET, sansa->sectorbuf, 0x200) < 0) { | ||
438 | fprintf(stderr,"[ERR] Seek to 0x%"PRIx64" in is_sansa failed.\n", | ||
439 | sansa->start+PPMI_OFFSET); | ||
440 | return -5; | ||
441 | } | ||
442 | if (memcmp(sansa->sectorbuf,"PPMI",4)!=0) { | ||
443 | /* No bootloader header, abort */ | ||
444 | return -7; | ||
445 | } | ||
446 | ppmi_length = le2int(sansa->sectorbuf+4); | ||
447 | |||
448 | /* Check main mi4 file header */ | ||
449 | if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET+0x200, sansa->sectorbuf, 0x200) < 0) { | ||
450 | fprintf(stderr,"[ERR] Seek to 0x%"PRIx64" in is_sansa failed.\n", | ||
451 | sansa->start+PPMI_OFFSET+0x200); | ||
452 | return -5; | ||
453 | } | ||
454 | |||
455 | if (get_mi4header(sansa->sectorbuf,&mi4header) < 0) { | ||
456 | fprintf(stderr,"[ERR] Invalid mi4header\n"); | ||
457 | return -6; | ||
458 | } | ||
459 | |||
460 | /* Some sanity checks: | ||
461 | |||
462 | 1) Main MI4 image without RBBL and < 100000 bytes -> old install | ||
463 | 2) Main MI4 image with RBBL but no second image -> old install | ||
464 | */ | ||
465 | |||
466 | sansa->hasoldbootloader = 0; | ||
467 | if (memcmp(sansa->sectorbuf+0x1f8,"RBBL",4)==0) { | ||
468 | /* Look for an original firmware after the first image */ | ||
469 | if (sansa_seek_and_read(sansa, | ||
470 | sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, | ||
471 | sansa->sectorbuf, 512) < 0) { | ||
472 | return -7; | ||
473 | } | ||
474 | |||
475 | if (get_mi4header(sansa->sectorbuf,&mi4header)!=0) { | ||
476 | fprintf(stderr,"[ERR] No original firmware found\n"); | ||
477 | sansa->hasoldbootloader = 1; | ||
478 | } | ||
479 | } else if (mi4header.mi4size < 100000) { | ||
480 | fprintf(stderr,"[ERR] Old bootloader found\n"); | ||
481 | sansa->hasoldbootloader = 1; | ||
482 | } | ||
483 | |||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | int sansa_scan(struct sansa_t* sansa) | ||
488 | { | ||
489 | int i; | ||
490 | int n = 0; | ||
491 | char last_disk[4096]; | ||
492 | int denied = 0; | ||
493 | int result; | ||
494 | |||
495 | printf("[INFO] Scanning disk devices...\n"); | ||
496 | |||
497 | for (i = 0; i <= 25 ; i++) { | ||
498 | #ifdef __WIN32__ | ||
499 | sprintf(sansa->diskname,"\\\\.\\PhysicalDrive%d",i); | ||
500 | #elif defined(linux) || defined (__linux) | ||
501 | sprintf(sansa->diskname,"/dev/sd%c",'a'+i); | ||
502 | #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ | ||
503 | || defined(__bsdi__) || defined(__DragonFly__) | ||
504 | sprintf(sansa->diskname,"/dev/da%d",i); | ||
505 | #elif defined(__APPLE__) && defined(__MACH__) | ||
506 | sprintf(sansa->diskname,"/dev/disk%d",i); | ||
507 | #else | ||
508 | #error No disk paths defined for this platform | ||
509 | #endif | ||
510 | if ((result = sansa_open(sansa, 1)) < 0) { | ||
511 | if(result == -2) { | ||
512 | denied++; | ||
513 | } | ||
514 | sansa_close(sansa); | ||
515 | continue; | ||
516 | } | ||
517 | |||
518 | if (sansa_read_partinfo(sansa,1) < 0) { | ||
519 | sansa_close(sansa); | ||
520 | continue; | ||
521 | } | ||
522 | |||
523 | if (is_sansa(sansa) < 0) { | ||
524 | sansa_close(sansa); | ||
525 | continue; | ||
526 | } | ||
527 | |||
528 | #ifdef __WIN32__ | ||
529 | printf("[INFO] %s found - disk device %d\n",sansa->targetname, i); | ||
530 | #else | ||
531 | printf("[INFO] %s found - %s\n",sansa->targetname, sansa->diskname); | ||
532 | #endif | ||
533 | n++; | ||
534 | strcpy(last_disk,sansa->diskname); | ||
535 | sansa_close(sansa); | ||
536 | } | ||
537 | |||
538 | if (n==1) { | ||
539 | /* Remember the disk name */ | ||
540 | strcpy(sansa->diskname,last_disk); | ||
541 | } | ||
542 | else if (n == 0 && denied) { | ||
543 | printf("[ERR] FATAL: Permission denied on %d device(s) and no sansa detected.\n", denied); | ||
544 | #ifdef __WIN32__ | ||
545 | printf("[ERR] You need to run this program with administrator priviledges!\n"); | ||
546 | #else | ||
547 | printf("[ERR] You need permissions for raw disc access for this program to work!\n"); | ||
548 | #endif | ||
549 | } | ||
550 | |||
551 | return (n == 0 && denied) ? -1 : n; | ||
552 | } | ||
553 | |||
554 | /* Prepare original firmware for writing to the firmware partition by decrypting | ||
555 | and updating the header */ | ||
556 | static int prepare_original_firmware(struct sansa_t* sansa, unsigned char* buf, struct mi4header_t* mi4header) | ||
557 | { | ||
558 | unsigned char* tmpbuf; | ||
559 | int i; | ||
560 | int key_found; | ||
561 | |||
562 | get_mi4header(buf,mi4header); | ||
563 | |||
564 | #if 0 | ||
565 | printf("mi4header->version =0x%08x\n",mi4header->version); | ||
566 | printf("mi4header->length =0x%08x\n",mi4header->length); | ||
567 | printf("mi4header->crc32 =0x%08x\n",mi4header->crc32); | ||
568 | printf("mi4header->enctype =0x%08x\n",mi4header->enctype); | ||
569 | printf("mi4header->mi4size =0x%08x\n",mi4header->mi4size); | ||
570 | printf("mi4header->plaintext =0x%08x\n",mi4header->plaintext); | ||
571 | #endif | ||
572 | |||
573 | /* Decrypt anything that needs decrypting. */ | ||
574 | if (mi4header->plaintext < mi4header->mi4size - 0x200) { | ||
575 | /* TODO: Check different keys */ | ||
576 | tmpbuf=malloc(mi4header->mi4size-(mi4header->plaintext+0x200)); | ||
577 | if (tmpbuf==NULL) { | ||
578 | fprintf(stderr,"[ERR] Can not allocate memory\n"); | ||
579 | return -1; | ||
580 | } | ||
581 | |||
582 | key_found=0; | ||
583 | for (i=0; i < NUM_KEYS && !key_found ; i++) { | ||
584 | tea_decrypt_buf(buf+(mi4header->plaintext+0x200), | ||
585 | tmpbuf, | ||
586 | mi4header->mi4size-(mi4header->plaintext+0x200), | ||
587 | keys[i]); | ||
588 | key_found = (le2uint(tmpbuf+mi4header->length-mi4header->plaintext-4) == 0xaa55aa55); | ||
589 | } | ||
590 | |||
591 | if (key_found) { | ||
592 | memcpy(buf+(mi4header->plaintext+0x200),tmpbuf,mi4header->mi4size-(mi4header->plaintext+0x200)); | ||
593 | free(tmpbuf); | ||
594 | } else { | ||
595 | fprintf(stderr,"[ERR] Failed to decrypt image, aborting\n"); | ||
596 | free(tmpbuf); | ||
597 | return -1; | ||
598 | } | ||
599 | } | ||
600 | |||
601 | /* Increase plaintext value to full file */ | ||
602 | mi4header->plaintext = mi4header->mi4size - 0x200; | ||
603 | |||
604 | /* Update CRC checksum */ | ||
605 | chksum_crc32gentab (); | ||
606 | mi4header->crc32 = chksum_crc32(buf+0x200,mi4header->mi4size-0x200); | ||
607 | |||
608 | set_mi4header(buf,mi4header); | ||
609 | |||
610 | /* Add Rockbox-specific header */ | ||
611 | memcpy(buf+0x1f8,"RBOF",4); | ||
612 | memcpy(buf+0x1fc,sansa->targetname,4); | ||
613 | |||
614 | return 0; | ||
615 | } | ||
616 | |||
617 | static int load_original_firmware(struct sansa_t* sansa, unsigned char* buf, struct mi4header_t* mi4header) | ||
618 | { | ||
619 | int ppmi_length; | ||
620 | int n; | ||
621 | |||
622 | /* Read 512 bytes from PPMI_OFFSET - the PPMI header plus the mi4 header */ | ||
623 | if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET, buf, 512) < 0) { | ||
624 | return -1; | ||
625 | } | ||
626 | |||
627 | /* No need to check PPMI magic - it's done during init to confirm | ||
628 | this is an E200 */ | ||
629 | ppmi_length = le2int(buf+4); | ||
630 | |||
631 | /* Firstly look for an original firmware after the first image */ | ||
632 | if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, buf, 512) < 0) { | ||
633 | return -1; | ||
634 | } | ||
635 | |||
636 | if (get_mi4header(buf,mi4header)==0) { | ||
637 | /* We have a valid MI4 file after a bootloader, so we use this. */ | ||
638 | if ((n = sansa_seek_and_read(sansa, | ||
639 | sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, | ||
640 | buf, mi4header->mi4size)) < 0) { | ||
641 | return -1; | ||
642 | } | ||
643 | } else { | ||
644 | /* No valid MI4 file, so read the first image. */ | ||
645 | if ((n = sansa_seek_and_read(sansa, | ||
646 | sansa->start + PPMI_OFFSET + 0x200, | ||
647 | buf, ppmi_length)) < 0) { | ||
648 | return -1; | ||
649 | } | ||
650 | } | ||
651 | return prepare_original_firmware(sansa, buf, mi4header); | ||
652 | } | ||
653 | |||
654 | int sansa_read_firmware(struct sansa_t* sansa, const char* filename) | ||
655 | { | ||
656 | int res; | ||
657 | int outfile; | ||
658 | struct mi4header_t mi4header; | ||
659 | |||
660 | res = load_original_firmware(sansa,sansa->sectorbuf,&mi4header); | ||
661 | if (res < 0) | ||
662 | return res; | ||
663 | |||
664 | outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666); | ||
665 | if (outfile < 0) { | ||
666 | fprintf(stderr,"[ERR] Couldn't open file %s\n",filename); | ||
667 | return -1; | ||
668 | } | ||
669 | |||
670 | res = write(outfile,sansa->sectorbuf,mi4header.mi4size); | ||
671 | if (res != (int)mi4header.mi4size) { | ||
672 | fprintf(stderr,"[ERR] Write error - %d\n", res); | ||
673 | return -1; | ||
674 | } | ||
675 | close(outfile); | ||
676 | |||
677 | return 0; | ||
678 | } | ||
679 | |||
680 | unsigned int sansa_read_bootloader(struct sansa_t* sansa, const char* filename, unsigned char** bl_buffer) | ||
681 | { | ||
682 | /* Step 1 - read bootloader into RAM. */ | ||
683 | int infile; | ||
684 | unsigned int n; | ||
685 | unsigned int len; | ||
686 | infile=open(filename,O_RDONLY|O_BINARY); | ||
687 | if (infile < 0) { | ||
688 | fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); | ||
689 | return 0; | ||
690 | } | ||
691 | |||
692 | len = filesize(infile); | ||
693 | |||
694 | unsigned char* b = malloc(len); | ||
695 | if (b == NULL) { | ||
696 | fprintf(stderr,"[ERR] Could not allocate memory for bootloader\n"); | ||
697 | close(infile); | ||
698 | return 0; | ||
699 | } | ||
700 | |||
701 | n = read(infile,b,len); | ||
702 | close(infile); | ||
703 | if (n < len) { | ||
704 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n" | ||
705 | ,len,n); | ||
706 | return 0; | ||
707 | } | ||
708 | |||
709 | if (memcmp(b+0x1f8,"RBBL",4)!=0) { | ||
710 | fprintf(stderr,"[ERR] %s is not a Rockbox bootloader, aborting.\n", | ||
711 | filename); | ||
712 | return 0; | ||
713 | } | ||
714 | if (memcmp(b+0x1fc,sansa->targetname,4)!=0) { | ||
715 | fprintf(stderr,"[ERR] %s is not a Rockbox bootloader for %s, aborting.\n", | ||
716 | filename, sansa->targetname); | ||
717 | return 0; | ||
718 | } | ||
719 | *bl_buffer = b; | ||
720 | return len; | ||
721 | } | ||
722 | |||
723 | int sansa_add_bootloader(struct sansa_t* sansa, const unsigned char* bootloader, const unsigned int bl_length) | ||
724 | { | ||
725 | int res; | ||
726 | struct mi4header_t mi4header; | ||
727 | int length; | ||
728 | int n; | ||
729 | |||
730 | /* Create PPMI header */ | ||
731 | memset(sansa->sectorbuf,0,0x200); | ||
732 | memcpy(sansa->sectorbuf,"PPMI",4); | ||
733 | int2le(bl_length, sansa->sectorbuf+4); | ||
734 | int2le(0x00020000, sansa->sectorbuf+8); | ||
735 | |||
736 | /* copy bootloader to sansa->sectorbuf+0x200 */ | ||
737 | memcpy(sansa->sectorbuf+0x200,bootloader,bl_length); | ||
738 | |||
739 | /* Load original firmware from Sansa to the space after the bootloader */ | ||
740 | res = load_original_firmware(sansa,sansa->sectorbuf+0x200+bl_length,&mi4header); | ||
741 | if (res < 0) | ||
742 | return res; | ||
743 | |||
744 | /* Now write the whole thing back to the Sansa */ | ||
745 | |||
746 | if (sansa_seek(sansa, sansa->start+PPMI_OFFSET) < 0) { | ||
747 | fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in add_bootloader failed.\n", | ||
748 | sansa->start+PPMI_OFFSET); | ||
749 | return -5; | ||
750 | } | ||
751 | |||
752 | length = 0x200 + bl_length + mi4header.mi4size; | ||
753 | |||
754 | n=sansa_write(sansa, length); | ||
755 | if (n < length) { | ||
756 | fprintf(stderr,"[ERR] Short write in add_bootloader\n"); | ||
757 | return -6; | ||
758 | } | ||
759 | |||
760 | return 0; | ||
761 | } | ||
762 | |||
763 | int sansa_delete_bootloader(struct sansa_t* sansa) | ||
764 | { | ||
765 | int res; | ||
766 | struct mi4header_t mi4header; | ||
767 | int n; | ||
768 | int length; | ||
769 | |||
770 | /* Load original firmware from Sansa to sansa->sectorbuf+0x200 */ | ||
771 | res = load_original_firmware(sansa,sansa->sectorbuf+0x200,&mi4header); | ||
772 | if (res < 0) | ||
773 | return res; | ||
774 | |||
775 | /* Create PPMI header */ | ||
776 | memset(sansa->sectorbuf,0,0x200); | ||
777 | memcpy(sansa->sectorbuf,"PPMI",4); | ||
778 | int2le(mi4header.mi4size, sansa->sectorbuf+4); | ||
779 | int2le(0x00020000, sansa->sectorbuf+8); | ||
780 | |||
781 | /* Now write the whole thing back to the Sansa */ | ||
782 | |||
783 | if (sansa_seek(sansa, sansa->start+PPMI_OFFSET) < 0) { | ||
784 | fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in add_bootloader failed.\n", | ||
785 | sansa->start+PPMI_OFFSET); | ||
786 | return -5; | ||
787 | } | ||
788 | |||
789 | length = 0x200 + mi4header.mi4size; | ||
790 | |||
791 | n=sansa_write(sansa, length); | ||
792 | if (n < length) { | ||
793 | fprintf(stderr,"[ERR] Short write in delete_bootloader\n"); | ||
794 | return -6; | ||
795 | } | ||
796 | |||
797 | return 0; | ||
798 | } | ||
799 | |||
800 | /** List number of MI4 images on the player, return number. | ||
801 | */ | ||
802 | int sansa_list_images(struct sansa_t* sansa) | ||
803 | { | ||
804 | struct mi4header_t mi4header; | ||
805 | loff_t ppmi_length; | ||
806 | int num = 0; | ||
807 | |||
808 | /* Check Main firmware header */ | ||
809 | if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET, sansa->sectorbuf, 0x200) < 0) { | ||
810 | return 0; | ||
811 | } | ||
812 | |||
813 | ppmi_length = le2int(sansa->sectorbuf+4); | ||
814 | |||
815 | printf("[INFO] Image 1 - %"PRIu64" bytes\n",ppmi_length); | ||
816 | num = 1; | ||
817 | |||
818 | /* Look for an original firmware after the first image */ | ||
819 | if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, sansa->sectorbuf, 512) < 0) { | ||
820 | return 0; | ||
821 | } | ||
822 | |||
823 | if (get_mi4header(sansa->sectorbuf,&mi4header)==0) { | ||
824 | printf("[INFO] Image 2 - %d bytes\n",mi4header.mi4size); | ||
825 | num = 2; | ||
826 | } | ||
827 | return num; | ||
828 | } | ||
829 | |||
830 | int sansa_update_of(struct sansa_t* sansa, const char* filename) | ||
831 | { | ||
832 | int n; | ||
833 | int infile = -1; /* Prevent an erroneous "may be used uninitialised" gcc warning */ | ||
834 | int of_length = 0; /* Keep gcc happy when building for rbutil */ | ||
835 | int ppmi_length; | ||
836 | struct mi4header_t mi4header; | ||
837 | unsigned char buf[512]; | ||
838 | |||
839 | /* Step 1 - check we have an OF on the Sansa to upgrade. We expect the | ||
840 | Rockbox bootloader to be installed and the OF to be after it on disk. */ | ||
841 | |||
842 | /* Read 512 bytes from PPMI_OFFSET - the PPMI header */ | ||
843 | if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET, | ||
844 | buf, 512) < 0) { | ||
845 | return -1; | ||
846 | } | ||
847 | |||
848 | /* No need to check PPMI magic - it's done during init to confirm | ||
849 | this is an E200 */ | ||
850 | ppmi_length = le2int(buf+4); | ||
851 | |||
852 | /* Look for an original firmware after the first image */ | ||
853 | if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET+0x200+ppmi_length, | ||
854 | buf, 512) < 0) { | ||
855 | return -1; | ||
856 | } | ||
857 | |||
858 | if (get_mi4header(buf,&mi4header)!=0) { | ||
859 | /* We don't have a valid MI4 file after a bootloader, so do nothing. */ | ||
860 | fprintf(stderr,"[ERR] No original firmware found at 0x%08"PRIx64"\n", | ||
861 | sansa->start+PPMI_OFFSET+0x200+ppmi_length); | ||
862 | return -1; | ||
863 | } | ||
864 | |||
865 | /* Step 2 - read OF into RAM. */ | ||
866 | infile=open(filename,O_RDONLY|O_BINARY); | ||
867 | if (infile < 0) { | ||
868 | fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); | ||
869 | return -1; | ||
870 | } | ||
871 | |||
872 | of_length = filesize(infile); | ||
873 | |||
874 | /* Load original firmware from file */ | ||
875 | memset(sansa->sectorbuf,0,0x200); | ||
876 | n = read(infile,sansa->sectorbuf,of_length); | ||
877 | close(infile); | ||
878 | if (n < of_length) { | ||
879 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n" | ||
880 | , of_length, n); | ||
881 | return -1; | ||
882 | } | ||
883 | |||
884 | /* Check we have a valid MI4 file. */ | ||
885 | if (get_mi4header(sansa->sectorbuf,&mi4header)!=0) { | ||
886 | fprintf(stderr,"[ERR] %s is not a valid mi4 file\n",filename); | ||
887 | return -1; | ||
888 | } | ||
889 | |||
890 | /* Decrypt and build the header */ | ||
891 | if(prepare_original_firmware(sansa, sansa->sectorbuf, &mi4header)!=0){ | ||
892 | fprintf(stderr,"[ERR] Unable to build decrypted mi4 from %s\n" | ||
893 | ,filename); | ||
894 | return -1; | ||
895 | } | ||
896 | |||
897 | /* Step 3 - write the OF to the Sansa */ | ||
898 | if (sansa_seek(sansa, sansa->start+PPMI_OFFSET+0x200+ppmi_length) < 0) { | ||
899 | fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in sansa_update_of failed.\n", | ||
900 | sansa->start+PPMI_OFFSET+0x200+ppmi_length); | ||
901 | return -1; | ||
902 | } | ||
903 | |||
904 | n=sansa_write(sansa, of_length); | ||
905 | if (n < of_length) { | ||
906 | fprintf(stderr,"[ERR] Short write in sansa_update_of\n"); | ||
907 | return -1; | ||
908 | } | ||
909 | |||
910 | /* Step 4 - zero out the nvparams section - we have to do this or we end up | ||
911 | with multiple copies of the nvparams data and don't know which one to | ||
912 | work with for the database rebuild disabling trick in our bootloader */ | ||
913 | if (strcmp(sansa->targetname,"e200") == 0) { | ||
914 | printf("[INFO] Resetting Original Firmware settings\n"); | ||
915 | if (sansa_seek(sansa, sansa->start+NVPARAMS_OFFSET+0x200) < 0) { | ||
916 | fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in sansa_update_of failed.\n", | ||
917 | sansa->start+NVPARAMS_OFFSET+0x200); | ||
918 | return -1; | ||
919 | } | ||
920 | |||
921 | memset(sansa->sectorbuf,0,NVPARAMS_SIZE); | ||
922 | n=sansa_write(sansa, NVPARAMS_SIZE); | ||
923 | if (n < NVPARAMS_SIZE) { | ||
924 | fprintf(stderr,"[ERR] Short write in sansa_update_of\n"); | ||
925 | return -1; | ||
926 | } | ||
927 | } | ||
928 | |||
929 | return 0; | ||
930 | } | ||
931 | |||
932 | /* Update the PPBL (bootloader) image in the hidden firmware partition */ | ||
933 | int sansa_update_ppbl(struct sansa_t* sansa, const char* filename) | ||
934 | { | ||
935 | int n; | ||
936 | int infile = -1; /* Prevent an erroneous "may be used uninitialised" gcc warning */ | ||
937 | int ppbl_length = 0; /* Keep gcc happy when building for rbutil */ | ||
938 | |||
939 | /* Step 1 - read bootloader into RAM. */ | ||
940 | infile=open(filename,O_RDONLY|O_BINARY); | ||
941 | if (infile < 0) { | ||
942 | fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); | ||
943 | return -1; | ||
944 | } | ||
945 | |||
946 | ppbl_length = filesize(infile); | ||
947 | |||
948 | n = read(infile,sansa->sectorbuf+0x200,ppbl_length); | ||
949 | close(infile); | ||
950 | if (n < ppbl_length) { | ||
951 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", ppbl_length, n); | ||
952 | return -1; | ||
953 | } | ||
954 | |||
955 | /* Step 2 - Build the header */ | ||
956 | memset(sansa->sectorbuf,0,0x200); | ||
957 | memcpy(sansa->sectorbuf,"PPBL",4); | ||
958 | int2le(ppbl_length, sansa->sectorbuf+4); | ||
959 | int2le(0x00010000, sansa->sectorbuf+8); | ||
960 | |||
961 | /* Step 3 - write the bootloader to the Sansa */ | ||
962 | if (sansa_seek(sansa, sansa->start) < 0) { | ||
963 | fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in sansa_update_ppbl failed.\n", sansa->start); | ||
964 | return -1; | ||
965 | } | ||
966 | |||
967 | n=sansa_write(sansa, ppbl_length + 0x200); | ||
968 | if (n < (ppbl_length+0x200)) { | ||
969 | fprintf(stderr,"[ERR] Short write in sansa_update_ppbl\n"); | ||
970 | return -1; | ||
971 | } | ||
972 | |||
973 | return 0; | ||
974 | } | ||
975 | |||
diff --git a/utils/sansapatcher/sansapatcher.h b/utils/sansapatcher/sansapatcher.h new file mode 100644 index 0000000000..259143e38a --- /dev/null +++ b/utils/sansapatcher/sansapatcher.h | |||
@@ -0,0 +1,67 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006-2007 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef _SANSAPATCHER_H | ||
23 | #define _SANSAPATCHER_H | ||
24 | |||
25 | #ifdef __cplusplus | ||
26 | extern "C" { | ||
27 | #endif | ||
28 | |||
29 | #include "sansaio.h" | ||
30 | |||
31 | /* exit codes */ | ||
32 | #define SANSA_OK 0 | ||
33 | #define SANSA_WRONG_ARGUMENTS 1 | ||
34 | #define SANSA_OPEN_INFILE_FAILED 2 | ||
35 | #define SANSA_PARTITION_ERROR 3 | ||
36 | #define SANSA_CANNOT_REOPEN 5 | ||
37 | #define SANSA_INSTALL_FAILED 6 | ||
38 | #define SANSA_UNINSTALL_FAILED 7 | ||
39 | #define SANSA_ACCESS_DENIED 10 | ||
40 | #define SANSA_NOT_FOUND 11 | ||
41 | #define SANSA_WRONG_DEVICE_COUNT 12 | ||
42 | #define SANSA_MULTIPLE_DEVICES 15 | ||
43 | #define SANSA_WRONG_TYPE 16 | ||
44 | #define SANSA_OLD_INSTALL 17 | ||
45 | #define SANSA_INTERNAL_ERROR 20 | ||
46 | |||
47 | extern int sansa_verbose; | ||
48 | /* Size of buffer for disk I/O - 8MB is large enough for any version | ||
49 | of the Apple firmware, but not the Nano's RSRC image. */ | ||
50 | #define BUFFER_SIZE 8*1024*1024 | ||
51 | |||
52 | int sansa_read_partinfo(struct sansa_t* sansa, int silent); | ||
53 | int is_sansa(struct sansa_t* sansa); | ||
54 | int sansa_scan(struct sansa_t* sansa); | ||
55 | int sansa_read_firmware(struct sansa_t* sansa, const char* filename); | ||
56 | unsigned int sansa_read_bootloader(struct sansa_t* sansa, const char* filename, unsigned char** bl_buffer); | ||
57 | int sansa_add_bootloader(struct sansa_t* sansa, const unsigned char* buf, unsigned int len); | ||
58 | int sansa_delete_bootloader(struct sansa_t* sansa); | ||
59 | int sansa_update_of(struct sansa_t* sansa,const char* filename); | ||
60 | int sansa_update_ppbl(struct sansa_t* sansa,const char* filename); | ||
61 | int sansa_list_images(struct sansa_t* sansa); | ||
62 | |||
63 | #ifdef __cplusplus | ||
64 | } | ||
65 | #endif | ||
66 | #endif | ||
67 | |||
diff --git a/utils/sansapatcher/sansapatcher.manifest b/utils/sansapatcher/sansapatcher.manifest new file mode 100644 index 0000000000..71bb153688 --- /dev/null +++ b/utils/sansapatcher/sansapatcher.manifest | |||
@@ -0,0 +1,13 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> | ||
2 | <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> | ||
3 | <assemblyIdentity version="1.0.0.0" processorArchitecture="X86" name="sansapatcher.exe" type="win32"/> | ||
4 | |||
5 | <!-- Identify the application security requirements. --> | ||
6 | <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> | ||
7 | <security> | ||
8 | <requestedPrivileges> | ||
9 | <requestedExecutionLevel level="requireAdministrator"/> | ||
10 | </requestedPrivileges> | ||
11 | </security> | ||
12 | </trustInfo> | ||
13 | </assembly> | ||
diff --git a/utils/sansapatcher/sansapatcher.pro b/utils/sansapatcher/sansapatcher.pro new file mode 100644 index 0000000000..f8308e6283 --- /dev/null +++ b/utils/sansapatcher/sansapatcher.pro | |||
@@ -0,0 +1,38 @@ | |||
1 | # | ||
2 | # __________ __ ___. | ||
3 | # Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | # \/ \/ \/ \/ \/ | ||
8 | # | ||
9 | # All files in this archive are subject to the GNU General Public License. | ||
10 | # See the file COPYING in the source tree root for full license agreement. | ||
11 | # | ||
12 | # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
13 | # KIND, either express or implied. | ||
14 | # | ||
15 | |||
16 | TEMPLATE = app | ||
17 | TARGET = sansapatcher | ||
18 | QT -= core | ||
19 | |||
20 | SOURCES += \ | ||
21 | sansaio-posix.c \ | ||
22 | sansaio-win32.c \ | ||
23 | sansapatcher.c \ | ||
24 | main.c | ||
25 | |||
26 | HEADERS += \ | ||
27 | parttypes.h \ | ||
28 | sansaio.h \ | ||
29 | sansapatcher.h \ | ||
30 | |||
31 | RC_FILE = sansapatcher.rc | ||
32 | |||
33 | DEFINES += _LARGEFILE64_SOURCE | ||
34 | |||
35 | unix { | ||
36 | target.path = /usr/local/bin | ||
37 | INSTALLS += target | ||
38 | } | ||
diff --git a/utils/sansapatcher/sansapatcher.rc b/utils/sansapatcher/sansapatcher.rc new file mode 100644 index 0000000000..26bb8fe730 --- /dev/null +++ b/utils/sansapatcher/sansapatcher.rc | |||
@@ -0,0 +1 @@ | |||
1 24 MOVEABLE PURE "sansapatcher.manifest" | |||