diff options
Diffstat (limited to 'firmware/drivers')
-rw-r--r-- | firmware/drivers/fat.c | 106 |
1 files changed, 93 insertions, 13 deletions
diff --git a/firmware/drivers/fat.c b/firmware/drivers/fat.c index 854b815237..70a896442c 100644 --- a/firmware/drivers/fat.c +++ b/firmware/drivers/fat.c | |||
@@ -29,12 +29,13 @@ | |||
29 | #include "timefuncs.h" | 29 | #include "timefuncs.h" |
30 | #include "kernel.h" | 30 | #include "kernel.h" |
31 | #include "rbunicode.h" | 31 | #include "rbunicode.h" |
32 | #include "logf.h" | ||
32 | 33 | ||
33 | #define BYTES2INT16(array,pos) \ | 34 | #define BYTES2INT16(array,pos) \ |
34 | (array[pos] | (array[pos+1] << 8 )) | 35 | (array[pos] | (array[pos+1] << 8 )) |
35 | #define BYTES2INT32(array,pos) \ | 36 | #define BYTES2INT32(array,pos) \ |
36 | ((long)array[pos] | ((long)array[pos+1] << 8 ) | \ | 37 | ((long)array[pos] | ((long)array[pos+1] << 8 ) | \ |
37 | ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 )) | 38 | ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 )) |
38 | 39 | ||
39 | #define FATTYPE_FAT12 0 | 40 | #define FATTYPE_FAT12 0 |
40 | #define FATTYPE_FAT16 1 | 41 | #define FATTYPE_FAT16 1 |
@@ -115,6 +116,8 @@ | |||
115 | #define NAME_BYTES_PER_ENTRY 13 | 116 | #define NAME_BYTES_PER_ENTRY 13 |
116 | #define FAT_BAD_MARK 0x0ffffff7 | 117 | #define FAT_BAD_MARK 0x0ffffff7 |
117 | #define FAT_EOF_MARK 0x0ffffff8 | 118 | #define FAT_EOF_MARK 0x0ffffff8 |
119 | #define FAT_LONGNAME_PAD_BYTE 0xff | ||
120 | #define FAT_LONGNAME_PAD_UCS 0xffff | ||
118 | 121 | ||
119 | struct fsinfo { | 122 | struct fsinfo { |
120 | unsigned long freecount; /* last known free cluster count */ | 123 | unsigned long freecount; /* last known free cluster count */ |
@@ -720,9 +723,7 @@ static unsigned long find_free_cluster(IF_MV2(struct bpb* fat_bpb,) unsigned lon | |||
720 | return 0; /* 0 is an illegal cluster number */ | 723 | return 0; /* 0 is an illegal cluster number */ |
721 | } | 724 | } |
722 | 725 | ||
723 | static int update_fat_entry(IF_MV2(struct bpb* fat_bpb,) | 726 | static int update_fat_entry(IF_MV2(struct bpb* fat_bpb,) unsigned long entry, unsigned long val) |
724 | unsigned long entry, | ||
725 | unsigned long val) | ||
726 | { | 727 | { |
727 | #ifndef HAVE_MULTIVOLUME | 728 | #ifndef HAVE_MULTIVOLUME |
728 | struct bpb* fat_bpb = &fat_bpbs[0]; | 729 | struct bpb* fat_bpb = &fat_bpbs[0]; |
@@ -1117,9 +1118,9 @@ static int write_long_name(struct fat_file* file, | |||
1117 | entry[FATLONG_ORDER] |= 0x40; | 1118 | entry[FATLONG_ORDER] |= 0x40; |
1118 | 1119 | ||
1119 | /* pad name with 0xffff */ | 1120 | /* pad name with 0xffff */ |
1120 | for (k=1; k<11; k++) entry[k] = 0xff; | 1121 | for (k=1; k<11; k++) entry[k] = FAT_LONGNAME_PAD_BYTE; |
1121 | for (k=14; k<26; k++) entry[k] = 0xff; | 1122 | for (k=14; k<26; k++) entry[k] = FAT_LONGNAME_PAD_BYTE; |
1122 | for (k=28; k<32; k++) entry[k] = 0xff; | 1123 | for (k=28; k<32; k++) entry[k] = FAT_LONGNAME_PAD_BYTE; |
1123 | }; | 1124 | }; |
1124 | /* set name */ | 1125 | /* set name */ |
1125 | for (k=0; k<5 && l <= namelen; k++) { | 1126 | for (k=0; k<5 && l <= namelen; k++) { |
@@ -2165,12 +2166,43 @@ int fat_opendir(IF_MV2(int volume,) | |||
2165 | return 0; | 2166 | return 0; |
2166 | } | 2167 | } |
2167 | 2168 | ||
2169 | /* Copies a segment of long file name (UTF-16 LE encoded) to the | ||
2170 | * destination buffer (UTF-8 encoded). Copying is stopped when | ||
2171 | * either 0x0000 or 0xffff (FAT pad char) is encountered. | ||
2172 | * Trailing \0 is also appended at the end of the UTF8-encoded | ||
2173 | * string. | ||
2174 | * | ||
2175 | * utf16src utf16 (little endian) segment to copy | ||
2176 | * utf16count max number of the utf16-characters to copy | ||
2177 | * utf8dst where to write UTF8-encoded string to | ||
2178 | * | ||
2179 | * returns the number of UTF-16 characters actually copied | ||
2180 | */ | ||
2181 | static int fat_copy_long_name_segment(unsigned char *utf16src, | ||
2182 | int utf16count, unsigned char *utf8dst) { | ||
2183 | int cnt = 0; | ||
2184 | while ((utf16count--) > 0) { | ||
2185 | unsigned short ucs = utf16src[0] | (utf16src[1] << 8); | ||
2186 | if ((ucs == 0) || (ucs == FAT_LONGNAME_PAD_UCS)) { | ||
2187 | break; | ||
2188 | } | ||
2189 | utf8dst = utf8encode(ucs, utf8dst); | ||
2190 | utf16src += 2; | ||
2191 | cnt++; | ||
2192 | } | ||
2193 | *utf8dst = 0; | ||
2194 | return cnt; | ||
2195 | } | ||
2196 | |||
2168 | int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry) | 2197 | int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry) |
2169 | { | 2198 | { |
2170 | bool done = false; | 2199 | bool done = false; |
2171 | int i; | 2200 | int i; |
2172 | int rc; | 2201 | int rc; |
2173 | unsigned char firstbyte; | 2202 | unsigned char firstbyte; |
2203 | /* Long file names are stored in special entries. Each entry holds | ||
2204 | up to 13 characters. Names can be max 255 chars (not bytes!) long | ||
2205 | hence max 20 entries are required. */ | ||
2174 | int longarray[20]; | 2206 | int longarray[20]; |
2175 | int longs=0; | 2207 | int longs=0; |
2176 | int sectoridx=0; | 2208 | int sectoridx=0; |
@@ -2238,7 +2270,14 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry) | |||
2238 | /* replace shortname with longname? */ | 2270 | /* replace shortname with longname? */ |
2239 | if ( longs ) { | 2271 | if ( longs ) { |
2240 | int j; | 2272 | int j; |
2241 | unsigned char* utf8 = entry->name; | 2273 | /* This should be enough to hold any name segment utf8-encoded */ |
2274 | unsigned char shortname[13]; /* 8+3+dot+\0 */ | ||
2275 | unsigned char longname_utf8segm[6*4 + 1]; /* Add 1 for trailing \0 */ | ||
2276 | int longname_utf8len = 0; | ||
2277 | |||
2278 | strcpy(shortname, entry->name); /* Temporarily store it */ | ||
2279 | entry->name[0] = 0; | ||
2280 | |||
2242 | /* iterate backwards through the dir entries */ | 2281 | /* iterate backwards through the dir entries */ |
2243 | for (j=longs-1; j>=0; j--) { | 2282 | for (j=longs-1; j>=0; j--) { |
2244 | unsigned char* ptr = cached_buf; | 2283 | unsigned char* ptr = cached_buf; |
@@ -2260,11 +2299,52 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry) | |||
2260 | index &= SECTOR_SIZE-1; | 2299 | index &= SECTOR_SIZE-1; |
2261 | } | 2300 | } |
2262 | 2301 | ||
2263 | utf8 = utf16LEdecode(ptr + index + 1, utf8, 5); | 2302 | /* Try to append each segment of the long name. Check if we'd |
2264 | utf8 = utf16LEdecode(ptr + index + 14, utf8, 6); | 2303 | exceed the buffer. Also check for FAT padding characters 0xFFFF. */ |
2265 | utf8 = utf16LEdecode(ptr + index + 28, utf8, 2); | 2304 | if (fat_copy_long_name_segment(ptr + index + 1, 5, |
2305 | longname_utf8segm) == 0) break; | ||
2306 | logf("SG: %s, EN: %s", longname_utf8segm, entry->name); | ||
2307 | longname_utf8len += strlen(longname_utf8segm); | ||
2308 | if (longname_utf8len < FAT_FILENAME_BYTES) | ||
2309 | strcat(entry->name, longname_utf8segm); | ||
2310 | else | ||
2311 | break; | ||
2312 | |||
2313 | if (fat_copy_long_name_segment(ptr + index + 14, 6, | ||
2314 | longname_utf8segm) == 0) break; | ||
2315 | logf("SG: %s, EN: %s", longname_utf8segm, entry->name); | ||
2316 | longname_utf8len += strlen(longname_utf8segm); | ||
2317 | if (longname_utf8len < FAT_FILENAME_BYTES) | ||
2318 | strcat(entry->name, longname_utf8segm); | ||
2319 | else | ||
2320 | break; | ||
2321 | |||
2322 | if (fat_copy_long_name_segment(ptr + index + 28, 2, | ||
2323 | longname_utf8segm) == 0) break; | ||
2324 | logf("SG: %s, EN: %s", longname_utf8segm, entry->name); | ||
2325 | longname_utf8len += strlen(longname_utf8segm); | ||
2326 | if (longname_utf8len < FAT_FILENAME_BYTES) | ||
2327 | strcat(entry->name, longname_utf8segm); | ||
2328 | else | ||
2329 | break; | ||
2266 | } | 2330 | } |
2331 | |||
2332 | /* Does the utf8-encoded name fit into the entry? */ | ||
2333 | if (longname_utf8len >= FAT_FILENAME_BYTES) { | ||
2334 | /* Take the short DOS name. Need to utf8-encode it since | ||
2335 | it may contain chars from the upper half of the OEM | ||
2336 | code page which wouldn't be a valid utf8. Beware: this | ||
2337 | file will be shown with strange glyphs in file browser | ||
2338 | since unicode 0x80 to 0x9F are control characters. */ | ||
2339 | logf("SN-DOS: %s", shortname); | ||
2340 | unsigned char *utf8; | ||
2341 | utf8 = iso_decode(shortname, entry->name, -1, strlen(shortname)); | ||
2267 | *utf8 = 0; | 2342 | *utf8 = 0; |
2343 | logf("SN: %s", entry->name); | ||
2344 | } else { | ||
2345 | logf("LN: %s", entry->name); | ||
2346 | logf("LNLen: %d (%c)", longname_utf8len, entry->name[0]); | ||
2347 | } | ||
2268 | } | 2348 | } |
2269 | done = true; | 2349 | done = true; |
2270 | sectoridx = 0; | 2350 | sectoridx = 0; |