FS#11118: reduce the number of cached sector in FAT code because some are useless after a rewrite of LFN entries handling. Also makes LFN handling more robust.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25290 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Amaury Pouly 2010-03-22 10:35:25 +00:00
parent 7a3822c8b0
commit a8137252a5
2 changed files with 98 additions and 118 deletions

View file

@ -31,6 +31,7 @@
#include "timefuncs.h"
#include "kernel.h"
#include "rbunicode.h"
/*#define LOGF_ENABLE*/
#include "logf.h"
#define BYTES2INT16(array,pos) \
@ -110,6 +111,14 @@
#define FATLONG_ORDER 0
#define FATLONG_TYPE 12
#define FATLONG_CHKSUM 13
#define FATLONG_LAST_LONG_ENTRY 0x40
#define FATLONG_NAME_BYTES_PER_ENTRY 26
/* at most 20 LFN entries, keep coherent with fat_dir->longname size ! */
#define FATLONG_MAX_ORDER 20
#define FATLONG_NAME_CHUNKS 3
static unsigned char FATLONG_NAME_POS[FATLONG_NAME_CHUNKS] = {1, 14, 28};
static unsigned char FATLONG_NAME_SIZE[FATLONG_NAME_CHUNKS] = {10, 12, 4};
#define CLUSTERS_PER_FAT_SECTOR (SECTOR_SIZE / 4)
#define CLUSTERS_PER_FAT16_SECTOR (SECTOR_SIZE / 2)
@ -1173,7 +1182,7 @@ static int write_long_name(struct fat_file* file,
entry[FATLONG_ORDER] = numentries-i-1;
if (i==0) {
/* mark this as last long entry */
entry[FATLONG_ORDER] |= 0x40;
entry[FATLONG_ORDER] |= FATLONG_LAST_LONG_ENTRY;
/* pad name with 0xffff */
for (k=1; k<11; k++) entry[k] = FAT_LONGNAME_PAD_BYTE;
@ -2323,47 +2332,20 @@ int fat_opendir(IF_MV2(int volume,)
return 0;
}
/* Copies a segment of long file name (UTF-16 LE encoded) to the
* destination buffer (UTF-8 encoded). Copying is stopped when
* either 0x0000 or 0xffff (FAT pad char) is encountered.
* Trailing \0 is also appended at the end of the UTF8-encoded
* string.
*
* utf16src utf16 (little endian) segment to copy
* utf16count max number of the utf16-characters to copy
* utf8dst where to write UTF8-encoded string to
*
* returns the number of UTF-16 characters actually copied
*/
static int fat_copy_long_name_segment(unsigned char *utf16src,
int utf16count, unsigned char *utf8dst) {
int cnt = 0;
while ((utf16count--) > 0) {
unsigned short ucs = utf16src[0] | (utf16src[1] << 8);
if ((ucs == 0) || (ucs == FAT_LONGNAME_PAD_UCS)) {
break;
}
utf8dst = utf8encode(ucs, utf8dst);
utf16src += 2;
cnt++;
}
*utf8dst = 0;
return cnt;
}
int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
{
bool done = false;
int i;
int i, j;
int rc;
int order;
unsigned char firstbyte;
/* Long file names are stored in special entries. Each entry holds
up to 13 characters. Names can be max 255 chars (not bytes!) long
hence max 20 entries are required. */
int longarray[20];
int longs=0;
int sectoridx=0;
unsigned char* cached_buf = dir->sectorcache[0];
up to 13 characters. Names can be max 255 chars (not bytes!) long */
/* The number of long entries in the long name can be retrieve from the first
* long entry because there are stored in reverse order and have an ordinal */
int nb_longs = 0;
/* The long entries are expected to be in order, so remember the last ordinal */
int last_long_ord = 0;
dir->entrycount = 0;
@ -2371,7 +2353,7 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
{
if ( !(dir->entry % DIR_ENTRIES_PER_SECTOR) || !dir->sector )
{
rc = fat_readwrite(&dir->file, 1, cached_buf, false);
rc = fat_readwrite(&dir->file, 1, dir->sectorcache, false);
if (rc == 0) {
/* eof */
entry->name[0] = 0;
@ -2386,16 +2368,14 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
}
for (i = dir->entry % DIR_ENTRIES_PER_SECTOR;
i < DIR_ENTRIES_PER_SECTOR; i++)
{
i < DIR_ENTRIES_PER_SECTOR; i++) {
unsigned int entrypos = i * DIR_ENTRY_SIZE;
firstbyte = cached_buf[entrypos];
firstbyte = dir->sectorcache[entrypos];
dir->entry++;
if (firstbyte == 0xe5) {
/* free entry */
sectoridx = 0;
dir->entrycount = 0;
continue;
}
@ -2409,14 +2389,48 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
dir->entrycount++;
/* longname entry? */
if ( ( cached_buf[entrypos + FATDIR_ATTR] &
/* LFN entry? */
if ( ( dir->sectorcache[entrypos + FATDIR_ATTR] &
FAT_ATTR_LONG_NAME_MASK ) == FAT_ATTR_LONG_NAME ) {
longarray[longs++] = entrypos + sectoridx;
/* extract ordinal */
order = dir->sectorcache[entrypos + FATLONG_ORDER] & ~FATLONG_LAST_LONG_ENTRY;
/* is this entry the first long entry ? (first in order but containing last part) */
if (dir->sectorcache[entrypos + FATLONG_ORDER] & FATLONG_LAST_LONG_ENTRY) {
/* check that order is not too big ! (and non-zero) */
if(order <= 0 || order > FATLONG_MAX_ORDER)
continue; /* ignore the whole LFN, will trigger lots of warnings */
nb_longs = order;
last_long_ord = order;
}
else {
/* check orphan entry */
if (nb_longs == 0) {
logf("fat warning: orphan LFN entry");
/* ignore */
continue;
}
/* check order */
if (order != (last_long_ord - 1)) {
logf("fat warning: wrong LFN ordinal");
/* ignore the whole LFN, will trigger lots of warnings */
nb_longs = 0;
}
last_long_ord = order;
}
/* copy part, reuse [order] for another purpose :) */
order = (order - 1) * FATLONG_NAME_BYTES_PER_ENTRY;
for(j = 0; j < FATLONG_NAME_CHUNKS; j++) {
memcpy(dir->longname + order,
dir->sectorcache + entrypos + FATLONG_NAME_POS[j],
FATLONG_NAME_SIZE[j]);
order += FATLONG_NAME_SIZE[j];
}
}
else {
if ( parse_direntry(entry,
&cached_buf[entrypos]) ) {
if ( parse_direntry(entry, dir->sectorcache + entrypos) ) {
/* don't return volume id entry */
if ( (entry->attr &
@ -2425,74 +2439,45 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
continue;
/* replace shortname with longname? */
if ( longs ) {
int j;
/* This should be enough to hold any name segment
utf8-encoded */
/* check that the long name is complete */
if (nb_longs != 0 && last_long_ord == 1) {
/* hold a copy of the shortname in case the long one is too long */
unsigned char shortname[13]; /* 8+3+dot+\0 */
/* Add 1 for trailing \0 */
unsigned char longname_utf8segm[6*4 + 1];
int longname_utf8len = 0;
/* Temporarily store it */
/* One character at a time, add 1 for trailing \0, 4 is the maximum size
* of a UTF8 encoded character in rockbox */
unsigned char longname_utf8segm[4 + 1];
unsigned short ucs;
int segm_utf8len;
/* Temporarily store short name */
strcpy(shortname, entry->name);
entry->name[0] = 0;
/* iterate backwards through the dir entries */
for (j=longs-1; j>=0; j--) {
unsigned char* ptr = cached_buf;
int index = longarray[j];
/* current or cached sector? */
if ( sectoridx >= SECTOR_SIZE ) {
if ( sectoridx >= SECTOR_SIZE*2 ) {
if ( ( index >= SECTOR_SIZE ) &&
( index < SECTOR_SIZE*2 ))
ptr = dir->sectorcache[1];
else
ptr = dir->sectorcache[2];
}
else {
if ( index < SECTOR_SIZE )
ptr = dir->sectorcache[1];
}
/* Convert the FAT name to a utf8-encoded one.
* The name is not necessary NUL-terminated ! */
for (j = 0; j < nb_longs * FATLONG_NAME_BYTES_PER_ENTRY; j += 2) {
ucs = dir->longname[j] | (dir->longname[j + 1] << 8);
if(ucs == 0 || ucs == FAT_LONGNAME_PAD_UCS)
break;
/* utf8encode will return a pointer after the converted
* string, subtract the pointer to the start to get the length of it */
segm_utf8len = utf8encode(ucs, longname_utf8segm) - longname_utf8segm;
index &= SECTOR_SIZE-1;
/* warn the trailing zero ! (FAT_FILENAME_BYTES includes it) */
if (longname_utf8len + segm_utf8len >= FAT_FILENAME_BYTES) {
/* force use of short name */
longname_utf8len = FAT_FILENAME_BYTES + 1;
break; /* fallback later */
}
else {
longname_utf8segm[segm_utf8len] = 0;
strcat(entry->name + longname_utf8len, longname_utf8segm);
longname_utf8len += segm_utf8len;
}
/* Try to append each segment of the long name.
Check if we'd exceed the buffer.
Also check for FAT padding characters 0xFFFF. */
if (fat_copy_long_name_segment(ptr + index + 1, 5,
longname_utf8segm) == 0) break;
/* logf("SG: %s, EN: %s", longname_utf8segm,
entry->name); */
longname_utf8len += strlen(longname_utf8segm);
if (longname_utf8len < FAT_FILENAME_BYTES)
strcat(entry->name, longname_utf8segm);
else
break;
if (fat_copy_long_name_segment(ptr + index + 14, 6,
longname_utf8segm) == 0) break;
/* logf("SG: %s, EN: %s", longname_utf8segm,
entry->name); */
longname_utf8len += strlen(longname_utf8segm);
if (longname_utf8len < FAT_FILENAME_BYTES)
strcat(entry->name, longname_utf8segm);
else
break;
if (fat_copy_long_name_segment(ptr + index + 28, 2,
longname_utf8segm) == 0) break;
/* logf("SG: %s, EN: %s", longname_utf8segm,
entry->name); */
longname_utf8len += strlen(longname_utf8segm);
if (longname_utf8len < FAT_FILENAME_BYTES)
strcat(entry->name, longname_utf8segm);
else
break;
}
/* Does the utf8-encoded name fit into the entry? */
/* warn the trailing zero ! (FAT_FILENAME_BYTES includes it) */
if (longname_utf8len >= FAT_FILENAME_BYTES) {
/* Take the short DOS name. Need to utf8-encode it
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)
unsigned char *utf8;
utf8 = iso_decode(shortname, entry->name, -1,
strlen(shortname));
*utf8 = 0;
*utf8 = 0;
logf("SN: %s", entry->name);
} else {
/* logf("LN: %s", entry->name);
logf("LNLen: %d (%c)", longname_utf8len,
entry->name[0]); */
logf("LN: %s", entry->name);
logf("LNLen: %d", longname_utf8len);
}
}
done = true;
sectoridx = 0;
i++;
break;
}
}
}
/* save this sector, for longname use */
if ( sectoridx )
memcpy( dir->sectorcache[2], dir->sectorcache[0], SECTOR_SIZE );
else
memcpy( dir->sectorcache[1], dir->sectorcache[0], SECTOR_SIZE );
sectoridx += SECTOR_SIZE;
}
return 0;
}

View file

@ -85,7 +85,12 @@ struct fat_dir
unsigned int entrycount;
long sector;
struct fat_file file;
unsigned char sectorcache[3][SECTOR_SIZE];
unsigned char sectorcache[SECTOR_SIZE];
/* There are 2-bytes per characters. We don't want to bother too much, as LFN entries are
* at much 255 characters longs, that's at most 20 LFN entries. Each entry hold at most
* 13 characters, that a total of 260 characters. So we keep a buffer of that size.
* Keep coherent with fat.c code. */
unsigned char longname[260 * 2];
};
#ifdef HAVE_HOTSWAP