summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
Diffstat (limited to 'firmware')
-rw-r--r--firmware/drivers/fat.c211
-rw-r--r--firmware/export/fat.h7
2 files changed, 99 insertions, 119 deletions
diff --git a/firmware/drivers/fat.c b/firmware/drivers/fat.c
index 13719fa874..ff2bebce92 100644
--- a/firmware/drivers/fat.c
+++ b/firmware/drivers/fat.c
@@ -31,6 +31,7 @@
31#include "timefuncs.h" 31#include "timefuncs.h"
32#include "kernel.h" 32#include "kernel.h"
33#include "rbunicode.h" 33#include "rbunicode.h"
34/*#define LOGF_ENABLE*/
34#include "logf.h" 35#include "logf.h"
35 36
36#define BYTES2INT16(array,pos) \ 37#define BYTES2INT16(array,pos) \
@@ -110,6 +111,14 @@
110#define FATLONG_ORDER 0 111#define FATLONG_ORDER 0
111#define FATLONG_TYPE 12 112#define FATLONG_TYPE 12
112#define FATLONG_CHKSUM 13 113#define FATLONG_CHKSUM 13
114#define FATLONG_LAST_LONG_ENTRY 0x40
115#define FATLONG_NAME_BYTES_PER_ENTRY 26
116/* at most 20 LFN entries, keep coherent with fat_dir->longname size ! */
117#define FATLONG_MAX_ORDER 20
118
119#define FATLONG_NAME_CHUNKS 3
120static unsigned char FATLONG_NAME_POS[FATLONG_NAME_CHUNKS] = {1, 14, 28};
121static unsigned char FATLONG_NAME_SIZE[FATLONG_NAME_CHUNKS] = {10, 12, 4};
113 122
114#define CLUSTERS_PER_FAT_SECTOR (SECTOR_SIZE / 4) 123#define CLUSTERS_PER_FAT_SECTOR (SECTOR_SIZE / 4)
115#define CLUSTERS_PER_FAT16_SECTOR (SECTOR_SIZE / 2) 124#define CLUSTERS_PER_FAT16_SECTOR (SECTOR_SIZE / 2)
@@ -1173,7 +1182,7 @@ static int write_long_name(struct fat_file* file,
1173 entry[FATLONG_ORDER] = numentries-i-1; 1182 entry[FATLONG_ORDER] = numentries-i-1;
1174 if (i==0) { 1183 if (i==0) {
1175 /* mark this as last long entry */ 1184 /* mark this as last long entry */
1176 entry[FATLONG_ORDER] |= 0x40; 1185 entry[FATLONG_ORDER] |= FATLONG_LAST_LONG_ENTRY;
1177 1186
1178 /* pad name with 0xffff */ 1187 /* pad name with 0xffff */
1179 for (k=1; k<11; k++) entry[k] = FAT_LONGNAME_PAD_BYTE; 1188 for (k=1; k<11; k++) entry[k] = FAT_LONGNAME_PAD_BYTE;
@@ -2323,47 +2332,20 @@ int fat_opendir(IF_MV2(int volume,)
2323 return 0; 2332 return 0;
2324} 2333}
2325 2334
2326/* Copies a segment of long file name (UTF-16 LE encoded) to the
2327 * destination buffer (UTF-8 encoded). Copying is stopped when
2328 * either 0x0000 or 0xffff (FAT pad char) is encountered.
2329 * Trailing \0 is also appended at the end of the UTF8-encoded
2330 * string.
2331 *
2332 * utf16src utf16 (little endian) segment to copy
2333 * utf16count max number of the utf16-characters to copy
2334 * utf8dst where to write UTF8-encoded string to
2335 *
2336 * returns the number of UTF-16 characters actually copied
2337 */
2338static int fat_copy_long_name_segment(unsigned char *utf16src,
2339 int utf16count, unsigned char *utf8dst) {
2340 int cnt = 0;
2341 while ((utf16count--) > 0) {
2342 unsigned short ucs = utf16src[0] | (utf16src[1] << 8);
2343 if ((ucs == 0) || (ucs == FAT_LONGNAME_PAD_UCS)) {
2344 break;
2345 }
2346 utf8dst = utf8encode(ucs, utf8dst);
2347 utf16src += 2;
2348 cnt++;
2349 }
2350 *utf8dst = 0;
2351 return cnt;
2352}
2353
2354int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry) 2335int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
2355{ 2336{
2356 bool done = false; 2337 bool done = false;
2357 int i; 2338 int i, j;
2358 int rc; 2339 int rc;
2340 int order;
2359 unsigned char firstbyte; 2341 unsigned char firstbyte;
2360 /* Long file names are stored in special entries. Each entry holds 2342 /* Long file names are stored in special entries. Each entry holds
2361 up to 13 characters. Names can be max 255 chars (not bytes!) long 2343 up to 13 characters. Names can be max 255 chars (not bytes!) long */
2362 hence max 20 entries are required. */ 2344 /* The number of long entries in the long name can be retrieve from the first
2363 int longarray[20]; 2345 * long entry because there are stored in reverse order and have an ordinal */
2364 int longs=0; 2346 int nb_longs = 0;
2365 int sectoridx=0; 2347 /* The long entries are expected to be in order, so remember the last ordinal */
2366 unsigned char* cached_buf = dir->sectorcache[0]; 2348 int last_long_ord = 0;
2367 2349
2368 dir->entrycount = 0; 2350 dir->entrycount = 0;
2369 2351
@@ -2371,7 +2353,7 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
2371 { 2353 {
2372 if ( !(dir->entry % DIR_ENTRIES_PER_SECTOR) || !dir->sector ) 2354 if ( !(dir->entry % DIR_ENTRIES_PER_SECTOR) || !dir->sector )
2373 { 2355 {
2374 rc = fat_readwrite(&dir->file, 1, cached_buf, false); 2356 rc = fat_readwrite(&dir->file, 1, dir->sectorcache, false);
2375 if (rc == 0) { 2357 if (rc == 0) {
2376 /* eof */ 2358 /* eof */
2377 entry->name[0] = 0; 2359 entry->name[0] = 0;
@@ -2386,16 +2368,14 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
2386 } 2368 }
2387 2369
2388 for (i = dir->entry % DIR_ENTRIES_PER_SECTOR; 2370 for (i = dir->entry % DIR_ENTRIES_PER_SECTOR;
2389 i < DIR_ENTRIES_PER_SECTOR; i++) 2371 i < DIR_ENTRIES_PER_SECTOR; i++) {
2390 {
2391 unsigned int entrypos = i * DIR_ENTRY_SIZE; 2372 unsigned int entrypos = i * DIR_ENTRY_SIZE;
2392 2373
2393 firstbyte = cached_buf[entrypos]; 2374 firstbyte = dir->sectorcache[entrypos];
2394 dir->entry++; 2375 dir->entry++;
2395 2376
2396 if (firstbyte == 0xe5) { 2377 if (firstbyte == 0xe5) {
2397 /* free entry */ 2378 /* free entry */
2398 sectoridx = 0;
2399 dir->entrycount = 0; 2379 dir->entrycount = 0;
2400 continue; 2380 continue;
2401 } 2381 }
@@ -2409,14 +2389,48 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
2409 2389
2410 dir->entrycount++; 2390 dir->entrycount++;
2411 2391
2412 /* longname entry? */ 2392 /* LFN entry? */
2413 if ( ( cached_buf[entrypos + FATDIR_ATTR] & 2393 if ( ( dir->sectorcache[entrypos + FATDIR_ATTR] &
2414 FAT_ATTR_LONG_NAME_MASK ) == FAT_ATTR_LONG_NAME ) { 2394 FAT_ATTR_LONG_NAME_MASK ) == FAT_ATTR_LONG_NAME ) {
2415 longarray[longs++] = entrypos + sectoridx; 2395 /* extract ordinal */
2396 order = dir->sectorcache[entrypos + FATLONG_ORDER] & ~FATLONG_LAST_LONG_ENTRY;
2397 /* is this entry the first long entry ? (first in order but containing last part) */
2398 if (dir->sectorcache[entrypos + FATLONG_ORDER] & FATLONG_LAST_LONG_ENTRY) {
2399 /* check that order is not too big ! (and non-zero) */
2400 if(order <= 0 || order > FATLONG_MAX_ORDER)
2401 continue; /* ignore the whole LFN, will trigger lots of warnings */
2402 nb_longs = order;
2403 last_long_ord = order;
2404 }
2405 else {
2406 /* check orphan entry */
2407 if (nb_longs == 0) {
2408 logf("fat warning: orphan LFN entry");
2409 /* ignore */
2410 continue;
2411 }
2412
2413 /* check order */
2414 if (order != (last_long_ord - 1)) {
2415 logf("fat warning: wrong LFN ordinal");
2416 /* ignore the whole LFN, will trigger lots of warnings */
2417 nb_longs = 0;
2418 }
2419
2420 last_long_ord = order;
2421 }
2422
2423 /* copy part, reuse [order] for another purpose :) */
2424 order = (order - 1) * FATLONG_NAME_BYTES_PER_ENTRY;
2425 for(j = 0; j < FATLONG_NAME_CHUNKS; j++) {
2426 memcpy(dir->longname + order,
2427 dir->sectorcache + entrypos + FATLONG_NAME_POS[j],
2428 FATLONG_NAME_SIZE[j]);
2429 order += FATLONG_NAME_SIZE[j];
2430 }
2416 } 2431 }
2417 else { 2432 else {
2418 if ( parse_direntry(entry, 2433 if ( parse_direntry(entry, dir->sectorcache + entrypos) ) {
2419 &cached_buf[entrypos]) ) {
2420 2434
2421 /* don't return volume id entry */ 2435 /* don't return volume id entry */
2422 if ( (entry->attr & 2436 if ( (entry->attr &
@@ -2425,74 +2439,45 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
2425 continue; 2439 continue;
2426 2440
2427 /* replace shortname with longname? */ 2441 /* replace shortname with longname? */
2428 if ( longs ) { 2442 /* check that the long name is complete */
2429 int j; 2443 if (nb_longs != 0 && last_long_ord == 1) {
2430 /* This should be enough to hold any name segment 2444 /* hold a copy of the shortname in case the long one is too long */
2431 utf8-encoded */
2432 unsigned char shortname[13]; /* 8+3+dot+\0 */ 2445 unsigned char shortname[13]; /* 8+3+dot+\0 */
2433 /* Add 1 for trailing \0 */
2434 unsigned char longname_utf8segm[6*4 + 1];
2435 int longname_utf8len = 0; 2446 int longname_utf8len = 0;
2436 /* Temporarily store it */ 2447 /* One character at a time, add 1 for trailing \0, 4 is the maximum size
2448 * of a UTF8 encoded character in rockbox */
2449 unsigned char longname_utf8segm[4 + 1];
2450 unsigned short ucs;
2451 int segm_utf8len;
2452 /* Temporarily store short name */
2437 strcpy(shortname, entry->name); 2453 strcpy(shortname, entry->name);
2438 entry->name[0] = 0; 2454 entry->name[0] = 0;
2439 2455
2440 /* iterate backwards through the dir entries */ 2456 /* Convert the FAT name to a utf8-encoded one.
2441 for (j=longs-1; j>=0; j--) { 2457 * The name is not necessary NUL-terminated ! */
2442 unsigned char* ptr = cached_buf; 2458 for (j = 0; j < nb_longs * FATLONG_NAME_BYTES_PER_ENTRY; j += 2) {
2443 int index = longarray[j]; 2459 ucs = dir->longname[j] | (dir->longname[j + 1] << 8);
2444 /* current or cached sector? */ 2460 if(ucs == 0 || ucs == FAT_LONGNAME_PAD_UCS)
2445 if ( sectoridx >= SECTOR_SIZE ) {
2446 if ( sectoridx >= SECTOR_SIZE*2 ) {
2447 if ( ( index >= SECTOR_SIZE ) &&
2448 ( index < SECTOR_SIZE*2 ))
2449 ptr = dir->sectorcache[1];
2450 else
2451 ptr = dir->sectorcache[2];
2452 }
2453 else {
2454 if ( index < SECTOR_SIZE )
2455 ptr = dir->sectorcache[1];
2456 }
2457
2458 index &= SECTOR_SIZE-1;
2459 }
2460
2461 /* Try to append each segment of the long name.
2462 Check if we'd exceed the buffer.
2463 Also check for FAT padding characters 0xFFFF. */
2464 if (fat_copy_long_name_segment(ptr + index + 1, 5,
2465 longname_utf8segm) == 0) break;
2466 /* logf("SG: %s, EN: %s", longname_utf8segm,
2467 entry->name); */
2468 longname_utf8len += strlen(longname_utf8segm);
2469 if (longname_utf8len < FAT_FILENAME_BYTES)
2470 strcat(entry->name, longname_utf8segm);
2471 else
2472 break;
2473
2474 if (fat_copy_long_name_segment(ptr + index + 14, 6,
2475 longname_utf8segm) == 0) break;
2476 /* logf("SG: %s, EN: %s", longname_utf8segm,
2477 entry->name); */
2478 longname_utf8len += strlen(longname_utf8segm);
2479 if (longname_utf8len < FAT_FILENAME_BYTES)
2480 strcat(entry->name, longname_utf8segm);
2481 else
2482 break;
2483
2484 if (fat_copy_long_name_segment(ptr + index + 28, 2,
2485 longname_utf8segm) == 0) break;
2486 /* logf("SG: %s, EN: %s", longname_utf8segm,
2487 entry->name); */
2488 longname_utf8len += strlen(longname_utf8segm);
2489 if (longname_utf8len < FAT_FILENAME_BYTES)
2490 strcat(entry->name, longname_utf8segm);
2491 else
2492 break; 2461 break;
2462 /* utf8encode will return a pointer after the converted
2463 * string, subtract the pointer to the start to get the length of it */
2464 segm_utf8len = utf8encode(ucs, longname_utf8segm) - longname_utf8segm;
2465
2466 /* warn the trailing zero ! (FAT_FILENAME_BYTES includes it) */
2467 if (longname_utf8len + segm_utf8len >= FAT_FILENAME_BYTES) {
2468 /* force use of short name */
2469 longname_utf8len = FAT_FILENAME_BYTES + 1;
2470 break; /* fallback later */
2471 }
2472 else {
2473 longname_utf8segm[segm_utf8len] = 0;
2474 strcat(entry->name + longname_utf8len, longname_utf8segm);
2475 longname_utf8len += segm_utf8len;
2476 }
2493 } 2477 }
2494 2478
2495 /* Does the utf8-encoded name fit into the entry? */ 2479 /* Does the utf8-encoded name fit into the entry? */
2480 /* warn the trailing zero ! (FAT_FILENAME_BYTES includes it) */
2496 if (longname_utf8len >= FAT_FILENAME_BYTES) { 2481 if (longname_utf8len >= FAT_FILENAME_BYTES) {
2497 /* Take the short DOS name. Need to utf8-encode it 2482 /* Take the short DOS name. Need to utf8-encode it
2498 since it may contain chars from the upper half of 2483 since it may contain chars from the upper half of
@@ -2504,29 +2489,19 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
2504 unsigned char *utf8; 2489 unsigned char *utf8;
2505 utf8 = iso_decode(shortname, entry->name, -1, 2490 utf8 = iso_decode(shortname, entry->name, -1,
2506 strlen(shortname)); 2491 strlen(shortname));
2507 *utf8 = 0; 2492 *utf8 = 0;
2508 logf("SN: %s", entry->name); 2493 logf("SN: %s", entry->name);
2509 } else { 2494 } else {
2510 /* logf("LN: %s", entry->name); 2495 logf("LN: %s", entry->name);
2511 logf("LNLen: %d (%c)", longname_utf8len, 2496 logf("LNLen: %d", longname_utf8len);
2512 entry->name[0]); */
2513 } 2497 }
2514 } 2498 }
2515 done = true; 2499 done = true;
2516 sectoridx = 0;
2517 i++; 2500 i++;
2518 break; 2501 break;
2519 } 2502 }
2520 } 2503 }
2521 } 2504 }
2522
2523 /* save this sector, for longname use */
2524 if ( sectoridx )
2525 memcpy( dir->sectorcache[2], dir->sectorcache[0], SECTOR_SIZE );
2526 else
2527 memcpy( dir->sectorcache[1], dir->sectorcache[0], SECTOR_SIZE );
2528 sectoridx += SECTOR_SIZE;
2529
2530 } 2505 }
2531 return 0; 2506 return 0;
2532} 2507}
diff --git a/firmware/export/fat.h b/firmware/export/fat.h
index 638a659f7a..5df5dc4b91 100644
--- a/firmware/export/fat.h
+++ b/firmware/export/fat.h
@@ -85,7 +85,12 @@ struct fat_dir
85 unsigned int entrycount; 85 unsigned int entrycount;
86 long sector; 86 long sector;
87 struct fat_file file; 87 struct fat_file file;
88 unsigned char sectorcache[3][SECTOR_SIZE]; 88 unsigned char sectorcache[SECTOR_SIZE];
89 /* There are 2-bytes per characters. We don't want to bother too much, as LFN entries are
90 * at much 255 characters longs, that's at most 20 LFN entries. Each entry hold at most
91 * 13 characters, that a total of 260 characters. So we keep a buffer of that size.
92 * Keep coherent with fat.c code. */
93 unsigned char longname[260 * 2];
89}; 94};
90 95
91#ifdef HAVE_HOTSWAP 96#ifdef HAVE_HOTSWAP