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