5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
25 const char * sdCheckAndCreateDirectory(const char * path
)
29 FRESULT result
= f_opendir(&archiveFolder
, path
);
30 if (result
!= FR_OK
) {
31 if (result
== FR_NO_PATH
)
32 result
= f_mkdir(path
);
34 return SDCARD_ERROR(result
);
37 f_closedir(&archiveFolder
);
43 bool isFileAvailable(const char * path
, bool exclDir
)
47 return (f_stat(path
, &fno
) == FR_OK
&& !(fno
.fattrib
& AM_DIR
));
49 return f_stat(path
, 0) == FR_OK
;
53 Search file system path for a file. Can optionally take a list of file extensions to search through.
54 Eg. find "splash.bmp", or the first occurrence of one of "splash.[bmp|jpeg|jpg|gif]".
56 @param path String with path name, no trailing slash. eg; "/BITMAPS"
57 @param file String containing file name to search for, with or w/out an extension.
58 eg; "splash.bmp" or "splash"
59 @param pattern Optional list of one or more file extensions concatenated together, including the period(s).
60 The list is searched backwards and the first match, if any, is returned. If null, then only the actual filename
61 passed will be searched for.
62 eg: ".gif.jpg.jpeg.bmp"
63 @param exclDir true/false whether to allow directory matches (default true, excludes directories)
64 @param match Optional container to hold the matched file extension (wide enough to hold LEN_FILE_EXTENSION_MAX + 1).
65 @retval true if a file was found, false otherwise.
67 bool isFilePatternAvailable(const char * path
, const char * file
, const char * pattern
= NULL
, bool exclDir
= true, char * match
= NULL
)
70 char fqfp
[LEN_FILE_PATH_MAX
+ _MAX_LFN
+ 1] = "\0";
73 if (fplen
> LEN_FILE_PATH_MAX
) {
74 TRACE_ERROR("isFilePatternAvailable(%s) = error: file path too long.\n", path
, file
);
79 strcpy(fqfp
+ fplen
, "/");
80 strncat(fqfp
+ (++fplen
), file
, _MAX_LFN
);
82 if (pattern
== NULL
) {
83 // no extensions list, just check the filename as-is
84 return isFileAvailable(fqfp
, exclDir
);
87 // extensions list search
90 uint8_t extlen
, fnlen
;
93 getFileExtension(file
, 0, 0, &fnlen
, &extlen
);
94 len
= fplen
+ fnlen
- extlen
;
96 ext
= getFileExtension(pattern
, 0, 0, &fnlen
, &extlen
);
98 while (plen
> 0 && ext
) {
99 strncat(fqfp
+ len
, ext
, extlen
);
100 if (isFileAvailable(fqfp
, exclDir
)) {
101 if (match
!= NULL
) strncat(&(match
[0]='\0'), ext
, extlen
);
107 ext
= getFileExtension(pattern
, plen
, 0, NULL
, &extlen
);
114 char * getFileIndex(char * filename
, unsigned int & value
)
117 char * pos
= (char *)getFileExtension(filename
);
118 if (!pos
|| pos
== filename
)
121 while (pos
> filename
) {
124 if (c
>= '0' && c
<= '9') {
125 value
+= multiplier
* (c
- '0');
135 uint8_t getDigitsCount(unsigned int value
)
138 while (value
>= 10) {
145 int findNextFileIndex(char * filename
, uint8_t size
, const char * directory
)
149 char * indexPos
= getFileIndex(filename
, index
);
150 char extension
[LEN_FILE_EXTENSION_MAX
+1] = "\0";
151 char * p
= (char *)getFileExtension(filename
, 0, 0, NULL
, &extlen
);
152 if (p
) strncat(extension
, p
, sizeof(extension
)-1);
155 if ((indexPos
- filename
) + getDigitsCount(index
) + extlen
> size
) {
158 char * pos
= strAppendUnsigned(indexPos
, index
);
159 strAppend(pos
, extension
);
160 if (!isFilePatternAvailable(directory
, filename
, NULL
, false)) {
166 const char * getFileExtension(const char * filename
, uint8_t size
, uint8_t extMaxLen
, uint8_t *fnlen
, uint8_t *extlen
)
170 len
= strlen(filename
);
173 extMaxLen
= LEN_FILE_EXTENSION_MAX
;
176 *fnlen
= (uint8_t)len
;
178 for (int i
=len
-1; i
>= 0 && len
-i
<= extMaxLen
; --i
) {
179 if (filename
[i
] == '.') {
186 if (extlen
!= NULL
) {
193 Check if given extension exists in a list of extensions.
194 @param extension The extension to search for, including leading period.
195 @param pattern One or more file extensions concatenated together, including the periods.
196 The list is searched backwards and the first match, if any, is returned.
197 eg: ".gif.jpg.jpeg.png"
198 @param match Optional container to hold the matched file extension (wide enough to hold LEN_FILE_EXTENSION_MAX + 1).
199 @retval true if a extension was found in the lost, false otherwise.
201 bool isExtensionMatching(const char * extension
, const char * pattern
, char * match
)
204 uint8_t extlen
, fnlen
;
207 ext
= getFileExtension(pattern
, 0, 0, &fnlen
, &extlen
);
209 while (plen
> 0 && ext
) {
210 if (!strncasecmp(extension
, ext
, extlen
)) {
211 if (match
!= NULL
) strncat(&(match
[0]='\0'), ext
, extlen
);
216 ext
= getFileExtension(pattern
, plen
, 0, NULL
, &extlen
);
222 bool sdListFiles(const char * path
, const char * extension
, const uint8_t maxlen
, const char * selection
, uint8_t flags
)
224 static uint16_t lastpopupMenuOffset
= 0;
228 uint8_t fnLen
, extLen
;
229 char tmpExt
[LEN_FILE_EXTENSION_MAX
+1] = "\0";
232 popupMenuOffsetType
= MENU_OFFSET_EXTERNAL
;
236 static uint8_t s_last_flags
;
239 s_last_flags
= flags
;
240 if (!isFilePatternAvailable(path
, selection
, ((flags
& LIST_SD_FILE_EXT
) ? NULL
: extension
))) selection
= NULL
;
243 flags
= s_last_flags
;
247 if (popupMenuOffset
== 0) {
248 lastpopupMenuOffset
= 0;
249 memset(reusableBuffer
.modelsel
.menu_bss
, 0, sizeof(reusableBuffer
.modelsel
.menu_bss
));
251 else if (popupMenuOffset
== popupMenuNoItems
- MENU_MAX_DISPLAY_LINES
) {
252 lastpopupMenuOffset
= 0xffff;
253 memset(reusableBuffer
.modelsel
.menu_bss
, 0, sizeof(reusableBuffer
.modelsel
.menu_bss
));
255 else if (popupMenuOffset
== lastpopupMenuOffset
) {
256 // should not happen, only there because of Murphy's law
259 else if (popupMenuOffset
> lastpopupMenuOffset
) {
260 memmove(reusableBuffer
.modelsel
.menu_bss
[0], reusableBuffer
.modelsel
.menu_bss
[1], (MENU_MAX_DISPLAY_LINES
-1)*MENU_LINE_LENGTH
);
261 memset(reusableBuffer
.modelsel
.menu_bss
[MENU_MAX_DISPLAY_LINES
-1], 0xff, MENU_LINE_LENGTH
);
264 memmove(reusableBuffer
.modelsel
.menu_bss
[1], reusableBuffer
.modelsel
.menu_bss
[0], (MENU_MAX_DISPLAY_LINES
-1)*MENU_LINE_LENGTH
);
265 memset(reusableBuffer
.modelsel
.menu_bss
[0], 0, MENU_LINE_LENGTH
);
268 popupMenuNoItems
= 0;
269 POPUP_MENU_SET_BSS_FLAG();
271 FRESULT res
= f_opendir(&dir
, path
);
274 if (flags
& LIST_NONE_SD_FILE
) {
277 lastpopupMenuOffset
++;
279 else if (popupMenuOffset
==0 || popupMenuOffset
< lastpopupMenuOffset
) {
280 char * line
= reusableBuffer
.modelsel
.menu_bss
[0];
281 memset(line
, 0, MENU_LINE_LENGTH
);
283 popupMenuItems
[0] = line
;
288 res
= f_readdir(&dir
, &fno
); /* Read a directory item */
289 if (res
!= FR_OK
|| fno
.fname
[0] == 0) break; /* Break on error or end of dir */
290 if (fno
.fattrib
& AM_DIR
) continue; /* Skip subfolders */
291 if (fno
.fattrib
& AM_HID
) continue; /* Skip hidden files */
292 if (fno
.fattrib
& AM_SYS
) continue; /* Skip system files */
294 fnExt
= getFileExtension(fno
.fname
, 0, 0, &fnLen
, &extLen
);
297 // TRACE_DEBUG("listSdFiles(%s, %s, %u, %s, %u): fn='%s'; fnExt='%s'; match=%d\n",
298 // path, extension, maxlen, (selection ? selection : "nul"), flags, fno.fname, (fnExt ? fnExt : "nul"), (fnExt && isExtensionMatching(fnExt, extension)));
299 // file validation checks
300 if (!fnLen
|| fnLen
> maxlen
|| ( // wrong size
301 fnExt
&& extension
&& ( // extension-based checks follow...
302 !isExtensionMatching(fnExt
, extension
) || ( // wrong extension
303 !(flags
& LIST_SD_FILE_EXT
) && // only if we want unique file names...
304 strcasecmp(fnExt
, getFileExtension(extension
)) && // possible duplicate file name...
305 isFilePatternAvailable(path
, fno
.fname
, extension
, true, tmpExt
) && // find the first file from extensions list...
306 strncasecmp(fnExt
, tmpExt
, LEN_FILE_EXTENSION_MAX
) // found file doesn't match, this is a duplicate
316 if (!(flags
& LIST_SD_FILE_EXT
)) {
317 fno
.fname
[fnLen
] = '\0'; // strip extension
320 if (popupMenuOffset
== 0) {
321 if (selection
&& strncasecmp(fno
.fname
, selection
, maxlen
) < 0) {
322 lastpopupMenuOffset
++;
325 for (uint8_t i
=0; i
<MENU_MAX_DISPLAY_LINES
; i
++) {
326 char * line
= reusableBuffer
.modelsel
.menu_bss
[i
];
327 if (line
[0] == '\0' || strcasecmp(fno
.fname
, line
) < 0) {
328 if (i
< MENU_MAX_DISPLAY_LINES
-1) memmove(reusableBuffer
.modelsel
.menu_bss
[i
+1], line
, sizeof(reusableBuffer
.modelsel
.menu_bss
[i
]) * (MENU_MAX_DISPLAY_LINES
-1-i
));
329 memset(line
, 0, MENU_LINE_LENGTH
);
330 strcpy(line
, fno
.fname
);
335 for (uint8_t i
=0; i
<min(popupMenuNoItems
, (uint16_t)MENU_MAX_DISPLAY_LINES
); i
++) {
336 popupMenuItems
[i
] = reusableBuffer
.modelsel
.menu_bss
[i
];
340 else if (lastpopupMenuOffset
== 0xffff) {
341 for (int i
=MENU_MAX_DISPLAY_LINES
-1; i
>=0; i
--) {
342 char * line
= reusableBuffer
.modelsel
.menu_bss
[i
];
343 if (line
[0] == '\0' || strcasecmp(fno
.fname
, line
) > 0) {
344 if (i
> 0) memmove(reusableBuffer
.modelsel
.menu_bss
[0], reusableBuffer
.modelsel
.menu_bss
[1], sizeof(reusableBuffer
.modelsel
.menu_bss
[i
]) * i
);
345 memset(line
, 0, MENU_LINE_LENGTH
);
346 strcpy(line
, fno
.fname
);
350 for (uint8_t i
=0; i
<min(popupMenuNoItems
, (uint16_t)MENU_MAX_DISPLAY_LINES
); i
++) {
351 popupMenuItems
[i
] = reusableBuffer
.modelsel
.menu_bss
[i
];
354 else if (popupMenuOffset
> lastpopupMenuOffset
) {
355 if (strcasecmp(fno
.fname
, reusableBuffer
.modelsel
.menu_bss
[MENU_MAX_DISPLAY_LINES
-2]) > 0 && strcasecmp(fno
.fname
, reusableBuffer
.modelsel
.menu_bss
[MENU_MAX_DISPLAY_LINES
-1]) < 0) {
356 memset(reusableBuffer
.modelsel
.menu_bss
[MENU_MAX_DISPLAY_LINES
-1], 0, MENU_LINE_LENGTH
);
357 strcpy(reusableBuffer
.modelsel
.menu_bss
[MENU_MAX_DISPLAY_LINES
-1], fno
.fname
);
361 if (strcasecmp(fno
.fname
, reusableBuffer
.modelsel
.menu_bss
[1]) < 0 && strcasecmp(fno
.fname
, reusableBuffer
.modelsel
.menu_bss
[0]) > 0) {
362 memset(reusableBuffer
.modelsel
.menu_bss
[0], 0, MENU_LINE_LENGTH
);
363 strcpy(reusableBuffer
.modelsel
.menu_bss
[0], fno
.fname
);
370 if (popupMenuOffset
> 0)
371 lastpopupMenuOffset
= popupMenuOffset
;
373 popupMenuOffset
= lastpopupMenuOffset
;
375 return popupMenuNoItems
;
378 // returns true if current working dir is at the root level
382 if (f_getcwd(path
, sizeof(path
)-1) == FR_OK
) {
383 return (strcasecmp("/", path
) == 0);
389 Wrapper around the f_readdir() function which
390 also returns ".." entry for sub-dirs. (FatFS 0.12 does
391 not return ".", ".." dirs anymore)
393 FRESULT
sdReadDir(DIR * dir
, FILINFO
* fno
, bool & firstTime
)
396 if (firstTime
&& !isCwdAtRoot()) {
397 // fake parent directory entry
398 strcpy(fno
->fname
, "..");
399 fno
->fattrib
= AM_DIR
;
403 res
= f_readdir(dir
, fno
); /* Read a directory item */
409 #if defined(CPUARM) && defined(SDCARD)
410 const char * sdCopyFile(const char * srcPath
, const char * destPath
)
415 UINT read
= sizeof(buf
);
416 UINT written
= sizeof(buf
);
418 FRESULT result
= f_open(&srcFile
, srcPath
, FA_OPEN_EXISTING
| FA_READ
);
419 if (result
!= FR_OK
) {
420 return SDCARD_ERROR(result
);
423 result
= f_open(&destFile
, destPath
, FA_CREATE_ALWAYS
| FA_WRITE
);
424 if (result
!= FR_OK
) {
426 return SDCARD_ERROR(result
);
429 while (result
==FR_OK
&& read
==sizeof(buf
) && written
==sizeof(buf
)) {
430 result
= f_read(&srcFile
, buf
, sizeof(buf
), &read
);
431 if (result
== FR_OK
) {
432 result
= f_write(&destFile
, buf
, read
, &written
);
439 if (result
!= FR_OK
) {
440 return SDCARD_ERROR(result
);
446 const char * sdCopyFile(const char * srcFilename
, const char * srcDir
, const char * destFilename
, const char * destDir
)
448 char srcPath
[2*CLIPBOARD_PATH_LEN
+1];
449 char * tmp
= strAppend(srcPath
, srcDir
, CLIPBOARD_PATH_LEN
);
451 strAppend(tmp
, srcFilename
, CLIPBOARD_PATH_LEN
);
453 char destPath
[2*CLIPBOARD_PATH_LEN
+1];
454 tmp
= strAppend(destPath
, destDir
, CLIPBOARD_PATH_LEN
);
456 strAppend(tmp
, destFilename
, CLIPBOARD_PATH_LEN
);
458 return sdCopyFile(srcPath
, destPath
);
460 #endif // defined(CPUARM) && defined(SDCARD)
463 #if !defined(SIMU) || defined(SIMU_DISKIO)
464 uint32_t sdGetNoSectors()
466 static DWORD noSectors
= 0;
467 if (noSectors
== 0 ) {
468 disk_ioctl(0, GET_SECTOR_COUNT
, &noSectors
);
475 return (sdGetNoSectors() * BLOCK_SIZE
) / 1000000;
478 uint32_t sdGetFreeSectors()
482 if (f_getfree("", &nofree
, &fat
) != FR_OK
) {
485 return nofree
* fat
->csize
;
488 #else // #if !defined(SIMU) || defined(SIMU_DISKIO)
490 uint32_t sdGetNoSectors()
500 uint32_t sdGetFreeSectors()
505 #endif // #if !defined(SIMU) || defined(SIMU_DISKIO)