summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/CREDITS1
-rw-r--r--firmware/drivers/fat.c106
-rw-r--r--firmware/export/fat.h10
3 files changed, 103 insertions, 14 deletions
diff --git a/docs/CREDITS b/docs/CREDITS
index d54d548121..bcd791895b 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -219,3 +219,4 @@ Plácido Revilla
219Michael Sevakis 219Michael Sevakis
220Lukas Sabota 220Lukas Sabota
221Emanuel Zephir 221Emanuel Zephir
222Alexander Levin
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
119struct fsinfo { 122struct 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
723static int update_fat_entry(IF_MV2(struct bpb* fat_bpb,) 726static 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 */
2181static 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
2168int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry) 2197int 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;
diff --git a/firmware/export/fat.h b/firmware/export/fat.h
index c56d8f85f6..4cdc479844 100644
--- a/firmware/export/fat.h
+++ b/firmware/export/fat.h
@@ -25,9 +25,17 @@
25 25
26#define SECTOR_SIZE 512 26#define SECTOR_SIZE 512
27 27
28/* Number of bytes reserved for a file name (including the trailing \0).
29 Since names are stored in the entry as UTF-8, we won't be able to
30 store all names allowed by FAT. In FAT, a name can have max 255
31 characters (not bytes!). Since the UTF-8 encoding of a char may take
32 up to 4 bytes, there will be names that we won't be able to store
33 completely. For such names, the short DOS name is used. */
34#define FAT_FILENAME_BYTES 256
35
28struct fat_direntry 36struct fat_direntry
29{ 37{
30 unsigned char name[256]; /* Name plus \0 */ 38 unsigned char name[FAT_FILENAME_BYTES]; /* UTF-8 encoded name plus \0 */
31 unsigned short attr; /* Attributes */ 39 unsigned short attr; /* Attributes */
32 unsigned char crttimetenth; /* Millisecond creation 40 unsigned char crttimetenth; /* Millisecond creation
33 time stamp (0-199) */ 41 time stamp (0-199) */