diff options
author | Rafaël Carré <rafael.carre@gmail.com> | 2009-05-28 18:27:08 +0000 |
---|---|---|
committer | Rafaël Carré <rafael.carre@gmail.com> | 2009-05-28 18:27:08 +0000 |
commit | 96165abec2da60c466659fa0e68e06d97587d51a (patch) | |
tree | 5445bddea2ac0e706546ce972d61dacc8d0f81e2 /rbutil/mkamsboot/mkamsboot.c | |
parent | bebc8587cfa7896684278d824ed5b28b2e9f9df1 (diff) | |
download | rockbox-96165abec2da60c466659fa0e68e06d97587d51a.tar.gz rockbox-96165abec2da60c466659fa0e68e06d97587d51a.zip |
FS#10253 : mkamsboot v1.0mkamsboot_1.0
- Bump version to 1.0
- Add Clipv2 target
- Make mkamsboot work as a library (work by domonoky : FS#10185, with a few modifications by me)
. Use a macro with variadic arguments for error cases in functions which might error.
. Add detailed descriptions to functions exported by the library (in the header file)
- modify bin2c.c to produce only one pair of .c/.h files with several files embedded in it
- move files needing to be built by an ARM cross compiler into dualboot/
- commit produced .c/.h files (containing nrv2e_d8.S and dualboot.S built for Clip, Fuze, e200v2, c200v2, m200v4, Clipv2)
- Write a real README file
- cosmetics: indent dualboot.S properly, remove trailing spaces, limit lines to 80 characters
- comments: add/correct comments in dualboot.S and mkamsboot.c
- move back extract_fw.c to utils/AMS/hacking
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@21118 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'rbutil/mkamsboot/mkamsboot.c')
-rw-r--r-- | rbutil/mkamsboot/mkamsboot.c | 598 |
1 files changed, 305 insertions, 293 deletions
diff --git a/rbutil/mkamsboot/mkamsboot.c b/rbutil/mkamsboot/mkamsboot.c index 65b88af994..4210d3bd7c 100644 --- a/rbutil/mkamsboot/mkamsboot.c +++ b/rbutil/mkamsboot/mkamsboot.c | |||
@@ -24,7 +24,17 @@ | |||
24 | 24 | ||
25 | /* | 25 | /* |
26 | 26 | ||
27 | Insert a Rockbox bootloader into an AMS original firmware file. | 27 | Insert a Rockbox bootloader into a Sansa AMS original firmware file. |
28 | |||
29 | Layout of a Sansa AMS original firmware file: | ||
30 | |||
31 | ---------------------- 0x0 | ||
32 | | HEADER | | ||
33 | |----------------------| 0x400 | ||
34 | | FIRMWARE BLOCK | | ||
35 | |----------------------| 0x400 + firmware block size | ||
36 | | LIBRARIES/DATA | | ||
37 | ---------------------- END | ||
28 | 38 | ||
29 | We replace the main firmware block (bytes 0x400..0x400+firmware_size) | 39 | We replace the main firmware block (bytes 0x400..0x400+firmware_size) |
30 | as follows: | 40 | as follows: |
@@ -50,9 +60,12 @@ as follows: | |||
50 | 60 | ||
51 | This entire block fits into the space previously occupied by the main | 61 | This entire block fits into the space previously occupied by the main |
52 | firmware block - the space saved by compressing the OF image is used | 62 | firmware block - the space saved by compressing the OF image is used |
53 | to store the compressed version of the Rockbox bootloader. The OF | 63 | to store the compressed version of the Rockbox bootloader. |
54 | image is typically about 120KB, which allows us to store a Rockbox | 64 | |
55 | bootloader with an uncompressed size of about 60KB-70KB. | 65 | On version 1 firmwares, the OF image is typically about 120KB, which allows |
66 | us to store a Rockbox bootloader with an uncompressed size of about 60KB-70KB. | ||
67 | Version 2 firmwares are bigger and are stored in SDRAM (instead of IRAM). | ||
68 | In both cases, the RAM we are using is mapped at offset 0x0. | ||
56 | 69 | ||
57 | mkamsboot then corrects the checksums and writes a new legal firmware | 70 | mkamsboot then corrects the checksums and writes a new legal firmware |
58 | file which can be installed on the device. | 71 | file which can be installed on the device. |
@@ -65,7 +78,7 @@ end of RAM. | |||
65 | 78 | ||
66 | Then, depending on the detection of the dual-boot keypress, either the | 79 | Then, depending on the detection of the dual-boot keypress, either the |
67 | OF image or the Rockbox image is copied to the end of RAM (just before | 80 | OF image or the Rockbox image is copied to the end of RAM (just before |
68 | the ucl unpack function) and uncompress it to the start of RAM. | 81 | the ucl unpack function) and uncompressed to the start of RAM. |
69 | 82 | ||
70 | Finally, the ucl unpack function branches to address 0x0, passing | 83 | Finally, the ucl unpack function branches to address 0x0, passing |
71 | execution to the uncompressed firmware. | 84 | execution to the uncompressed firmware. |
@@ -73,7 +86,6 @@ execution to the uncompressed firmware. | |||
73 | 86 | ||
74 | */ | 87 | */ |
75 | 88 | ||
76 | |||
77 | #include <stdio.h> | 89 | #include <stdio.h> |
78 | #include <stdlib.h> | 90 | #include <stdlib.h> |
79 | #include <stdint.h> | 91 | #include <stdint.h> |
@@ -85,27 +97,22 @@ execution to the uncompressed firmware. | |||
85 | 97 | ||
86 | #include <ucl/ucl.h> | 98 | #include <ucl/ucl.h> |
87 | 99 | ||
88 | /* Headers for ARM code binaries */ | 100 | #include "mkamsboot.h" |
89 | #include "nrv2e_d8.h" | 101 | |
90 | #include "md5.h" | 102 | #include "md5.h" |
91 | 103 | ||
92 | #include "dualboot_clip.h" | 104 | /* Header for ARM code binaries */ |
93 | #include "dualboot_e200v2.h" | 105 | #include "dualboot.h" |
94 | #include "dualboot_fuze.h" | ||
95 | #include "dualboot_m200v4.h" | ||
96 | #include "dualboot_c200v2.h" | ||
97 | 106 | ||
98 | /* Win32 compatibility */ | 107 | /* Win32 compatibility */ |
99 | #ifndef O_BINARY | 108 | #ifndef O_BINARY |
100 | #define O_BINARY 0 | 109 | #define O_BINARY 0 |
101 | #endif | 110 | #endif |
102 | 111 | ||
103 | #ifndef VERSION | 112 | #define VERSION "1.0" |
104 | #define VERSION "0.1" | ||
105 | #endif | ||
106 | 113 | ||
107 | enum | 114 | /* Supported models */ |
108 | { | 115 | enum { |
109 | MODEL_UNKNOWN = -1, | 116 | MODEL_UNKNOWN = -1, |
110 | MODEL_FUZE = 0, | 117 | MODEL_FUZE = 0, |
111 | MODEL_CLIP, | 118 | MODEL_CLIP, |
@@ -115,31 +122,31 @@ enum | |||
115 | MODEL_C200V2, | 122 | MODEL_C200V2, |
116 | }; | 123 | }; |
117 | 124 | ||
118 | static const char* model_names[] = | 125 | /* Descriptive name of these models */ |
119 | { | 126 | static const char* model_names[] = { |
120 | "Fuze", | 127 | "Fuze", |
121 | "Clip", | 128 | "Clip", |
122 | "Clip V2", | 129 | "Clip v2", |
123 | "e200 v2", | 130 | "e200 v2", |
124 | "m200 v4", | 131 | "m200 v4", |
125 | "c200 v2" | 132 | "c200 v2" |
126 | }; | 133 | }; |
127 | 134 | ||
128 | static const unsigned char* bootloaders[] = | 135 | /* Dualboot functions for these models */ |
129 | { | 136 | static const unsigned char* bootloaders[] = { |
130 | dualboot_fuze, | 137 | dualboot_fuze, |
131 | dualboot_clip, | 138 | dualboot_clip, |
132 | NULL, | 139 | dualboot_clipv2, |
133 | dualboot_e200v2, | 140 | dualboot_e200v2, |
134 | dualboot_m200v4, | 141 | dualboot_m200v4, |
135 | dualboot_c200v2, | 142 | dualboot_c200v2, |
136 | }; | 143 | }; |
137 | 144 | ||
138 | static const int bootloader_sizes[] = | 145 | /* Size of dualboot functions for these models */ |
139 | { | 146 | static const int bootloader_sizes[] = { |
140 | sizeof(dualboot_fuze), | 147 | sizeof(dualboot_fuze), |
141 | sizeof(dualboot_clip), | 148 | sizeof(dualboot_clip), |
142 | 0, | 149 | sizeof(dualboot_clipv2), |
143 | sizeof(dualboot_e200v2), | 150 | sizeof(dualboot_e200v2), |
144 | sizeof(dualboot_m200v4), | 151 | sizeof(dualboot_m200v4), |
145 | sizeof(dualboot_c200v2), | 152 | sizeof(dualboot_c200v2), |
@@ -147,11 +154,10 @@ static const int bootloader_sizes[] = | |||
147 | 154 | ||
148 | /* Model names used in the Rockbox header in ".sansa" files - these match the | 155 | /* Model names used in the Rockbox header in ".sansa" files - these match the |
149 | -add parameter to the "scramble" tool */ | 156 | -add parameter to the "scramble" tool */ |
150 | static const char* rb_model_names[] = | 157 | static const char* rb_model_names[] = { |
151 | { | ||
152 | "fuze", | 158 | "fuze", |
153 | "clip", | 159 | "clip", |
154 | NULL, | 160 | "clv2", |
155 | "e2v2", | 161 | "e2v2", |
156 | "m2v4", | 162 | "m2v4", |
157 | "c2v2", | 163 | "c2v2", |
@@ -159,11 +165,10 @@ static const char* rb_model_names[] = | |||
159 | 165 | ||
160 | /* Model numbers used to initialise the checksum in the Rockbox header in | 166 | /* Model numbers used to initialise the checksum in the Rockbox header in |
161 | ".sansa" files - these are the same as MODEL_NUMBER in config-target.h */ | 167 | ".sansa" files - these are the same as MODEL_NUMBER in config-target.h */ |
162 | static const int rb_model_num[] = | 168 | static const int rb_model_num[] = { |
163 | { | ||
164 | 43, | 169 | 43, |
165 | 40, | 170 | 40, |
166 | 0, | 171 | 60, |
167 | 41, | 172 | 41, |
168 | 42, | 173 | 42, |
169 | 44 | 174 | 44 |
@@ -172,7 +177,7 @@ static const int rb_model_num[] = | |||
172 | struct md5sums { | 177 | struct md5sums { |
173 | int model; | 178 | int model; |
174 | char *version; | 179 | char *version; |
175 | int fw_version; | 180 | int fw_version; /* version 2 is used in Clipv2 and Fuzev2 firmwares */ |
176 | char *md5; | 181 | char *md5; |
177 | }; | 182 | }; |
178 | 183 | ||
@@ -181,27 +186,32 @@ struct md5sums { | |||
181 | static struct md5sums sansasums[] = { | 186 | static struct md5sums sansasums[] = { |
182 | /* NOTE: Different regional versions of the firmware normally only | 187 | /* NOTE: Different regional versions of the firmware normally only |
183 | differ in the filename - the md5sums are identical */ | 188 | differ in the filename - the md5sums are identical */ |
184 | { MODEL_E200V2, "3.01.11", 1, "e622ca8cb6df423f54b8b39628a1f0a3" }, | 189 | |
185 | { MODEL_E200V2, "3.01.14", 1, "2c1d0383fc3584b2cc83ba8cc2243af6" }, | 190 | /* model version fw_version md5 */ |
186 | { MODEL_E200V2, "3.01.16", 1, "12563ad71b25a1034cf2092d1e0218c4" }, | 191 | { MODEL_E200V2, "3.01.11", 1, "e622ca8cb6df423f54b8b39628a1f0a3" }, |
187 | 192 | { MODEL_E200V2, "3.01.14", 1, "2c1d0383fc3584b2cc83ba8cc2243af6" }, | |
188 | { MODEL_FUZE, "1.01.11", 1, "cac8ffa03c599330ac02c4d41de66166" }, | 193 | { MODEL_E200V2, "3.01.16", 1, "12563ad71b25a1034cf2092d1e0218c4" }, |
189 | { MODEL_FUZE, "1.01.15", 1, "df0e2c1612727f722c19a3c764cff7f2" }, | 194 | |
190 | { MODEL_FUZE, "1.01.22", 1, "5aff5486fe8dd64239cc71eac470af98" }, | 195 | { MODEL_FUZE, "1.01.11", 1, "cac8ffa03c599330ac02c4d41de66166" }, |
191 | { MODEL_FUZE, "1.02.26", 1, "7c632c479461c48c8833baed74eb5e4f" }, | 196 | { MODEL_FUZE, "1.01.15", 1, "df0e2c1612727f722c19a3c764cff7f2" }, |
192 | 197 | { MODEL_FUZE, "1.01.22", 1, "5aff5486fe8dd64239cc71eac470af98" }, | |
193 | { MODEL_C200V2, "3.02.05", 1, "b6378ebd720b0ade3fad4dc7ab61c1a5" }, | 198 | { MODEL_FUZE, "1.02.26", 1, "7c632c479461c48c8833baed74eb5e4f" }, |
194 | 199 | ||
195 | { MODEL_M200V4, "4.00.45", 1, "82e3194310d1514e3bbcd06e84c4add3" }, | 200 | { MODEL_C200V2, "3.02.05", 1, "b6378ebd720b0ade3fad4dc7ab61c1a5" }, |
196 | { MODEL_M200V4, "4.01.08-A", 1, "fc9dd6116001b3e6a150b898f1b091f0" }, | 201 | |
197 | { MODEL_M200V4, "4.01.08-E", 1, "d3fb7d8ec8624ee65bc99f8dab0e2369" }, | 202 | { MODEL_M200V4, "4.00.45", 1, "82e3194310d1514e3bbcd06e84c4add3" }, |
198 | 203 | { MODEL_M200V4, "4.01.08-A", 1, "fc9dd6116001b3e6a150b898f1b091f0" }, | |
199 | { MODEL_CLIP, "1.01.17", 1, "12caad785d506219d73f538772afd99e" }, | 204 | { MODEL_M200V4, "4.01.08-E", 1, "d3fb7d8ec8624ee65bc99f8dab0e2369" }, |
200 | { MODEL_CLIP, "1.01.18", 1, "d720b266bd5afa38a198986ef0508a45" }, | 205 | |
201 | { MODEL_CLIP, "1.01.20", 1, "236d8f75189f468462c03f6d292cf2ac" }, | 206 | { MODEL_CLIP, "1.01.17", 1, "12caad785d506219d73f538772afd99e" }, |
202 | { MODEL_CLIP, "1.01.29", 1, "c12711342169c66e209540cd1f27cd26" }, | 207 | { MODEL_CLIP, "1.01.18", 1, "d720b266bd5afa38a198986ef0508a45" }, |
203 | { MODEL_CLIP, "1.01.30", 1, "f2974d47c536549c9d8259170f1dbe4d" }, | 208 | { MODEL_CLIP, "1.01.20", 1, "236d8f75189f468462c03f6d292cf2ac" }, |
204 | { MODEL_CLIP, "1.01.32", 1, "d835d12342500732ffb9c4ee54abec15" }, | 209 | { MODEL_CLIP, "1.01.29", 1, "c12711342169c66e209540cd1f27cd26" }, |
210 | { MODEL_CLIP, "1.01.30", 1, "f2974d47c536549c9d8259170f1dbe4d" }, | ||
211 | { MODEL_CLIP, "1.01.32", 1, "d835d12342500732ffb9c4ee54abec15" }, | ||
212 | |||
213 | { MODEL_CLIPV2, "2.01.16", 2, "c57fb3fcbe07c2c9b360f060938f80cb" }, | ||
214 | { MODEL_CLIPV2, "2.01.32", 2, "0ad3723e52022509089d938d0fbbf8c5" } | ||
205 | }; | 215 | }; |
206 | 216 | ||
207 | #define NUM_MD5S (sizeof(sansasums)/sizeof(sansasums[0])) | 217 | #define NUM_MD5S (sizeof(sansasums)/sizeof(sansasums[0])) |
@@ -209,7 +219,7 @@ static struct md5sums sansasums[] = { | |||
209 | static off_t filesize(int fd) { | 219 | static off_t filesize(int fd) { |
210 | struct stat buf; | 220 | struct stat buf; |
211 | 221 | ||
212 | if (fstat(fd,&buf) < 0) { | 222 | if (fstat(fd, &buf) < 0) { |
213 | perror("[ERR] Checking filesize of input file"); | 223 | perror("[ERR] Checking filesize of input file"); |
214 | return -1; | 224 | return -1; |
215 | } else { | 225 | } else { |
@@ -217,30 +227,26 @@ static off_t filesize(int fd) { | |||
217 | } | 227 | } |
218 | } | 228 | } |
219 | 229 | ||
220 | static uint32_t get_uint32le(unsigned char* p) | 230 | static uint32_t get_uint32le(unsigned char* p) { |
221 | { | ||
222 | return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); | 231 | return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); |
223 | } | 232 | } |
224 | 233 | ||
225 | static uint32_t get_uint32be(unsigned char* p) | 234 | static uint32_t get_uint32be(unsigned char* p) { |
226 | { | ||
227 | return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; | 235 | return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; |
228 | } | 236 | } |
229 | 237 | ||
230 | static void put_uint32le(unsigned char* p, uint32_t x) | 238 | static void put_uint32le(unsigned char* p, uint32_t x) { |
231 | { | ||
232 | p[0] = x & 0xff; | 239 | p[0] = x & 0xff; |
233 | p[1] = (x >> 8) & 0xff; | 240 | p[1] = (x >> 8) & 0xff; |
234 | p[2] = (x >> 16) & 0xff; | 241 | p[2] = (x >> 16) & 0xff; |
235 | p[3] = (x >> 24) & 0xff; | 242 | p[3] = (x >> 24) & 0xff; |
236 | } | 243 | } |
237 | 244 | ||
238 | void calc_MD5(unsigned char* buf, int len, char *md5str) | 245 | void calc_MD5(unsigned char* buf, int len, char *md5str) { |
239 | { | ||
240 | int i; | 246 | int i; |
241 | md5_context ctx; | 247 | md5_context ctx; |
242 | unsigned char md5sum[16]; | 248 | unsigned char md5sum[16]; |
243 | 249 | ||
244 | md5_starts(&ctx); | 250 | md5_starts(&ctx); |
245 | md5_update(&ctx, buf, len); | 251 | md5_update(&ctx, buf, len); |
246 | md5_finish(&ctx, md5sum); | 252 | md5_finish(&ctx, md5sum); |
@@ -249,9 +255,8 @@ void calc_MD5(unsigned char* buf, int len, char *md5str) | |||
249 | sprintf(md5str + 2*i, "%02x", md5sum[i]); | 255 | sprintf(md5str + 2*i, "%02x", md5sum[i]); |
250 | } | 256 | } |
251 | 257 | ||
252 | 258 | /* Calculate a simple checksum used in Sansa Original Firmwares */ | |
253 | static uint32_t calc_checksum(unsigned char* buf, uint32_t n) | 259 | static uint32_t calc_checksum(unsigned char* buf, uint32_t n) { |
254 | { | ||
255 | uint32_t sum = 0; | 260 | uint32_t sum = 0; |
256 | uint32_t i; | 261 | uint32_t i; |
257 | 262 | ||
@@ -261,10 +266,8 @@ static uint32_t calc_checksum(unsigned char* buf, uint32_t n) | |||
261 | return sum; | 266 | return sum; |
262 | } | 267 | } |
263 | 268 | ||
264 | static int get_model(int model_id) | 269 | static int get_model(int model_id) { |
265 | { | 270 | switch(model_id) { |
266 | switch(model_id) | ||
267 | { | ||
268 | case 0x1e: | 271 | case 0x1e: |
269 | return MODEL_FUZE; | 272 | return MODEL_FUZE; |
270 | case 0x22: | 273 | case 0x22: |
@@ -282,9 +285,8 @@ static int get_model(int model_id) | |||
282 | return MODEL_UNKNOWN; | 285 | return MODEL_UNKNOWN; |
283 | } | 286 | } |
284 | 287 | ||
285 | 288 | /* Compress using nrv2e algorithm : Thumb decompressor fits in 168 bytes ! */ | |
286 | static unsigned char* uclpack(unsigned char* inbuf, int insize, int* outsize) | 289 | static unsigned char* uclpack(unsigned char* inbuf, int insize, int* outsize) { |
287 | { | ||
288 | int maxsize; | 290 | int maxsize; |
289 | unsigned char* outbuf; | 291 | unsigned char* outbuf; |
290 | int r; | 292 | int r; |
@@ -295,10 +297,9 @@ static unsigned char* uclpack(unsigned char* inbuf, int insize, int* outsize) | |||
295 | /* Allocate some memory for the output buffer */ | 297 | /* Allocate some memory for the output buffer */ |
296 | outbuf = malloc(maxsize); | 298 | outbuf = malloc(maxsize); |
297 | 299 | ||
298 | if (outbuf == NULL) { | 300 | if (outbuf == NULL) |
299 | return NULL; | 301 | return NULL; |
300 | } | 302 | |
301 | |||
302 | r = ucl_nrv2e_99_compress( | 303 | r = ucl_nrv2e_99_compress( |
303 | (const ucl_bytep) inbuf, | 304 | (const ucl_bytep) inbuf, |
304 | (ucl_uint) insize, | 305 | (ucl_uint) insize, |
@@ -306,8 +307,7 @@ static unsigned char* uclpack(unsigned char* inbuf, int insize, int* outsize) | |||
306 | (ucl_uintp) outsize, | 307 | (ucl_uintp) outsize, |
307 | 0, 10, NULL, NULL); | 308 | 0, 10, NULL, NULL); |
308 | 309 | ||
309 | if (r != UCL_E_OK || *outsize > maxsize) | 310 | if (r != UCL_E_OK || *outsize > maxsize) { |
310 | { | ||
311 | /* this should NEVER happen, and implies memory corruption */ | 311 | /* this should NEVER happen, and implies memory corruption */ |
312 | fprintf(stderr, "internal error - compression failed: %d\n", r); | 312 | fprintf(stderr, "internal error - compression failed: %d\n", r); |
313 | free(outbuf); | 313 | free(outbuf); |
@@ -317,42 +317,105 @@ static unsigned char* uclpack(unsigned char* inbuf, int insize, int* outsize) | |||
317 | return outbuf; | 317 | return outbuf; |
318 | } | 318 | } |
319 | 319 | ||
320 | static unsigned char* load_file(char* filename, off_t* bufsize) | 320 | #define ERROR(format, ...) \ |
321 | { | 321 | do { \ |
322 | snprintf(errstr, errstrsize, format, __VA_ARGS__); \ | ||
323 | goto error; \ | ||
324 | } while(0) | ||
325 | |||
326 | /* Loads a Sansa AMS Original Firmware file into memory */ | ||
327 | unsigned char* load_of_file( | ||
328 | char* filename, off_t* bufsize, char* md5sum, int* model, | ||
329 | int* fw_version, int* firmware_size, unsigned char** of_packed, | ||
330 | int* of_packedsize, char* errstr, int errstrsize | ||
331 | ) { | ||
322 | int fd; | 332 | int fd; |
323 | unsigned char* buf; | 333 | unsigned char* buf =NULL; |
324 | off_t n; | 334 | off_t n; |
335 | unsigned int i=0; | ||
336 | uint32_t checksum; | ||
337 | int model_id; | ||
338 | unsigned int last_word; | ||
325 | 339 | ||
326 | fd = open(filename, O_RDONLY|O_BINARY); | 340 | fd = open(filename, O_RDONLY|O_BINARY); |
327 | if (fd < 0) | 341 | if (fd < 0) |
328 | { | 342 | ERROR("[ERR] Could not open %s for reading\n", filename); |
329 | fprintf(stderr,"[ERR] Could not open %s for reading\n",filename); | ||
330 | return NULL; | ||
331 | } | ||
332 | 343 | ||
333 | *bufsize = filesize(fd); | 344 | *bufsize = filesize(fd); |
334 | 345 | ||
335 | buf = malloc(*bufsize); | 346 | buf = malloc(*bufsize); |
336 | if (buf == NULL) { | 347 | if (buf == NULL) |
337 | fprintf(stderr,"[ERR] Could not allocate memory for %s\n",filename); | 348 | ERROR("[ERR] Could not allocate memory for %s\n", filename); |
338 | return NULL; | ||
339 | } | ||
340 | 349 | ||
341 | n = read(fd, buf, *bufsize); | 350 | n = read(fd, buf, *bufsize); |
342 | 351 | ||
343 | if (n != *bufsize) { | 352 | if (n != *bufsize) |
344 | fprintf(stderr,"[ERR] Could not read file %s\n",filename); | 353 | ERROR("[ERR] Could not read file %s\n", filename); |
345 | return NULL; | 354 | |
355 | /* check the file */ | ||
356 | |||
357 | /* Calculate MD5 checksum of OF */ | ||
358 | calc_MD5(buf, *bufsize, md5sum); | ||
359 | |||
360 | while ((i < NUM_MD5S) && (strcmp(sansasums[i].md5, md5sum) != 0)) | ||
361 | i++; | ||
362 | |||
363 | if (i < NUM_MD5S) { | ||
364 | *model = sansasums[i].model; | ||
365 | *fw_version = sansasums[i].fw_version; | ||
366 | } else { | ||
367 | fprintf(stderr, "[WARN] ****** Original firmware unknown ******\n"); | ||
368 | if (get_uint32le(&buf[0x204])==0x0000f000) { | ||
369 | *fw_version = 2; | ||
370 | model_id = buf[0x219]; | ||
371 | } else { | ||
372 | *fw_version = 1; | ||
373 | model_id = buf[0x215]; | ||
374 | } | ||
375 | |||
376 | *model = get_model(model_id); | ||
377 | |||
378 | if (*model == MODEL_UNKNOWN) | ||
379 | ERROR("[ERR] Unknown firmware - model id 0x%02x\n", model_id); | ||
346 | } | 380 | } |
347 | 381 | ||
382 | /* TODO: Do some more sanity checks on the OF image. Some images (like | ||
383 | m200v4) dont have a checksum at the end, only padding (0xdeadbeef). */ | ||
384 | last_word = *bufsize - 4; | ||
385 | checksum = get_uint32le(buf + last_word); | ||
386 | if (checksum != 0xefbeadde && checksum != calc_checksum(buf, last_word)) | ||
387 | ERROR("%s", "[ERR] Whole file checksum failed\n"); | ||
388 | |||
389 | if (bootloaders[*model] == NULL) | ||
390 | ERROR("[ERR] Unsupported model - \"%s\"\n", model_names[*model]); | ||
391 | |||
392 | |||
393 | /* Get the firmware size */ | ||
394 | if (*fw_version == 1) | ||
395 | *firmware_size = get_uint32le(&buf[0x0c]); | ||
396 | else /* fw_version == 2 */ | ||
397 | *firmware_size = get_uint32le(&buf[0x10]); | ||
398 | |||
399 | /* Compress the original firmware image */ | ||
400 | *of_packed = uclpack(buf + 0x400, *firmware_size, of_packedsize); | ||
401 | if (*of_packed == NULL) | ||
402 | ERROR("[ERR] Could not compress %s\n", filename); | ||
403 | |||
348 | return buf; | 404 | return buf; |
349 | } | ||
350 | 405 | ||
406 | error: | ||
407 | free(buf); | ||
408 | return NULL; | ||
409 | } | ||
351 | 410 | ||
352 | static unsigned char* load_rockbox_file(char* filename, int model, off_t* bufsize) | 411 | /* Loads a rockbox bootloader file into memory */ |
353 | { | 412 | unsigned char* load_rockbox_file( |
413 | char* filename, int model, int* bufsize, int* rb_packedsize, | ||
414 | char* errstr, int errstrsize | ||
415 | ) { | ||
354 | int fd; | 416 | int fd; |
355 | unsigned char* buf; | 417 | unsigned char* buf = NULL; |
418 | unsigned char* packed = NULL; | ||
356 | unsigned char header[8]; | 419 | unsigned char header[8]; |
357 | uint32_t sum; | 420 | uint32_t sum; |
358 | off_t n; | 421 | off_t n; |
@@ -360,39 +423,28 @@ static unsigned char* load_rockbox_file(char* filename, int model, off_t* bufsiz | |||
360 | 423 | ||
361 | fd = open(filename, O_RDONLY|O_BINARY); | 424 | fd = open(filename, O_RDONLY|O_BINARY); |
362 | if (fd < 0) | 425 | if (fd < 0) |
363 | { | 426 | ERROR("[ERR] Could not open %s for reading\n", filename); |
364 | fprintf(stderr,"[ERR] Could not open %s for reading\n",filename); | ||
365 | return NULL; | ||
366 | } | ||
367 | 427 | ||
368 | /* Read Rockbox header */ | 428 | /* Read Rockbox header */ |
369 | n = read(fd, header, sizeof(header)); | 429 | n = read(fd, header, sizeof(header)); |
370 | if (n != sizeof(header)) { | 430 | if (n != sizeof(header)) |
371 | fprintf(stderr,"[ERR] Could not read file %s\n",filename); | 431 | ERROR("[ERR] Could not read file %s\n", filename); |
372 | return NULL; | ||
373 | } | ||
374 | 432 | ||
375 | /* Check for correct model string */ | 433 | /* Check for correct model string */ |
376 | if (memcmp(rb_model_names[model],header + 4,4)!=0) { | 434 | if (memcmp(rb_model_names[model], header + 4, 4)!=0) |
377 | fprintf(stderr,"[ERR] Model name \"%s\" not found in %s\n", | 435 | ERROR("[ERR] Model name \"%s\" not found in %s\n", |
378 | rb_model_names[model],filename); | 436 | rb_model_names[model], filename); |
379 | return NULL; | ||
380 | } | ||
381 | 437 | ||
382 | *bufsize = filesize(fd) - sizeof(header); | 438 | *bufsize = filesize(fd) - sizeof(header); |
383 | 439 | ||
384 | buf = malloc(*bufsize); | 440 | buf = malloc(*bufsize); |
385 | if (buf == NULL) { | 441 | if (buf == NULL) |
386 | fprintf(stderr,"[ERR] Could not allocate memory for %s\n",filename); | 442 | ERROR("[ERR] Could not allocate memory for %s\n", filename); |
387 | return NULL; | ||
388 | } | ||
389 | 443 | ||
390 | n = read(fd, buf, *bufsize); | 444 | n = read(fd, buf, *bufsize); |
391 | 445 | ||
392 | if (n != *bufsize) { | 446 | if (n != *bufsize) |
393 | fprintf(stderr,"[ERR] Could not read file %s\n",filename); | 447 | ERROR("[ERR] Could not read file %s\n", filename); |
394 | return NULL; | ||
395 | } | ||
396 | 448 | ||
397 | /* Check checksum */ | 449 | /* Check checksum */ |
398 | sum = rb_model_num[model]; | 450 | sum = rb_model_num[model]; |
@@ -401,176 +453,36 @@ static unsigned char* load_rockbox_file(char* filename, int model, off_t* bufsiz | |||
401 | sum += buf[i]; | 453 | sum += buf[i]; |
402 | } | 454 | } |
403 | 455 | ||
404 | if (sum != get_uint32be(header)) { | 456 | if (sum != get_uint32be(header)) |
405 | fprintf(stderr,"[ERR] Checksum mismatch in %s\n",filename); | 457 | ERROR("[ERR] Checksum mismatch in %s\n", filename); |
406 | return NULL; | ||
407 | } | ||
408 | return buf; | ||
409 | } | ||
410 | |||
411 | |||
412 | int main(int argc, char* argv[]) | ||
413 | { | ||
414 | char *infile, *bootfile, *outfile; | ||
415 | int fdout; | ||
416 | off_t len; | ||
417 | uint32_t n; | ||
418 | unsigned char* buf; | ||
419 | int firmware_size; | ||
420 | off_t bootloader_size; | ||
421 | uint32_t sum,filesum; | ||
422 | uint8_t model_id; | ||
423 | int model; | ||
424 | uint32_t i; | ||
425 | unsigned char* of_packed; | ||
426 | int of_packedsize; | ||
427 | unsigned char* rb_unpacked; | ||
428 | unsigned char* rb_packed; | ||
429 | int rb_packedsize; | ||
430 | int fw_version; | ||
431 | int totalsize; | ||
432 | unsigned char* p; | ||
433 | uint32_t checksum; | ||
434 | char md5sum[33]; /* 32 hex digits, plus terminating zero */ | ||
435 | |||
436 | fprintf(stderr,"mkamsboot v" VERSION " - (C) Dave Chapman and Rafaël Carré 2008\n"); | ||
437 | fprintf(stderr,"This is free software; see the source for copying conditions. There is NO\n"); | ||
438 | fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"); | ||
439 | |||
440 | if(argc != 4) { | ||
441 | printf("Usage: mkamsboot <firmware file> <boot file> <output file>\n\n"); | ||
442 | return 1; | ||
443 | } | ||
444 | |||
445 | infile = argv[1]; | ||
446 | bootfile = argv[2]; | ||
447 | outfile = argv[3]; | ||
448 | |||
449 | /* Load original firmware file */ | ||
450 | buf = load_file(infile, &len); | ||
451 | |||
452 | if (buf == NULL) { | ||
453 | fprintf(stderr,"[ERR] Could not load %s\n",infile); | ||
454 | return 1; | ||
455 | } | ||
456 | |||
457 | /* Calculate MD5 checksum of OF */ | ||
458 | calc_MD5(buf, len, md5sum); | ||
459 | 458 | ||
460 | fprintf(stderr,"[INFO] MD5 sum - %s\n",md5sum); | 459 | packed = uclpack(buf, *bufsize, rb_packedsize); |
460 | if(packed == NULL) | ||
461 | ERROR("[ERR] Could not compress %s\n", filename); | ||
461 | 462 | ||
462 | i = 0; | 463 | free(buf); |
463 | while ((i < NUM_MD5S) && (strcmp(sansasums[i].md5, md5sum) != 0)) | 464 | return packed; |
464 | i++; | ||
465 | 465 | ||
466 | if (i < NUM_MD5S) { | 466 | error: |
467 | model = sansasums[i].model; | 467 | free(buf); |
468 | fw_version = sansasums[i].fw_version; | 468 | return NULL; |
469 | fprintf(stderr,"[INFO] Original firmware MD5 checksum match - %s %s\n", | 469 | } |
470 | model_names[model], sansasums[i].version); | ||
471 | } else { | ||
472 | if (get_uint32le(&buf[0x204])==0x0000f000) { | ||
473 | fw_version = 2; | ||
474 | model_id = buf[0x219]; | ||
475 | } else { | ||
476 | fw_version = 1; | ||
477 | model_id = buf[0x215]; | ||
478 | } | ||
479 | |||
480 | model = get_model(model_id); | ||
481 | |||
482 | #if 0 | ||
483 | /* if you are a tester this info might help */ | ||
484 | fprintf(stderr,"[WARN] ****** Original firmware unknown ******\n"); | ||
485 | if (model == MODEL_UNKNOWN) { | ||
486 | fprintf(stderr,"[ERR] Unknown firmware - model id 0x%02x\n", | ||
487 | model_id); | ||
488 | free(buf); | ||
489 | return 1; | ||
490 | } | ||
491 | #else | ||
492 | /* else you don't want to brick your player */ | ||
493 | fprintf(stderr, "[ERR] Original firmware untested - aborting\n"); | ||
494 | free(buf); | ||
495 | return 1; | ||
496 | #endif | ||
497 | } | ||
498 | |||
499 | |||
500 | /* TODO: Do some more sanity checks on the OF image. Some images (like | ||
501 | m200v4) dont have a checksum at the end, only padding (0xdeadbeef). */ | ||
502 | checksum = get_uint32le(buf + len - 4); | ||
503 | if (checksum != 0xefbeadde && checksum != calc_checksum(buf, len - 4)) { | ||
504 | |||
505 | fprintf(stderr,"[ERR] Whole file checksum failed - %s\n",infile); | ||
506 | free(buf); | ||
507 | return 1; | ||
508 | } | ||
509 | |||
510 | if (bootloaders[model] == NULL) { | ||
511 | fprintf(stderr,"[ERR] Unsupported model - \"%s\"\n",model_names[model]); | ||
512 | free(buf); | ||
513 | return 1; | ||
514 | } | ||
515 | |||
516 | /* Load bootloader file */ | ||
517 | rb_unpacked = load_rockbox_file(bootfile, model, &bootloader_size); | ||
518 | if (rb_unpacked == NULL) { | ||
519 | fprintf(stderr,"[ERR] Could not load %s\n",bootfile); | ||
520 | free(buf); | ||
521 | return 1; | ||
522 | } | ||
523 | |||
524 | printf("[INFO] Patching %s firmware\n",model_names[model]); | ||
525 | |||
526 | /* Get the firmware size */ | ||
527 | firmware_size = get_uint32le(&buf[0x0c]); | ||
528 | |||
529 | /* Compress the original firmware image */ | ||
530 | of_packed = uclpack(buf + 0x400, firmware_size, &of_packedsize); | ||
531 | if (of_packed == NULL) { | ||
532 | fprintf(stderr,"[ERR] Could not compress original firmware\n"); | ||
533 | free(buf); | ||
534 | free(rb_unpacked); | ||
535 | return 1; | ||
536 | } | ||
537 | |||
538 | rb_packed = uclpack(rb_unpacked, bootloader_size, &rb_packedsize); | ||
539 | if (rb_packed == NULL) { | ||
540 | fprintf(stderr,"[ERR] Could not compress %s\n",bootfile); | ||
541 | free(buf); | ||
542 | free(rb_unpacked); | ||
543 | free(of_packed); | ||
544 | return 1; | ||
545 | } | ||
546 | |||
547 | /* We are finished with the unpacked version of the bootloader */ | ||
548 | free(rb_unpacked); | ||
549 | |||
550 | fprintf(stderr,"[INFO] Original firmware size: %d bytes\n",firmware_size); | ||
551 | fprintf(stderr,"[INFO] Packed OF size: %d bytes\n",of_packedsize); | ||
552 | fprintf(stderr,"[INFO] Bootloader size: %d bytes\n",(int)bootloader_size); | ||
553 | fprintf(stderr,"[INFO] Packed bootloader size: %d bytes\n",rb_packedsize); | ||
554 | fprintf(stderr,"[INFO] Dual-boot function size: %d bytes\n",bootloader_sizes[model]); | ||
555 | fprintf(stderr,"[INFO] UCL unpack function size: %d bytes\n",sizeof(nrv2e_d8)); | ||
556 | |||
557 | totalsize = bootloader_sizes[model] + sizeof(nrv2e_d8) + of_packedsize + | ||
558 | rb_packedsize; | ||
559 | 470 | ||
560 | fprintf(stderr,"[INFO] Total size of new image: %d bytes\n",totalsize); | 471 | #undef ERROR |
561 | 472 | ||
562 | if (totalsize > firmware_size) { | 473 | /* Patches a Sansa AMS Original Firmware file */ |
563 | fprintf(stderr,"[ERR] No room to insert bootloader, aborting\n"); | 474 | void patch_firmware( |
564 | free(buf); | 475 | int model, int fw_version, int firmware_size, unsigned char* buf, |
565 | free(rb_unpacked); | 476 | int len, unsigned char* of_packed, int of_packedsize, |
566 | free(of_packed); | 477 | unsigned char* rb_packed, int rb_packedsize |
567 | return 1; | 478 | ) { |
568 | } | 479 | unsigned char *p; |
480 | uint32_t sum, filesum; | ||
481 | unsigned int i; | ||
569 | 482 | ||
570 | /* Zero the original firmware area - not needed, but helps debugging */ | 483 | /* Zero the original firmware area - not needed, but helps debugging */ |
571 | memset(buf + 0x400, 0, firmware_size); | 484 | memset(buf + 0x400, 0, firmware_size); |
572 | 485 | ||
573 | |||
574 | /* Insert dual-boot bootloader at offset 0 */ | 486 | /* Insert dual-boot bootloader at offset 0 */ |
575 | memcpy(buf + 0x400, bootloaders[model], bootloader_sizes[model]); | 487 | memcpy(buf + 0x400, bootloaders[model], bootloader_sizes[model]); |
576 | 488 | ||
@@ -589,7 +501,7 @@ int main(int argc, char* argv[]) | |||
589 | p -= rb_packedsize; | 501 | p -= rb_packedsize; |
590 | memcpy(p, rb_packed, rb_packedsize); | 502 | memcpy(p, rb_packed, rb_packedsize); |
591 | 503 | ||
592 | /* Write the locations of the various images to the variables at the | 504 | /* Write the locations of the various images to the variables at the |
593 | start of the dualboot image - we save the location of the last byte | 505 | start of the dualboot image - we save the location of the last byte |
594 | in each image, along with the size in bytes */ | 506 | in each image, along with the size in bytes */ |
595 | 507 | ||
@@ -602,19 +514,18 @@ int main(int argc, char* argv[]) | |||
602 | put_uint32le(&buf[0x42c], of_packedsize); | 514 | put_uint32le(&buf[0x42c], of_packedsize); |
603 | 515 | ||
604 | /* Compressed Rockbox image */ | 516 | /* Compressed Rockbox image */ |
605 | put_uint32le(&buf[0x430], firmware_size - sizeof(nrv2e_d8) - of_packedsize - 1); | 517 | put_uint32le(&buf[0x430], firmware_size - sizeof(nrv2e_d8) - of_packedsize |
518 | - 1); | ||
606 | put_uint32le(&buf[0x434], rb_packedsize); | 519 | put_uint32le(&buf[0x434], rb_packedsize); |
607 | 520 | ||
608 | 521 | ||
609 | /* Update the firmware block checksum */ | 522 | /* Update the firmware block checksum */ |
610 | sum = calc_checksum(buf + 0x400,firmware_size); | 523 | sum = calc_checksum(buf + 0x400, firmware_size); |
611 | 524 | ||
612 | if (fw_version == 1) { | 525 | if (fw_version == 1) { |
613 | put_uint32le(&buf[0x04], sum); | 526 | put_uint32le(&buf[0x04], sum); |
614 | put_uint32le(&buf[0x204], sum); | 527 | put_uint32le(&buf[0x204], sum); |
615 | } else { | 528 | } else { |
616 | /* TODO: Verify that this is correct for the v2 firmware */ | ||
617 | |||
618 | put_uint32le(&buf[0x08], sum); | 529 | put_uint32le(&buf[0x08], sum); |
619 | put_uint32le(&buf[0x208], sum); | 530 | put_uint32le(&buf[0x208], sum); |
620 | 531 | ||
@@ -629,29 +540,130 @@ int main(int argc, char* argv[]) | |||
629 | filesum += get_uint32le(&buf[i]); | 540 | filesum += get_uint32le(&buf[i]); |
630 | 541 | ||
631 | put_uint32le(buf + len - 4, filesum); | 542 | put_uint32le(buf + len - 4, filesum); |
543 | } | ||
632 | 544 | ||
545 | /* returns size of new firmware block */ | ||
546 | int total_size(int model, int rb_packedsize, int of_packedsize) { | ||
547 | return bootloader_sizes[model] + sizeof(nrv2e_d8) + of_packedsize + | ||
548 | rb_packedsize; | ||
549 | } | ||
550 | |||
551 | #ifndef LIB | ||
552 | /* standalone executable */ | ||
553 | int main(int argc, char* argv[]) { | ||
554 | char *infile, *bootfile, *outfile; | ||
555 | int fdout; | ||
556 | off_t len; | ||
557 | uint32_t n; | ||
558 | unsigned char* buf; | ||
559 | int firmware_size; | ||
560 | int bootloader_size; | ||
561 | int model; | ||
562 | unsigned char* of_packed; | ||
563 | int of_packedsize; | ||
564 | unsigned char* rb_packed; | ||
565 | int rb_packedsize; | ||
566 | int fw_version; | ||
567 | int totalsize; | ||
568 | char md5sum[33]; /* 32 hex digits, plus terminating zero */ | ||
569 | char errstr[200]; | ||
570 | |||
571 | fprintf(stderr, | ||
572 | "mkamsboot v" VERSION " - (C) Dave Chapman and Rafaël Carré 2008\n" | ||
573 | "This is free software; see the source for copying conditions. There is NO\n" | ||
574 | "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" | ||
575 | "\n"); | ||
576 | |||
577 | if(argc != 4) { | ||
578 | printf("Usage: mkamsboot <firmware file> <boot file> <output file>\n"); | ||
579 | return 1; | ||
580 | } | ||
581 | |||
582 | infile = argv[1]; | ||
583 | bootfile = argv[2]; | ||
584 | outfile = argv[3]; | ||
585 | |||
586 | /* Load original firmware file */ | ||
587 | buf = load_of_file(infile, &len, md5sum, &model, &fw_version, | ||
588 | &firmware_size, &of_packed, &of_packedsize, errstr, sizeof(errstr)); | ||
589 | |||
590 | if (buf == NULL) { | ||
591 | fprintf(stderr, "%s", errstr); | ||
592 | fprintf(stderr, "[ERR] Could not load %s\n", infile); | ||
593 | return 1; | ||
594 | } | ||
595 | |||
596 | fprintf(stderr, "[INFO] Original firmware MD5 checksum match - %s\n", | ||
597 | model_names[model]); | ||
598 | |||
599 | |||
600 | /* Load bootloader file */ | ||
601 | rb_packed = load_rockbox_file(bootfile, model, &bootloader_size, | ||
602 | &rb_packedsize, errstr, sizeof(errstr)); | ||
603 | if (rb_packed == NULL) { | ||
604 | fprintf(stderr, "%s", errstr); | ||
605 | fprintf(stderr, "[ERR] Could not load %s\n", bootfile); | ||
606 | free(buf); | ||
607 | free(of_packed); | ||
608 | return 1; | ||
609 | } | ||
610 | |||
611 | printf("[INFO] Patching %s firmware\n", model_names[model]); | ||
612 | |||
613 | fprintf(stderr, "[INFO] Original firmware size: %d bytes\n", | ||
614 | firmware_size); | ||
615 | fprintf(stderr, "[INFO] Packed OF size: %d bytes\n", | ||
616 | of_packedsize); | ||
617 | fprintf(stderr, "[INFO] Bootloader size: %d bytes\n", | ||
618 | (int)bootloader_size); | ||
619 | fprintf(stderr, "[INFO] Packed bootloader size: %d bytes\n", | ||
620 | rb_packedsize); | ||
621 | fprintf(stderr, "[INFO] Dual-boot function size: %d bytes\n", | ||
622 | bootloader_sizes[model]); | ||
623 | fprintf(stderr, "[INFO] UCL unpack function size: %d bytes\n", | ||
624 | sizeof(nrv2e_d8)); | ||
625 | |||
626 | totalsize = total_size(model, of_packedsize, rb_packedsize); | ||
627 | |||
628 | fprintf(stderr, "[INFO] Total size of new image: %d bytes\n", totalsize); | ||
629 | |||
630 | if (totalsize > firmware_size) { | ||
631 | fprintf(stderr, "[ERR] No room to insert bootloader, aborting\n"); | ||
632 | free(buf); | ||
633 | free(of_packed); | ||
634 | free(rb_packed); | ||
635 | return 1; | ||
636 | } | ||
637 | |||
638 | patch_firmware(model, fw_version, firmware_size, buf, len, of_packed, | ||
639 | of_packedsize, rb_packed, rb_packedsize); | ||
633 | 640 | ||
634 | /* Write the new firmware */ | 641 | /* Write the new firmware */ |
635 | fdout = open(outfile, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666); | 642 | fdout = open(outfile, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, 0666); |
636 | 643 | ||
637 | if (fdout < 0) { | 644 | if (fdout < 0) { |
638 | fprintf(stderr,"[ERR] Could not open %s for writing\n",outfile); | 645 | fprintf(stderr, "[ERR] Could not open %s for writing\n", outfile); |
646 | free(buf); | ||
647 | free(of_packed); | ||
648 | free(rb_packed); | ||
639 | return 1; | 649 | return 1; |
640 | } | 650 | } |
641 | 651 | ||
642 | n = write(fdout, buf, len); | 652 | n = write(fdout, buf, len); |
643 | 653 | ||
644 | if (n != (unsigned)len) { | 654 | if (n != (unsigned)len) { |
645 | fprintf(stderr,"[ERR] Could not write firmware file\n"); | 655 | fprintf(stderr, "[ERR] Could not write firmware file\n"); |
656 | free(buf); | ||
657 | free(of_packed); | ||
658 | free(rb_packed); | ||
646 | return 1; | 659 | return 1; |
647 | } | 660 | } |
648 | 661 | ||
649 | close(fdout); | 662 | close(fdout); |
650 | 663 | free(buf); | |
651 | fprintf(stderr," *****************************************************************************\n"); | 664 | free(of_packed); |
652 | fprintf(stderr," *** THIS CODE IS UNTESTED - DO NOT USE IF YOU CAN NOT RECOVER YOUR DEVICE ***\n"); | 665 | free(rb_packed); |
653 | fprintf(stderr," *****************************************************************************\n"); | ||
654 | 666 | ||
655 | return 0; | 667 | return 0; |
656 | |||
657 | } | 668 | } |
669 | #endif | ||