diff options
Diffstat (limited to 'tools/mr500.c')
-rw-r--r-- | tools/mr500.c | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/tools/mr500.c b/tools/mr500.c new file mode 100644 index 0000000000..00cf17e26b --- /dev/null +++ b/tools/mr500.c | |||
@@ -0,0 +1,457 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * | ||
9 | * Copyright (C) 2009 by Karl Kurbjun | ||
10 | * based on work by Shirour: | ||
11 | * http://www.mrobe.org/forum/viewtopic.php?f=6&t=2176 | ||
12 | * $Id$ | ||
13 | * | ||
14 | * All files in this archive are subject to the GNU General Public License. | ||
15 | * See the file COPYING in the source tree root for full license agreement. | ||
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 <string.h> | ||
24 | #include <stdlib.h> | ||
25 | #include <fcntl.h> | ||
26 | #include <errno.h> | ||
27 | #include <unistd.h> | ||
28 | #include <inttypes.h> | ||
29 | #include <sys/stat.h> | ||
30 | #include "mr500.h" | ||
31 | |||
32 | /* Notes about firmware: | ||
33 | * These notes are based on the work and observations of Shirour on the M:Robe | ||
34 | * forums. | ||
35 | * | ||
36 | * The firmware for the M:Robe has basic encryption on it. The data is XORed | ||
37 | * and scrambled. The mr500_save_data function provides an implemenation of the | ||
38 | * encryption/decryption. | ||
39 | * | ||
40 | * When a firmware update is done the "{#4F494D4346575550#}" folder is stored | ||
41 | * in the system folder on the player. The "{#4F494D4346575550#}" should only | ||
42 | * contain the encrypted N5002-BD.BIN file. At the end of a firmware update | ||
43 | * the "{#4F494D4346575550#}" folder and it's contents are removed from the | ||
44 | * player. | ||
45 | * | ||
46 | * An interesting note is that the name "{#4F494D4346575550#}" is actually the | ||
47 | * Hex representation of the magic text found at the beginning of the firmware | ||
48 | * image "OIMCFWUP". | ||
49 | */ | ||
50 | |||
51 | /* These two arrays are used for descrambling or scrambling the data */ | ||
52 | int decrypt_array[16]={2, 0, 3, 1, 5, 7, 4, 6, 11, 10, 9, 8, 14, 12, 13, 15}; | ||
53 | int encrypt_array[16]; | ||
54 | |||
55 | /* mr500_patch_file: This function modifies the specified file with the patches | ||
56 | * struct. | ||
57 | * | ||
58 | * Parameters: | ||
59 | * filename: text filename | ||
60 | * patches: pointer to structure array of patches | ||
61 | * num_patches: number of patches to apply (applied in reverse order) | ||
62 | * | ||
63 | * Returns: | ||
64 | * Returns 0 if there was no error, -1 if there was an error | ||
65 | */ | ||
66 | int mr500_patch_file(char *filename, struct patch_single *patches, | ||
67 | int num_patches) { | ||
68 | int fdo; | ||
69 | int ret=0; | ||
70 | uint32_t endian_int; | ||
71 | |||
72 | /* Open the file write only. */ | ||
73 | fdo = open(filename, O_WRONLY); | ||
74 | |||
75 | if(fdo<0) { | ||
76 | ret=-1; | ||
77 | } | ||
78 | |||
79 | while(num_patches--) { | ||
80 | /* seek to patch offset */ | ||
81 | if(lseek(fdo, patches[num_patches].offset, SEEK_SET) | ||
82 | != patches[num_patches].offset) { | ||
83 | ret=-1; | ||
84 | break; | ||
85 | } | ||
86 | |||
87 | /* Make sure patch is written in little endian format */ | ||
88 | endian_int = htole32(patches[num_patches].value); | ||
89 | |||
90 | /* Write the patch value to the file */ | ||
91 | if(write(fdo, (void *) &endian_int, sizeof(endian_int)) < 0) { | ||
92 | ret = -1; | ||
93 | break; | ||
94 | } | ||
95 | } | ||
96 | |||
97 | /* Close the file and check for errors */ | ||
98 | if(close (fdo) < 0) { | ||
99 | ret = -1; | ||
100 | } | ||
101 | |||
102 | return ret; | ||
103 | } | ||
104 | |||
105 | /* mr500_save_header: This function saves the Olympus header to the firmware | ||
106 | * image. The values stored in the header are explained above. Note that this | ||
107 | * will truncate a file. The header is stored in little endian format. | ||
108 | * | ||
109 | * Parameters: | ||
110 | * filename: text filename | ||
111 | * header: pointer to header structure to be saved | ||
112 | * | ||
113 | * Returns: | ||
114 | * Returns 0 if there was no error, -1 if there was an error | ||
115 | */ | ||
116 | int mr500_save_header(char *filename, struct olympus_header *header) { | ||
117 | int fdo; | ||
118 | int ret=0; | ||
119 | |||
120 | /* Temporary header used for storing the header in little endian. */ | ||
121 | struct olympus_header save; | ||
122 | |||
123 | /* Open the file write only and truncate it. If it doesn't exist create. */ | ||
124 | fdo = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); | ||
125 | |||
126 | if(fdo<0) { | ||
127 | ret=-1; | ||
128 | } | ||
129 | |||
130 | /* Header is stored at offset 0 (Not really needed) */ | ||
131 | if(lseek(fdo, 0, SEEK_SET) != 0) { | ||
132 | ret=-1; | ||
133 | } | ||
134 | |||
135 | /* Convert header to Little Endian */ | ||
136 | memcpy(&save.magic_name, &header->magic_name, 8*sizeof(int8_t)); | ||
137 | save.unknown = htole32(header->unknown); | ||
138 | save.header_length = htole16(header->header_length); | ||
139 | save.flags = htole16(header->flags); | ||
140 | save.unknown_zeros = htole32(header->unknown_zeros); | ||
141 | save.image_length = htole32(header->image_length); | ||
142 | |||
143 | /* Write the header to the file */ | ||
144 | if(write(fdo, (void *) &save, sizeof(save)) < 0) { | ||
145 | ret = -1; | ||
146 | } | ||
147 | |||
148 | /* Close the file and check for errors */ | ||
149 | if(close (fdo) < 0) { | ||
150 | ret = -1; | ||
151 | } | ||
152 | |||
153 | return ret; | ||
154 | } | ||
155 | |||
156 | /* mr500_read_header: This function reads the Olympus header and converts it to | ||
157 | * the host endian format. The values stored in the header are explained above. | ||
158 | * The header is stored in little endian format. | ||
159 | * | ||
160 | * Parameters: | ||
161 | * filename: text filename | ||
162 | * header: pointer to header structure to store header read from file | ||
163 | * | ||
164 | * Returns: | ||
165 | * Returns 0 if there was no error, -1 if there was an error | ||
166 | */ | ||
167 | int mr500_read_header(char *filename, struct olympus_header *header) { | ||
168 | int fdi; | ||
169 | int ret = 0; | ||
170 | |||
171 | /* Open the file read only */ | ||
172 | fdi = open(filename, O_RDONLY); | ||
173 | |||
174 | if(fdi<0) { | ||
175 | ret=-1; | ||
176 | } | ||
177 | |||
178 | /* Header is stored at offset 0 (Not really needed) */ | ||
179 | if(lseek(fdi, 0, SEEK_SET) != 0) { | ||
180 | ret=-1; | ||
181 | } | ||
182 | |||
183 | /* Read in the header */ | ||
184 | if(read(fdi, (void *) header, sizeof(*header)) < 0) { | ||
185 | ret = -1; | ||
186 | } | ||
187 | |||
188 | /* Convert header to system endian */ | ||
189 | header->unknown = le32toh(header->unknown); | ||
190 | header->header_length = le16toh(header->header_length); | ||
191 | header->flags = le16toh(header->flags); | ||
192 | header->unknown_zeros = le32toh(header->unknown_zeros); | ||
193 | header->image_length = le32toh(header->image_length); | ||
194 | |||
195 | /* Close the file and check for errors */ | ||
196 | if(close (fdi) < 0) { | ||
197 | ret = -1; | ||
198 | } | ||
199 | |||
200 | return ret; | ||
201 | } | ||
202 | |||
203 | /* mr500_save_crc: This function saves the 'CRC' of the Olympus firmware image. | ||
204 | * Note that the 'CRC' must be calculated on the decrytped image. It is stored | ||
205 | * in little endian. | ||
206 | * | ||
207 | * Parameters: | ||
208 | * filename: text filename | ||
209 | * offset: Offset to store the 'CRC' (header size + data size) | ||
210 | * crc_value: pointer to crc value to save | ||
211 | * | ||
212 | * Returns: | ||
213 | * Returns 0 if there was no error, -1 if there was an error | ||
214 | */ | ||
215 | int mr500_save_crc(char *filename, off_t offset, uint32_t *crc_value) { | ||
216 | int fdo; | ||
217 | int ret = 0; | ||
218 | uint32_t save_crc; | ||
219 | |||
220 | /* Open the file write only */ | ||
221 | fdo = open(filename, O_WRONLY); | ||
222 | |||
223 | if(fdo<0) { | ||
224 | ret=-1; | ||
225 | } | ||
226 | |||
227 | /* Seek to offset and check for errors */ | ||
228 | if(lseek(fdo, offset, SEEK_SET) != offset) { | ||
229 | ret=-1; | ||
230 | } | ||
231 | |||
232 | /* Convert 'CRC' to little endian from system native endian */ | ||
233 | save_crc = htole32(*crc_value); | ||
234 | |||
235 | /* Write the 'CRC' and check for errors */ | ||
236 | if(write(fdo, (void *) &save_crc, sizeof(unsigned int)) < 0) { | ||
237 | ret = -1; | ||
238 | } | ||
239 | |||
240 | /* Close the file and check for errors */ | ||
241 | if(close (fdo) < 0) { | ||
242 | ret = -1; | ||
243 | } | ||
244 | |||
245 | return ret; | ||
246 | } | ||
247 | |||
248 | /* mr500_read_crc: This function reads the 'CRC' of the Olympus firmware image. | ||
249 | * Note that the 'CRC' is calculated on the decrytped values. It is stored | ||
250 | * in little endian. | ||
251 | * | ||
252 | * Parameters: | ||
253 | * filename: text filename | ||
254 | * offset: Offset to read the 'CRC' (header size + data size) | ||
255 | * crc_value: pointer to crc value to save | ||
256 | * | ||
257 | * Returns: | ||
258 | * Returns 0 if there was no error, -1 if there was an error | ||
259 | */ | ||
260 | int mr500_read_crc(char *filename, off_t offset, uint32_t *crc_value) { | ||
261 | int fdi; | ||
262 | int ret = 0; | ||
263 | |||
264 | /* Open the file read only */ | ||
265 | fdi = open(filename, O_RDONLY); | ||
266 | |||
267 | if(fdi<0) { | ||
268 | ret = -1; | ||
269 | } | ||
270 | |||
271 | /* Seek to offset and check for errors */ | ||
272 | if(lseek(fdi, offset, SEEK_SET) != offset) { | ||
273 | ret=-1; | ||
274 | } | ||
275 | |||
276 | /* Read in the 'CRC' */ | ||
277 | if(read(fdi, (void *) crc_value, sizeof(uint32_t)) < 0) { | ||
278 | ret = -1; | ||
279 | } | ||
280 | |||
281 | /* Convert the 'CRC' from little endian to system native format */ | ||
282 | *crc_value = le32toh(*crc_value); | ||
283 | |||
284 | /* Close the file and check for errors */ | ||
285 | if(close (fdi) < 0) { | ||
286 | ret = -1; | ||
287 | } | ||
288 | |||
289 | return ret; | ||
290 | } | ||
291 | |||
292 | /* mr500_calculate_crc: This function calculates the 'CRC' of the Olympus | ||
293 | * firmware image. Note that the 'CRC' must be calculated on decrytped values. | ||
294 | * It is stored in little endian. | ||
295 | * | ||
296 | * Parameters: | ||
297 | * filename: text filename | ||
298 | * offset: Offset to the start of the data (header size) | ||
299 | * length: Length of data to calculate | ||
300 | * crc_value: pointer to crc value to save | ||
301 | * | ||
302 | * Returns: | ||
303 | * Returns 0 if there was no error, -1 if there was an error | ||
304 | */ | ||
305 | int mr500_calculate_crc( char *filename, off_t offset, unsigned int length, | ||
306 | uint32_t *crc_value){ | ||
307 | uint32_t temp; | ||
308 | int fdi; | ||
309 | int ret = 0; | ||
310 | |||
311 | /* Open the file read only */ | ||
312 | fdi = open(filename, O_RDONLY); | ||
313 | |||
314 | if(fdi<0) { | ||
315 | ret = -1; | ||
316 | } | ||
317 | |||
318 | /* Seek to offset and check for errors */ | ||
319 | if(lseek(fdi, offset, SEEK_SET) != offset) { | ||
320 | ret=-1; | ||
321 | } | ||
322 | |||
323 | /* Initialize the crc_value to make sure this starts at 0 */ | ||
324 | *crc_value = 0; | ||
325 | /* Run this loop till the entire sum is created */ | ||
326 | do { | ||
327 | /* Read an integer at a time */ | ||
328 | if(read(fdi, &temp, sizeof(uint32_t)) < 0) { | ||
329 | ret = -1; | ||
330 | break; | ||
331 | } | ||
332 | |||
333 | /* Keep summing the values */ | ||
334 | *crc_value+=temp; | ||
335 | } while (length-=4); | ||
336 | |||
337 | /* Close the file and check for errors */ | ||
338 | if(close (fdi) < 0) { | ||
339 | ret = -1; | ||
340 | } | ||
341 | |||
342 | return ret; | ||
343 | } | ||
344 | |||
345 | /* mr500_save_data: This function encypts or decrypts the Olympus firmware | ||
346 | * image based on the dictionary passed to it. | ||
347 | * | ||
348 | * Parameters: | ||
349 | * source_filename: text filename where data is read from | ||
350 | * dest_filename: text filename where data is written to | ||
351 | * offset: Offset to the start of the data (header size) | ||
352 | * length: Length of data to modify | ||
353 | * dictionary: pointer to dictionary used for scrambling | ||
354 | * | ||
355 | * Returns: | ||
356 | * Returns 0 if there was no error, -1 if there was an error | ||
357 | */ | ||
358 | int mr500_save_data( | ||
359 | char *source_filename, char *dest_filename, off_t offset, | ||
360 | unsigned int length, int* dictionary) { | ||
361 | int fdi, fdo; | ||
362 | int ret = 0; | ||
363 | int i; | ||
364 | |||
365 | /* read_count stores the number of bytes actually read */ | ||
366 | int read_count; | ||
367 | |||
368 | /* read_request stores the number of bytes to be requested */ | ||
369 | int read_request; | ||
370 | |||
371 | /* These two buffers are used for reading data and scrambling or | ||
372 | * descrambling | ||
373 | */ | ||
374 | int8_t buffer_original[16]; | ||
375 | int8_t buffer_modified[16]; | ||
376 | |||
377 | /* Open input read only, output write only */ | ||
378 | fdi = open(source_filename, O_RDONLY); | ||
379 | fdo = open(dest_filename, O_WRONLY); | ||
380 | |||
381 | /* If there was an error loading the files set ret appropriately */ | ||
382 | if(fdi<0 || fdo < 0) { | ||
383 | ret = -1; | ||
384 | } | ||
385 | |||
386 | /* Input file: Seek to offset and check for errors */ | ||
387 | if(lseek(fdi, offset, SEEK_SET) != offset) { | ||
388 | ret=-1; | ||
389 | } | ||
390 | |||
391 | /* Output file: Seek to offset and check for errors */ | ||
392 | if(lseek(fdo, offset, SEEK_SET) != offset) { | ||
393 | ret=-1; | ||
394 | } | ||
395 | |||
396 | /* Run this loop till size is 0 */ | ||
397 | do { | ||
398 | /* Choose the amount of data to read - normally in 16 byte chunks, but | ||
399 | * when the end of the file is near may be less. | ||
400 | */ | ||
401 | if( length > sizeof(buffer_original)){ | ||
402 | read_request = sizeof(buffer_original); | ||
403 | } else { | ||
404 | read_request = length; | ||
405 | } | ||
406 | |||
407 | /* Read in the data */ | ||
408 | read_count = read(fdi, (void *) &buffer_original, read_request); | ||
409 | |||
410 | /* If there was an error set the flag and break */ | ||
411 | if(read_count < 0) { | ||
412 | ret = -1; | ||
413 | break; | ||
414 | } | ||
415 | |||
416 | for(i=0; i<read_count; i++) { | ||
417 | /* XOR all of the bits to de/encrypt them */ | ||
418 | buffer_original[i] ^= 0xFF; | ||
419 | /* Handle byte scrambling */ | ||
420 | buffer_modified[dictionary[i]] = buffer_original[i]; | ||
421 | } | ||
422 | |||
423 | /* write the data: If there was an error set the flag and break */ | ||
424 | if(write(fdo, (void *) &buffer_modified, read_count) < 0) { | ||
425 | ret = -1; | ||
426 | break; | ||
427 | } | ||
428 | } while (length -= read_count); | ||
429 | |||
430 | /* Close the files and check for errors */ | ||
431 | if(close (fdi) < 0) { | ||
432 | ret = -1; | ||
433 | } | ||
434 | if(close (fdo) < 0) { | ||
435 | ret = -1; | ||
436 | } | ||
437 | |||
438 | return ret; | ||
439 | } | ||
440 | |||
441 | /* mr500_init: This function initializes the encryption array | ||
442 | * | ||
443 | * Parameters: | ||
444 | * None | ||
445 | * | ||
446 | * Returns: | ||
447 | * Returns 0 | ||
448 | */ | ||
449 | int mr500_init(void) { | ||
450 | int i; | ||
451 | /* Initialize the encryption array */ | ||
452 | for(i=0; i<16; i++) { | ||
453 | encrypt_array[decrypt_array[i]]=i; | ||
454 | } | ||
455 | return 0; | ||
456 | } | ||
457 | |||