1 /*----------------------------------------------------------------------------/ 
   2 /  FatFs - FAT file system module  R0.09a                 (C)ChaN, 2012 
   3 /-----------------------------------------------------------------------------/ 
   4 / FatFs module is a generic FAT file system module for small embedded systems. 
   5 / This is a free software that opened for education, research and commercial 
   6 / developments under license policy of following terms. 
   8 /  Copyright (C) 2012, ChaN, all right reserved. 
  10 / * The FatFs module is a free software and there is NO WARRANTY. 
  11 / * No restriction on use. You can use, modify and redistribute it for 
  12 /   personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. 
  13 / * Redistributions of source code must retain the above copyright notice. 
  15 /-----------------------------------------------------------------------------/ 
  16 / Feb 26,'06 R0.00  Prototype. 
  18 / Apr 29,'06 R0.01  First stable version. 
  20 / Jun 01,'06 R0.02  Added FAT12 support. 
  21 /                   Removed unbuffered mode. 
  22 /                   Fixed a problem on small (<32M) partition. 
  23 / Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM). 
  25 / Sep 22,'06 R0.03  Added f_rename(). 
  26 /                   Changed option _FS_MINIMUM to _FS_MINIMIZE. 
  27 / Dec 11,'06 R0.03a Improved cluster scan algorithm to write files fast. 
  28 /                   Fixed f_mkdir() creates incorrect directory on FAT32. 
  30 / Feb 04,'07 R0.04  Supported multiple drive system. 
  31 /                   Changed some interfaces for multiple drive system. 
  32 /                   Changed f_mountdrv() to f_mount(). 
  34 / Apr 01,'07 R0.04a Supported multiple partitions on a physical drive. 
  35 /                   Added a capability of extending file size to f_lseek(). 
  36 /                   Added minimization level 3. 
  37 /                   Fixed an endian sensitive code in f_mkfs(). 
  38 / May 05,'07 R0.04b Added a configuration option _USE_NTFLAG. 
  39 /                   Added FSInfo support. 
  40 /                   Fixed DBCS name can result FR_INVALID_NAME. 
  41 /                   Fixed short seek (<= csize) collapses the file object. 
  43 / Aug 25,'07 R0.05  Changed arguments of f_read(), f_write() and f_mkfs(). 
  44 /                   Fixed f_mkfs() on FAT32 creates incorrect FSInfo. 
  45 /                   Fixed f_mkdir() on FAT32 creates incorrect directory. 
  46 / Feb 03,'08 R0.05a Added f_truncate() and f_utime(). 
  47 /                   Fixed off by one error at FAT sub-type determination. 
  48 /                   Fixed btr in f_read() can be mistruncated. 
  49 /                   Fixed cached sector is not flushed when create and close without write. 
  51 / Apr 01,'08 R0.06  Added fputc(), fputs(), fprintf() and fgets(). 
  52 /                   Improved performance of f_lseek() on moving to the same or following cluster. 
  54 / Apr 01,'09 R0.07  Merged Tiny-FatFs as a configuration option. (_FS_TINY) 
  55 /                   Added long file name feature. 
  56 /                   Added multiple code page feature. 
  57 /                   Added re-entrancy for multitask operation. 
  58 /                   Added auto cluster size selection to f_mkfs(). 
  59 /                   Added rewind option to f_readdir(). 
  60 /                   Changed result code of critical errors. 
  61 /                   Renamed string functions to avoid name collision. 
  62 / Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg. 
  63 /                   Added multiple sector size feature. 
  64 / Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error. 
  65 /                   Fixed wrong cache control in f_lseek(). 
  66 /                   Added relative path feature. 
  67 /                   Added f_chdir() and f_chdrive(). 
  68 /                   Added proper case conversion to extended char. 
  69 / Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h. 
  70 /                   Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH. 
  71 /                   Fixed name matching error on the 13 char boundary. 
  72 /                   Added a configuration option, _LFN_UNICODE. 
  73 /                   Changed f_readdir() to return the SFN with always upper case on non-LFN cfg. 
  75 / May 15,'10 R0.08  Added a memory configuration option. (_USE_LFN = 3) 
  76 /                   Added file lock feature. (_FS_SHARE) 
  77 /                   Added fast seek feature. (_USE_FASTSEEK) 
  78 /                   Changed some types on the API, XCHAR->TCHAR. 
  79 /                   Changed fname member in the FILINFO structure on Unicode cfg. 
  80 /                   String functions support UTF-8 encoding files on Unicode cfg. 
  81 / Aug 16,'10 R0.08a Added f_getcwd(). (_FS_RPATH = 2) 
  82 /                   Added sector erase feature. (_USE_ERASE) 
  83 /                   Moved file lock semaphore table from fs object to the bss. 
  84 /                   Fixed a wrong directory entry is created on non-LFN cfg when the given name contains ';'. 
  85 /                   Fixed f_mkfs() creates wrong FAT32 volume. 
  86 / Jan 15,'11 R0.08b Fast seek feature is also applied to f_read() and f_write(). 
  87 /                   f_lseek() reports required table size on creating CLMP. 
  88 /                   Extended format syntax of f_printf function. 
  89 /                   Ignores duplicated directory separators in given path name. 
  91 / Sep 06,'11 R0.09  f_mkfs() supports multiple partition to finish the multiple partition feature. 
  92 /                   Added f_fdisk(). (_MULTI_PARTITION = 2) 
  93 / Aug 27,'12 R0.09a Fixed assertion failure due to OS/2 EA on FAT12/16 volume. 
  94 /                   Changed f_open() and f_opendir reject null object pointer to avoid crash. 
  95 /                   Changed option name _FS_SHARE to _FS_LOCK. 
  96 /---------------------------------------------------------------------------*/ 
  98 #include "ff.h"                 /* FatFs configurations and declarations */ 
  99 #include "diskio.h"             /* Declarations of low level disk I/O functions */ 
 102 /*-------------------------------------------------------------------------- 
 104    Module Private Definitions 
 106 ---------------------------------------------------------------------------*/ 
 108 #if _FATFS != 4004      /* Revision ID */ 
 109 #error Wrong include file (ff.h). 
 113 /* Definitions on sector size */ 
 114 #if _MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096 
 115 #error Wrong sector size. 
 118 #define SS(fs)  ((fs)->ssize)   /* Variable sector size */ 
 120 #define SS(fs)  512U                    /* Fixed sector size */ 
 124 /* Reentrancy related */ 
 127 #error Static LFN work area must not be used in re-entrant configuration. 
 129 #define ENTER_FF(fs)            { if (!lock_fs(fs)) return FR_TIMEOUT; } 
 130 #define LEAVE_FF(fs, res)       { unlock_fs(fs, res); return res; } 
 133 #define LEAVE_FF(fs, res)       return res 
 136 #define ABORT(fs, res)          { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); } 
 139 /* File access control feature */ 
 142 #error _FS_LOCK must be 0 on read-only cfg. 
 145         FATFS 
*fs
;                              /* File ID 1, volume (NULL:blank entry) */ 
 146         DWORD clu
;                              /* File ID 2, directory */ 
 147         WORD idx
;                               /* File ID 3, directory index */ 
 148         WORD ctr
;                               /* File open counter, 0:none, 0x01..0xFF:read open count, 0x100:write mode */ 
 154 /* DBCS code ranges and SBCS extend char conversion table */ 
 156 #if _CODE_PAGE == 932   /* Japanese Shift-JIS */ 
 157 #define _DF1S   0x81    /* DBC 1st byte range 1 start */ 
 158 #define _DF1E   0x9F    /* DBC 1st byte range 1 end */ 
 159 #define _DF2S   0xE0    /* DBC 1st byte range 2 start */ 
 160 #define _DF2E   0xFC    /* DBC 1st byte range 2 end */ 
 161 #define _DS1S   0x40    /* DBC 2nd byte range 1 start */ 
 162 #define _DS1E   0x7E    /* DBC 2nd byte range 1 end */ 
 163 #define _DS2S   0x80    /* DBC 2nd byte range 2 start */ 
 164 #define _DS2E   0xFC    /* DBC 2nd byte range 2 end */ 
 166 #elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ 
 174 #elif _CODE_PAGE == 949 /* Korean */ 
 184 #elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ 
 192 #elif _CODE_PAGE == 437 /* U.S. (OEM) */ 
 194 #define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 
 195                                 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 196                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 197                                 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 199 #elif _CODE_PAGE == 720 /* Arabic (OEM) */ 
 201 #define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 
 202                                 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 203                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 204                                 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 206 #elif _CODE_PAGE == 737 /* Greek (OEM) */ 
 208 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \ 
 209                                 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 210                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 211                                 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 213 #elif _CODE_PAGE == 775 /* Baltic (OEM) */ 
 215 #define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ 
 216                                 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 217                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 218                                 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 220 #elif _CODE_PAGE == 850 /* Multilingual Latin 1 (OEM) */ 
 222 #define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ 
 223                                 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 224                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 225                                 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 227 #elif _CODE_PAGE == 852 /* Latin 2 (OEM) */ 
 229 #define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \ 
 230                                 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \ 
 231                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 232                                 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} 
 234 #elif _CODE_PAGE == 855 /* Cyrillic (OEM) */ 
 236 #define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \ 
 237                                 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \ 
 238                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \ 
 239                                 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} 
 241 #elif _CODE_PAGE == 857 /* Turkish (OEM) */ 
 243 #define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \ 
 244                                 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 245                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 246                                 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 248 #elif _CODE_PAGE == 858 /* Multilingual Latin 1 + Euro (OEM) */ 
 250 #define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ 
 251                                 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 252                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 253                                 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 255 #elif _CODE_PAGE == 862 /* Hebrew (OEM) */ 
 257 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 
 258                                 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 259                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 260                                 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 262 #elif _CODE_PAGE == 866 /* Russian (OEM) */ 
 264 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 
 265                                 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 266                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 267                                 0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 269 #elif _CODE_PAGE == 874 /* Thai (OEM, Windows) */ 
 271 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 
 272                                 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 273                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 274                                 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 276 #elif _CODE_PAGE == 1250 /* Central Europe (Windows) */ 
 278 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ 
 279                                 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \ 
 280                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 281                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} 
 283 #elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */ 
 285 #define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ 
 286                                 0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \ 
 287                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 288                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF} 
 290 #elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */ 
 292 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \ 
 293                                 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 294                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 295                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} 
 297 #elif _CODE_PAGE == 1253 /* Greek (Windows) */ 
 299 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 
 300                                 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 301                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \ 
 302                                 0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF} 
 304 #elif _CODE_PAGE == 1254 /* Turkish (Windows) */ 
 306 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \ 
 307                                 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 308                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 309                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} 
 311 #elif _CODE_PAGE == 1255 /* Hebrew (Windows) */ 
 313 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 
 314                                 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 315                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 316                                 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} 
 318 #elif _CODE_PAGE == 1256 /* Arabic (Windows) */ 
 320 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \ 
 321                                 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 322                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 323                                 0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF} 
 325 #elif _CODE_PAGE == 1257 /* Baltic (Windows) */ 
 327 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ 
 328                                 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \ 
 329                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 330                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} 
 332 #elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */ 
 334 #define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \ 
 335                                 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ 
 336                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ 
 337                                 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F} 
 339 #elif _CODE_PAGE == 1   /* ASCII (for only non-LFN cfg) */ 
 341 #error Cannot use LFN feature without valid code page. 
 346 #error Unknown code page 
 351 /* Character code support macros */ 
 352 #define IsUpper(c)      (((c)>='A')&&((c)<='Z')) 
 353 #define IsLower(c)      (((c)>='a')&&((c)<='z')) 
 354 #define IsDigit(c)      (((c)>='0')&&((c)<='9')) 
 356 #if _DF1S               /* Code page is DBCS */ 
 358 #ifdef _DF2S    /* Two 1st byte areas */ 
 359 #define IsDBCS1(c)      (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E)) 
 360 #else                   /* One 1st byte area */ 
 361 #define IsDBCS1(c)      ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) 
 364 #ifdef _DS3S    /* Three 2nd byte areas */ 
 365 #define IsDBCS2(c)      (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E)) 
 366 #else                   /* Two 2nd byte areas */ 
 367 #define IsDBCS2(c)      (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E)) 
 370 #else                   /* Code page is SBCS */ 
 378 /* Name status flags */ 
 379 #define NS                      11              /* Index of name status byte in fn[] */ 
 380 #define NS_LOSS         0x01    /* Out of 8.3 format */ 
 381 #define NS_LFN          0x02    /* Force to create LFN entry */ 
 382 #define NS_LAST         0x04    /* Last segment */ 
 383 #define NS_BODY         0x08    /* Lower case flag (body) */ 
 384 #define NS_EXT          0x10    /* Lower case flag (ext) */ 
 385 #define NS_DOT          0x20    /* Dot entry */ 
 388 /* FAT sub-type boundaries */ 
 389 /* Note that the FAT spec by Microsoft says 4085 but Windows works with 4087! */ 
 390 #define MIN_FAT16       4086    /* Minimum number of clusters for FAT16 */ 
 391 #define MIN_FAT32       65526   /* Minimum number of clusters for FAT32 */ 
 394 /* FatFs refers the members in the FAT structures as byte array instead of 
 395 / structure member because the structure is not binary compatible between 
 396 / different platforms */ 
 398 #define BS_jmpBoot                      0       /* Jump instruction (3) */ 
 399 #define BS_OEMName                      3       /* OEM name (8) */ 
 400 #define BPB_BytsPerSec          11      /* Sector size [byte] (2) */ 
 401 #define BPB_SecPerClus          13      /* Cluster size [sector] (1) */ 
 402 #define BPB_RsvdSecCnt          14      /* Size of reserved area [sector] (2) */ 
 403 #define BPB_NumFATs                     16      /* Number of FAT copies (1) */ 
 404 #define BPB_RootEntCnt          17      /* Number of root dir entries for FAT12/16 (2) */ 
 405 #define BPB_TotSec16            19      /* Volume size [sector] (2) */ 
 406 #define BPB_Media                       21      /* Media descriptor (1) */ 
 407 #define BPB_FATSz16                     22      /* FAT size [sector] (2) */ 
 408 #define BPB_SecPerTrk           24      /* Track size [sector] (2) */ 
 409 #define BPB_NumHeads            26      /* Number of heads (2) */ 
 410 #define BPB_HiddSec                     28      /* Number of special hidden sectors (4) */ 
 411 #define BPB_TotSec32            32      /* Volume size [sector] (4) */ 
 412 #define BS_DrvNum                       36      /* Physical drive number (2) */ 
 413 #define BS_BootSig                      38      /* Extended boot signature (1) */ 
 414 #define BS_VolID                        39      /* Volume serial number (4) */ 
 415 #define BS_VolLab                       43      /* Volume label (8) */ 
 416 #define BS_FilSysType           54      /* File system type (1) */ 
 417 #define BPB_FATSz32                     36      /* FAT size [sector] (4) */ 
 418 #define BPB_ExtFlags            40      /* Extended flags (2) */ 
 419 #define BPB_FSVer                       42      /* File system version (2) */ 
 420 #define BPB_RootClus            44      /* Root dir first cluster (4) */ 
 421 #define BPB_FSInfo                      48      /* Offset of FSInfo sector (2) */ 
 422 #define BPB_BkBootSec           50      /* Offset of backup boot sector (2) */ 
 423 #define BS_DrvNum32                     64      /* Physical drive number (2) */ 
 424 #define BS_BootSig32            66      /* Extended boot signature (1) */ 
 425 #define BS_VolID32                      67      /* Volume serial number (4) */ 
 426 #define BS_VolLab32                     71      /* Volume label (8) */ 
 427 #define BS_FilSysType32         82      /* File system type (1) */ 
 428 #define FSI_LeadSig                     0       /* FSI: Leading signature (4) */ 
 429 #define FSI_StrucSig            484     /* FSI: Structure signature (4) */ 
 430 #define FSI_Free_Count          488     /* FSI: Number of free clusters (4) */ 
 431 #define FSI_Nxt_Free            492     /* FSI: Last allocated cluster (4) */ 
 432 #define MBR_Table                       446     /* MBR: Partition table offset (2) */ 
 433 #define SZ_PTE                          16      /* MBR: Size of a partition table entry */ 
 434 #define BS_55AA                         510     /* Boot sector signature (2) */ 
 436 #define DIR_Name                        0       /* Short file name (11) */ 
 437 #define DIR_Attr                        11      /* Attribute (1) */ 
 438 #define DIR_NTres                       12      /* NT flag (1) */ 
 439 #define DIR_CrtTimeTenth        13      /* Created time sub-second (1) */ 
 440 #define DIR_CrtTime                     14      /* Created time (2) */ 
 441 #define DIR_CrtDate                     16      /* Created date (2) */ 
 442 #define DIR_LstAccDate          18      /* Last accessed date (2) */ 
 443 #define DIR_FstClusHI           20      /* Higher 16-bit of first cluster (2) */ 
 444 #define DIR_WrtTime                     22      /* Modified time (2) */ 
 445 #define DIR_WrtDate                     24      /* Modified date (2) */ 
 446 #define DIR_FstClusLO           26      /* Lower 16-bit of first cluster (2) */ 
 447 #define DIR_FileSize            28      /* File size (4) */ 
 448 #define LDIR_Ord                        0       /* LFN entry order and LLE flag (1) */ 
 449 #define LDIR_Attr                       11      /* LFN attribute (1) */ 
 450 #define LDIR_Type                       12      /* LFN type (1) */ 
 451 #define LDIR_Chksum                     13      /* Sum of corresponding SFN entry */ 
 452 #define LDIR_FstClusLO          26      /* Filled by zero (0) */ 
 453 #define SZ_DIR                          32              /* Size of a directory entry */ 
 454 #define LLE                                     0x40    /* Last long entry flag in LDIR_Ord */ 
 455 #define DDE                                     0xE5    /* Deleted directory entry mark in DIR_Name[0] */ 
 456 #define NDDE                            0x05    /* Replacement of the character collides with DDE */ 
 459 /*------------------------------------------------------------*/ 
 460 /* Module private work area                                   */ 
 461 /*------------------------------------------------------------*/ 
 462 /* Note that uninitialized variables with static duration are 
 463 /  zeroed/nulled at start-up. If not, the compiler or start-up 
 464 /  routine is out of ANSI-C standard. 
 469 FATFS 
*FatFs
[_VOLUMES
]; /* Pointer to the file system objects (logical drives) */ 
 471 #error Number of volumes must not be 0. 
 475 WORD Fsid
;                              /* File system mount ID */ 
 479 BYTE CurrVol
;                   /* Current drive */ 
 484 FILESEM Files
[_FS_LOCK
];        /* File lock semaphores */ 
 487 #if _USE_LFN == 0                       /* No LFN feature */ 
 488 #define DEF_NAMEBUF                     BYTE sfn[12] 
 489 #define INIT_BUF(dobj)          (dobj).fn = sfn 
 492 #elif _USE_LFN == 1                     /* LFN feature with static working buffer */ 
 493 static WCHAR LfnBuf
[_MAX_LFN
+1]; 
 494 #define DEF_NAMEBUF                     BYTE sfn[12] 
 495 #define INIT_BUF(dobj)          { (dobj).fn = sfn; (dobj).lfn = LfnBuf; } 
 498 #elif _USE_LFN == 2             /* LFN feature with dynamic working buffer on the stack */ 
 499 #define DEF_NAMEBUF                     BYTE sfn[12]; WCHAR lbuf[_MAX_LFN+1] 
 500 #define INIT_BUF(dobj)          { (dobj).fn = sfn; (dobj).lfn = lbuf; } 
 503 #elif _USE_LFN == 3             /* LFN feature with dynamic working buffer on the heap */ 
 504 #define DEF_NAMEBUF                     BYTE sfn[12]; WCHAR *lfn 
 505 #define INIT_BUF(dobj)          { lfn = ff_memalloc((_MAX_LFN + 1) * 2); \ 
 506                                                           if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); \ 
 507                                                           (dobj).lfn = lfn;     (dobj).fn = sfn; } 
 508 #define FREE_BUF()                      ff_memfree(lfn) 
 511 #error Wrong LFN configuration. 
 517 /*-------------------------------------------------------------------------- 
 519    Module Private Functions 
 521 ---------------------------------------------------------------------------*/ 
 524 /*-----------------------------------------------------------------------*/ 
 525 /* String functions                                                      */ 
 526 /*-----------------------------------------------------------------------*/ 
 528 /* Copy memory to memory */ 
 530 void mem_cpy (void* dst
, const void* src
, UINT cnt
) { 
 531         BYTE 
*d 
= (BYTE
*)dst
; 
 532         const BYTE 
*s 
= (const BYTE
*)src
; 
 534 #if _WORD_ACCESS == 1 
 535         while (cnt 
>= sizeof (int)) { 
 537                 d 
+= sizeof (int); s 
+= sizeof (int); 
 547 void mem_set (void* dst
, int val
, UINT cnt
) { 
 548         BYTE 
*d 
= (BYTE
*)dst
; 
 554 /* Compare memory to memory */ 
 556 int mem_cmp (const void* dst
, const void* src
, UINT cnt
) { 
 557         const BYTE 
*d 
= (const BYTE 
*)dst
, *s 
= (const BYTE 
*)src
; 
 560         while (cnt
-- && (r 
= *d
++ - *s
++) == 0) ; 
 564 /* Check if chr is contained in the string */ 
 566 int chk_chr (const char* str
, int chr
) { 
 567         while (*str 
&& *str 
!= chr
) str
++; 
 573 /*-----------------------------------------------------------------------*/ 
 574 /* Request/Release grant to access the volume                            */ 
 575 /*-----------------------------------------------------------------------*/ 
 580         FATFS 
*fs               
/* File system object */ 
 583         return ff_req_grant(fs
->sobj
); 
 589         FATFS 
*fs
,              /* File system object */ 
 590         FRESULT res             
/* Result code to be returned */ 
 594                 res 
!= FR_NOT_ENABLED 
&& 
 595                 res 
!= FR_INVALID_DRIVE 
&& 
 596                 res 
!= FR_INVALID_OBJECT 
&& 
 598                 ff_rel_grant(fs
->sobj
); 
 605 /*-----------------------------------------------------------------------*/ 
 606 /* File lock control functions                                           */ 
 607 /*-----------------------------------------------------------------------*/ 
 611 FRESULT 
chk_lock (      /* Check if the file can be accessed */ 
 612         DIR* dj
,                /* Directory object pointing the file to be checked */ 
 613         int acc                 
/* Desired access (0:Read, 1:Write, 2:Delete/Rename) */ 
 618         /* Search file semaphore table */ 
 619         for (i 
= be 
= 0; i 
< _FS_LOCK
; i
++) { 
 620                 if (Files
[i
].fs
) {      /* Existing entry */ 
 621                         if (Files
[i
].fs 
== dj
->fs 
&&            /* Check if the file matched with an open file */ 
 622                                 Files
[i
].clu 
== dj
->sclust 
&& 
 623                                 Files
[i
].idx 
== dj
->index
) break; 
 624                 } else {                        /* Blank entry */ 
 628         if (i 
== _FS_LOCK
)      /* The file is not opened */ 
 629                 return (be 
|| acc 
== 2) ? FR_OK 
: FR_TOO_MANY_OPEN_FILES
;       /* Is there a blank entry for new file? */ 
 631         /* The file has been opened. Reject any open against writing file and all write mode open */ 
 632         return (acc 
|| Files
[i
].ctr 
== 0x100) ? FR_LOCKED 
: FR_OK
; 
 637 int enq_lock (void)     /* Check if an entry is available for a new file */ 
 641         for (i 
= 0; i 
< _FS_LOCK 
&& Files
[i
].fs
; i
++) ; 
 642         return (i 
== _FS_LOCK
) ? 
0 : 1; 
 647 UINT 
inc_lock ( /* Increment file open counter and returns its index (0:int error) */ 
 648         DIR* dj
,        /* Directory object pointing the file to register or increment */ 
 649         int acc         
/* Desired access mode (0:Read, !0:Write) */ 
 655         for (i 
= 0; i 
< _FS_LOCK
; i
++) {        /* Find the file */ 
 656                 if (Files
[i
].fs 
== dj
->fs 
&& 
 657                         Files
[i
].clu 
== dj
->sclust 
&& 
 658                         Files
[i
].idx 
== dj
->index
) break; 
 661         if (i 
== _FS_LOCK
) {                            /* Not opened. Register it as new. */ 
 662                 for (i 
= 0; i 
< _FS_LOCK 
&& Files
[i
].fs
; i
++) ; 
 663                 if (i 
== _FS_LOCK
) return 0;    /* No space to register (int err) */ 
 664                 Files
[i
].fs 
= dj
->fs
; 
 665                 Files
[i
].clu 
= dj
->sclust
; 
 666                 Files
[i
].idx 
= dj
->index
; 
 670         if (acc 
&& Files
[i
].ctr
) return 0;      /* Access violation (int err) */ 
 672         Files
[i
].ctr 
= acc ? 
0x100 : Files
[i
].ctr 
+ 1;  /* Set semaphore value */ 
 679 FRESULT 
dec_lock (      /* Decrement file open counter */ 
 680         UINT i                  
/* Semaphore index */ 
 687         if (--i 
< _FS_LOCK
) { 
 689                 if (n 
== 0x100) n 
= 0; 
 692                 if (!n
) Files
[i
].fs 
= 0; 
 702 void clear_lock (       /* Clear lock entries of the volume */ 
 708         for (i 
= 0; i 
< _FS_LOCK
; i
++) { 
 709                 if (Files
[i
].fs 
== fs
) Files
[i
].fs 
= 0; 
 716 /*-----------------------------------------------------------------------*/ 
 717 /* Change window offset                                                  */ 
 718 /*-----------------------------------------------------------------------*/ 
 721 FRESULT 
move_window ( 
 722         FATFS 
*fs
,              /* File system object */ 
 723         DWORD sector    
/* Sector number to make appearance in the fs->win[] */ 
 724 )                                       /* Move to zero only writes back dirty window */ 
 730         if (wsect 
!= sector
) {  /* Changed current window */ 
 732                 if (fs
->wflag
) {        /* Write back dirty window if needed */ 
 733                         if (disk_write(fs
->drv
, fs
->win
, wsect
, 1) != RES_OK
) 
 736                         if (wsect 
< (fs
->fatbase 
+ fs
->fsize
)) {        /* In FAT area */ 
 738                                 for (nf 
= fs
->n_fats
; nf 
> 1; nf
--) {   /* Reflect the change to all FAT copies */ 
 740                                         disk_write(fs
->drv
, fs
->win
, wsect
, 1); 
 746                         if (disk_read(fs
->drv
, fs
->win
, sector
, 1) != RES_OK
) 
 748                         fs
->winsect 
= sector
; 
 758 /*-----------------------------------------------------------------------*/ 
 759 /* Clean-up cached data                                                  */ 
 760 /*-----------------------------------------------------------------------*/ 
 763 FRESULT 
sync (  /* FR_OK: successful, FR_DISK_ERR: failed */ 
 764         FATFS 
*fs       
/* File system object */ 
 770         res 
= move_window(fs
, 0); 
 772                 /* Update FSInfo sector if needed */ 
 773                 if (fs
->fs_type 
== FS_FAT32 
&& fs
->fsi_flag
) { 
 775                         /* Create FSInfo structure */ 
 776                         mem_set(fs
->win
, 0, 512); 
 777                         ST_WORD(fs
->win
+BS_55AA
, 0xAA55); 
 778                         ST_DWORD(fs
->win
+FSI_LeadSig
, 0x41615252); 
 779                         ST_DWORD(fs
->win
+FSI_StrucSig
, 0x61417272); 
 780                         ST_DWORD(fs
->win
+FSI_Free_Count
, fs
->free_clust
); 
 781                         ST_DWORD(fs
->win
+FSI_Nxt_Free
, fs
->last_clust
); 
 782                         /* Write it into the FSInfo sector */ 
 783                         disk_write(fs
->drv
, fs
->win
, fs
->fsi_sector
, 1); 
 786                 /* Make sure that no pending write process in the physical drive */ 
 787                 if (disk_ioctl(fs
->drv
, CTRL_SYNC
, 0) != RES_OK
) 
 798 /*-----------------------------------------------------------------------*/ 
 799 /* Get sector# from cluster#                                             */ 
 800 /*-----------------------------------------------------------------------*/ 
 803 DWORD 
clust2sect (      /* !=0: Sector number, 0: Failed - invalid cluster# */ 
 804         FATFS 
*fs
,              /* File system object */ 
 805         DWORD clst              
/* Cluster# to be converted */ 
 809         if (clst 
>= (fs
->n_fatent 
- 2)) return 0;               /* Invalid cluster# */ 
 810         return clst 
* fs
->csize 
+ fs
->database
; 
 816 /*-----------------------------------------------------------------------*/ 
 817 /* FAT access - Read value of a FAT entry                                */ 
 818 /*-----------------------------------------------------------------------*/ 
 821 DWORD 
get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, Else:Cluster status */ 
 822         FATFS 
*fs
,      /* File system object */ 
 823         DWORD clst      
/* Cluster# to get the link information */ 
 830         if (clst 
< 2 || clst 
>= fs
->n_fatent
)   /* Check range */ 
 833         switch (fs
->fs_type
) { 
 835                 bc 
= (UINT
)clst
; bc 
+= bc 
/ 2; 
 836                 if (move_window(fs
, fs
->fatbase 
+ (bc 
/ SS(fs
)))) break; 
 837                 wc 
= fs
->win
[bc 
% SS(fs
)]; bc
++; 
 838                 if (move_window(fs
, fs
->fatbase 
+ (bc 
/ SS(fs
)))) break; 
 839                 wc 
|= fs
->win
[bc 
% SS(fs
)] << 8; 
 840                 return (clst 
& 1) ? 
(wc 
>> 4) : (wc 
& 0xFFF); 
 843                 if (move_window(fs
, fs
->fatbase 
+ (clst 
/ (SS(fs
) / 2)))) break; 
 844                 p 
= &fs
->win
[clst 
* 2 % SS(fs
)]; 
 848                 if (move_window(fs
, fs
->fatbase 
+ (clst 
/ (SS(fs
) / 4)))) break; 
 849                 p 
= &fs
->win
[clst 
* 4 % SS(fs
)]; 
 850                 return LD_DWORD(p
) & 0x0FFFFFFF; 
 853         return 0xFFFFFFFF;      /* An error occurred at the disk I/O layer */ 
 859 /*-----------------------------------------------------------------------*/ 
 860 /* FAT access - Change value of a FAT entry                              */ 
 861 /*-----------------------------------------------------------------------*/ 
 865         FATFS 
*fs
,      /* File system object */ 
 866         DWORD clst
,     /* Cluster# to be changed in range of 2 to fs->n_fatent - 1 */ 
 867         DWORD val       
/* New value to mark the cluster */ 
 875         if (clst 
< 2 || clst 
>= fs
->n_fatent
) { /* Check range */ 
 879                 switch (fs
->fs_type
) { 
 881                         bc 
= (UINT
)clst
; bc 
+= bc 
/ 2; 
 882                         res 
= move_window(fs
, fs
->fatbase 
+ (bc 
/ SS(fs
))); 
 883                         if (res 
!= FR_OK
) break; 
 884                         p 
= &fs
->win
[bc 
% SS(fs
)]; 
 885                         *p 
= (clst 
& 1) ? 
((*p 
& 0x0F) | ((BYTE
)val 
<< 4)) : (BYTE
)val
; 
 888                         res 
= move_window(fs
, fs
->fatbase 
+ (bc 
/ SS(fs
))); 
 889                         if (res 
!= FR_OK
) break; 
 890                         p 
= &fs
->win
[bc 
% SS(fs
)]; 
 891                         *p 
= (clst 
& 1) ? 
(BYTE
)(val 
>> 4) : ((*p 
& 0xF0) | ((BYTE
)(val 
>> 8) & 0x0F)); 
 895                         res 
= move_window(fs
, fs
->fatbase 
+ (clst 
/ (SS(fs
) / 2))); 
 896                         if (res 
!= FR_OK
) break; 
 897                         p 
= &fs
->win
[clst 
* 2 % SS(fs
)]; 
 898                         ST_WORD(p
, (WORD
)val
); 
 902                         res 
= move_window(fs
, fs
->fatbase 
+ (clst 
/ (SS(fs
) / 4))); 
 903                         if (res 
!= FR_OK
) break; 
 904                         p 
= &fs
->win
[clst 
* 4 % SS(fs
)]; 
 905                         val 
|= LD_DWORD(p
) & 0xF0000000; 
 917 #endif /* !_FS_READONLY */ 
 922 /*-----------------------------------------------------------------------*/ 
 923 /* FAT handling - Remove a cluster chain                                 */ 
 924 /*-----------------------------------------------------------------------*/ 
 927 FRESULT 
remove_chain ( 
 928         FATFS 
*fs
,                      /* File system object */ 
 929         DWORD clst                      
/* Cluster# to remove a chain from */ 
 935         DWORD scl 
= clst
, ecl 
= clst
, rt
[2]; 
 938         if (clst 
< 2 || clst 
>= fs
->n_fatent
) { /* Check range */ 
 943                 while (clst 
< fs
->n_fatent
) {                   /* Not a last link? */ 
 944                         nxt 
= get_fat(fs
, clst
);                        /* Get cluster status */ 
 945                         if (nxt 
== 0) break;                            /* Empty cluster? */ 
 946                         if (nxt 
== 1) { res 
= FR_INT_ERR
; break; }      /* Internal error? */ 
 947                         if (nxt 
== 0xFFFFFFFF) { res 
= FR_DISK_ERR
; break; }    /* Disk error? */ 
 948                         res 
= put_fat(fs
, clst
, 0);                     /* Mark the cluster "empty" */ 
 949                         if (res 
!= FR_OK
) break; 
 950                         if (fs
->free_clust 
!= 0xFFFFFFFF) {     /* Update FSInfo */ 
 955                         if (ecl 
+ 1 == nxt
) {   /* Is next cluster contiguous? */ 
 957                         } else {                                /* End of contiguous clusters */  
 958                                 rt
[0] = clust2sect(fs
, scl
);                                    /* Start sector */ 
 959                                 rt
[1] = clust2sect(fs
, ecl
) + fs
->csize 
- 1;    /* End sector */ 
 960                                 disk_ioctl(fs
->drv
, CTRL_ERASE_SECTOR
, rt
);             /* Erase the block */ 
 964                         clst 
= nxt
;     /* Next cluster */ 
 975 /*-----------------------------------------------------------------------*/ 
 976 /* FAT handling - Stretch or Create a cluster chain                      */ 
 977 /*-----------------------------------------------------------------------*/ 
 980 DWORD 
create_chain (    /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ 
 981         FATFS 
*fs
,                      /* File system object */ 
 982         DWORD clst                      
/* Cluster# to stretch. 0 means create a new chain. */ 
 989         if (clst 
== 0) {                /* Create a new chain */ 
 990                 scl 
= fs
->last_clust
;                   /* Get suggested start point */ 
 991                 if (!scl 
|| scl 
>= fs
->n_fatent
) scl 
= 1; 
 993         else {                                  /* Stretch the current chain */ 
 994                 cs 
= get_fat(fs
, clst
);                 /* Check the cluster status */ 
 995                 if (cs 
< 2) return 1;                   /* It is an invalid cluster */ 
 996                 if (cs 
< fs
->n_fatent
) return cs
;       /* It is already followed by next cluster */ 
1000         ncl 
= scl
;                              /* Start cluster */ 
1002                 ncl
++;                                                  /* Next cluster */ 
1003                 if (ncl 
>= fs
->n_fatent
) {              /* Wrap around */ 
1005                         if (ncl 
> scl
) return 0;        /* No free cluster */ 
1007                 cs 
= get_fat(fs
, ncl
);                  /* Get the cluster status */ 
1008                 if (cs 
== 0) break;                             /* Found a free cluster */ 
1009                 if (cs 
== 0xFFFFFFFF || cs 
== 1)/* An error occurred */ 
1011                 if (ncl 
== scl
) return 0;               /* No free cluster */ 
1014         res 
= put_fat(fs
, ncl
, 0x0FFFFFFF);     /* Mark the new cluster "last link" */ 
1015         if (res 
== FR_OK 
&& clst 
!= 0) { 
1016                 res 
= put_fat(fs
, clst
, ncl
);   /* Link it to the previous one if needed */ 
1019                 fs
->last_clust 
= ncl
;                   /* Update FSINFO */ 
1020                 if (fs
->free_clust 
!= 0xFFFFFFFF) { 
1025                 ncl 
= (res 
== FR_DISK_ERR
) ? 
0xFFFFFFFF : 1; 
1028         return ncl
;             /* Return new cluster number or error code */ 
1030 #endif /* !_FS_READONLY */ 
1034 /*-----------------------------------------------------------------------*/ 
1035 /* FAT handling - Convert offset into cluster with link map table        */ 
1036 /*-----------------------------------------------------------------------*/ 
1040 DWORD 
clmt_clust (      /* <2:Error, >=2:Cluster number */ 
1041         FIL
* fp
,                /* Pointer to the file object */ 
1042         DWORD ofs               
/* File offset to be converted to cluster# */ 
1045         DWORD cl
, ncl
, *tbl
; 
1048         tbl 
= fp
->cltbl 
+ 1;    /* Top of CLMT */ 
1049         cl 
= ofs 
/ SS(fp
->fs
) / fp
->fs
->csize
;  /* Cluster order from top of the file */ 
1051                 ncl 
= *tbl
++;                   /* Number of cluters in the fragment */ 
1052                 if (!ncl
) return 0;             /* End of table? (error) */ 
1053                 if (cl 
< ncl
) break;    /* In this fragment? */ 
1054                 cl 
-= ncl
; tbl
++;               /* Next fragment */ 
1056         return cl 
+ *tbl
;       /* Return the cluster number */ 
1058 #endif  /* _USE_FASTSEEK */ 
1062 /*-----------------------------------------------------------------------*/ 
1063 /* Directory handling - Set directory index                              */ 
1064 /*-----------------------------------------------------------------------*/ 
1068         DIR *dj
,                /* Pointer to directory object */ 
1069         WORD idx                
/* Index of directory table */ 
1078         if (clst 
== 1 || clst 
>= dj
->fs
->n_fatent
)      /* Check start cluster range */ 
1080         if (!clst 
&& dj
->fs
->fs_type 
== FS_FAT32
)       /* Replace cluster# 0 with root cluster# if in FAT32 */ 
1081                 clst 
= dj
->fs
->dirbase
; 
1083         if (clst 
== 0) {        /* Static table (root-dir in FAT12/16) */ 
1085                 if (idx 
>= dj
->fs
->n_rootdir
)           /* Index is out of range */ 
1087                 dj
->sect 
= dj
->fs
->dirbase 
+ idx 
/ (SS(dj
->fs
) / SZ_DIR
);       /* Sector# */ 
1089         else {                          /* Dynamic table (sub-dirs or root-dir in FAT32) */ 
1090                 ic 
= SS(dj
->fs
) / SZ_DIR 
* dj
->fs
->csize
;       /* Entries per cluster */ 
1091                 while (idx 
>= ic
) {     /* Follow cluster chain */ 
1092                         clst 
= get_fat(dj
->fs
, clst
);                           /* Get next cluster */ 
1093                         if (clst 
== 0xFFFFFFFF) return FR_DISK_ERR
;     /* Disk error */ 
1094                         if (clst 
< 2 || clst 
>= dj
->fs
->n_fatent
)       /* Reached to end of table or int error */ 
1099                 dj
->sect 
= clust2sect(dj
->fs
, clst
) + idx 
/ (SS(dj
->fs
) / SZ_DIR
);      /* Sector# */ 
1102         dj
->dir 
= dj
->fs
->win 
+ (idx 
% (SS(dj
->fs
) / SZ_DIR
)) * SZ_DIR
; /* Ptr to the entry in the sector */ 
1104         return FR_OK
;   /* Seek succeeded */ 
1110 /*-----------------------------------------------------------------------*/ 
1111 /* Directory handling - Move directory table index next                  */ 
1112 /*-----------------------------------------------------------------------*/ 
1115 FRESULT 
dir_next (      /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not stretch */ 
1116         DIR *dj
,                /* Pointer to directory object */ 
1117         int stretch             
/* 0: Do not stretch table, 1: Stretch table if needed */ 
1124         stretch 
= stretch
;              /* To suppress warning on read-only cfg. */ 
1126         if (!i 
|| !dj
->sect
)    /* Report EOT when index has reached 65535 */ 
1129         if (!(i 
% (SS(dj
->fs
) / SZ_DIR
))) {     /* Sector changed? */ 
1130                 dj
->sect
++;                                     /* Next sector */ 
1132                 if (dj
->clust 
== 0) {   /* Static table */ 
1133                         if (i 
>= dj
->fs
->n_rootdir
)     /* Report EOT when end of table */ 
1136                 else {                                  /* Dynamic table */ 
1137                         if (((i 
/ (SS(dj
->fs
) / SZ_DIR
)) & (dj
->fs
->csize 
- 1)) == 0) { /* Cluster changed? */ 
1138                                 clst 
= get_fat(dj
->fs
, dj
->clust
);                              /* Get next cluster */ 
1139                                 if (clst 
<= 1) return FR_INT_ERR
; 
1140                                 if (clst 
== 0xFFFFFFFF) return FR_DISK_ERR
; 
1141                                 if (clst 
>= dj
->fs
->n_fatent
) {                                 /* When it reached end of dynamic table */ 
1144                                         if (!stretch
) return FR_NO_FILE
;                        /* When do not stretch, report EOT */ 
1145                                         clst 
= create_chain(dj
->fs
, dj
->clust
);         /* Stretch cluster chain */ 
1146                                         if (clst 
== 0) return FR_DENIED
;                        /* No free cluster */ 
1147                                         if (clst 
== 1) return FR_INT_ERR
; 
1148                                         if (clst 
== 0xFFFFFFFF) return FR_DISK_ERR
; 
1149                                         /* Clean-up stretched table */ 
1150                                         if (move_window(dj
->fs
, 0)) return FR_DISK_ERR
; /* Flush active window */ 
1151                                         mem_set(dj
->fs
->win
, 0, SS(dj
->fs
));                    /* Clear window buffer */ 
1152                                         dj
->fs
->winsect 
= clust2sect(dj
->fs
, clst
);     /* Cluster start sector */ 
1153                                         for (c 
= 0; c 
< dj
->fs
->csize
; c
++) {           /* Fill the new cluster with 0 */ 
1155                                                 if (move_window(dj
->fs
, 0)) return FR_DISK_ERR
; 
1158                                         dj
->fs
->winsect 
-= c
;                                           /* Rewind window address */ 
1160                                         return FR_NO_FILE
;                      /* Report EOT */ 
1163                                 dj
->clust 
= clst
;                               /* Initialize data for new cluster */ 
1164                                 dj
->sect 
= clust2sect(dj
->fs
, clst
); 
1170         dj
->dir 
= dj
->fs
->win 
+ (i 
% (SS(dj
->fs
) / SZ_DIR
)) * SZ_DIR
; 
1178 /*-----------------------------------------------------------------------*/ 
1179 /* Directory handling - Load/Store start cluster number                  */ 
1180 /*-----------------------------------------------------------------------*/ 
1184         FATFS 
*fs
,      /* Pointer to the fs object */ 
1185         BYTE 
*dir       
/* Pointer to the directory entry */ 
1190         cl 
= LD_WORD(dir
+DIR_FstClusLO
); 
1191         if (fs
->fs_type 
== FS_FAT32
) 
1192                 cl 
|= (DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16; 
1201         BYTE 
*dir
,      /* Pointer to the directory entry */ 
1202         DWORD cl        
/* Value to be set */ 
1205         ST_WORD(dir
+DIR_FstClusLO
, cl
); 
1206         ST_WORD(dir
+DIR_FstClusHI
, cl 
>> 16); 
1212 /*-----------------------------------------------------------------------*/ 
1213 /* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry   */ 
1214 /*-----------------------------------------------------------------------*/ 
1217 const BYTE LfnOfs
[] = {1,3,5,7,9,14,16,18,20,22,24,28,30};      /* Offset of LFN chars in the directory entry */ 
1221 int cmp_lfn (                   /* 1:Matched, 0:Not matched */ 
1222         WCHAR 
*lfnbuf
,          /* Pointer to the LFN to be compared */ 
1223         BYTE 
*dir                       
/* Pointer to the directory entry containing a part of LFN */ 
1230         i 
= ((dir
[LDIR_Ord
] & ~LLE
) - 1) * 13;  /* Get offset in the LFN buffer */ 
1233                 uc 
= LD_WORD(dir
+LfnOfs
[s
]);    /* Pick an LFN character from the entry */ 
1234                 if (wc
) {       /* Last char has not been processed */ 
1235                         wc 
= ff_wtoupper(uc
);           /* Convert it to upper case */ 
1236                         if (i 
>= _MAX_LFN 
|| wc 
!= ff_wtoupper(lfnbuf
[i
++]))    /* Compare it */ 
1237                                 return 0;                               /* Not matched */ 
1239                         if (uc 
!= 0xFFFF) return 0;     /* Check filler */ 
1241         } while (++s 
< 13);                             /* Repeat until all chars in the entry are checked */ 
1243         if ((dir
[LDIR_Ord
] & LLE
) && wc 
&& lfnbuf
[i
])   /* Last segment matched but different length */ 
1246         return 1;                                               /* The part of LFN matched */ 
1252 int pick_lfn (                  /* 1:Succeeded, 0:Buffer overflow */ 
1253         WCHAR 
*lfnbuf
,          /* Pointer to the Unicode-LFN buffer */ 
1254         BYTE 
*dir                       
/* Pointer to the directory entry */ 
1261         i 
= ((dir
[LDIR_Ord
] & 0x3F) - 1) * 13;  /* Offset in the LFN buffer */ 
1265                 uc 
= LD_WORD(dir
+LfnOfs
[s
]);            /* Pick an LFN character from the entry */ 
1266                 if (wc
) {       /* Last char has not been processed */ 
1267                         if (i 
>= _MAX_LFN
) return 0;    /* Buffer overflow? */ 
1268                         lfnbuf
[i
++] = wc 
= uc
;                  /* Store it */ 
1270                         if (uc 
!= 0xFFFF) return 0;             /* Check filler */ 
1272         } while (++s 
< 13);                                             /* Read all character in the entry */ 
1274         if (dir
[LDIR_Ord
] & LLE
) {                              /* Put terminator if it is the last LFN part */ 
1275                 if (i 
>= _MAX_LFN
) return 0;            /* Buffer overflow? */ 
1286         const WCHAR 
*lfnbuf
,    /* Pointer to the LFN buffer */ 
1287         BYTE 
*dir
,                              /* Pointer to the directory entry */ 
1288         BYTE ord
,                               /* LFN order (1-20) */ 
1289         BYTE sum                                
/* SFN sum */ 
1296         dir
[LDIR_Chksum
] = sum
;                 /* Set check sum */ 
1297         dir
[LDIR_Attr
] = AM_LFN
;                /* Set attribute. LFN entry */ 
1299         ST_WORD(dir
+LDIR_FstClusLO
, 0); 
1301         i 
= (ord 
- 1) * 13;                             /* Get offset in the LFN buffer */ 
1304                 if (wc 
!= 0xFFFF) wc 
= lfnbuf
[i
++];     /* Get an effective char */ 
1305                 ST_WORD(dir
+LfnOfs
[s
], wc
);     /* Put it */ 
1306                 if (!wc
) wc 
= 0xFFFF;           /* Padding chars following last char */ 
1308         if (wc 
== 0xFFFF || !lfnbuf
[i
]) ord 
|= LLE
;     /* Bottom LFN part is the start of LFN sequence */ 
1309         dir
[LDIR_Ord
] = ord
;                    /* Set the LFN order */ 
1317 /*-----------------------------------------------------------------------*/ 
1318 /* Create numbered name                                                  */ 
1319 /*-----------------------------------------------------------------------*/ 
1322         BYTE 
*dst
,                      /* Pointer to generated SFN */ 
1323         const BYTE 
*src
,        /* Pointer to source SFN to be modified */ 
1324         const WCHAR 
*lfn
,       /* Pointer to LFN */ 
1325         WORD seq                        
/* Sequence number */ 
1332         mem_cpy(dst
, src
, 11); 
1334         if (seq 
> 5) {  /* On many collisions, generate a hash number instead of sequential number */ 
1335                 do seq 
= (seq 
>> 1) + (seq 
<< 15) + (WORD
)*lfn
++; while (*lfn
); 
1338         /* itoa (hexdecimal) */ 
1341                 c 
= (seq 
% 16) + '0'; 
1342                 if (c 
> '9') c 
+= 7; 
1348         /* Append the number */ 
1349         for (j 
= 0; j 
< i 
&& dst
[j
] != ' '; j
++) { 
1350                 if (IsDBCS1(dst
[j
])) { 
1351                         if (j 
== i 
- 1) break; 
1356                 dst
[j
++] = (i 
< 8) ? ns
[i
++] : ' '; 
1364 /*-----------------------------------------------------------------------*/ 
1365 /* Calculate sum of an SFN                                               */ 
1366 /*-----------------------------------------------------------------------*/ 
1370         const BYTE 
*dir         
/* Ptr to directory entry */ 
1376         do sum 
= (sum 
>> 1) + (sum 
<< 7) + *dir
++; while (--n
); 
1384 /*-----------------------------------------------------------------------*/ 
1385 /* Directory handling - Find an object in the directory                  */ 
1386 /*-----------------------------------------------------------------------*/ 
1390         DIR *dj                 
/* Pointer to the directory object linked to the file name */ 
1399         res 
= dir_sdi(dj
, 0);                   /* Rewind directory object */ 
1400         if (res 
!= FR_OK
) return res
; 
1406                 res 
= move_window(dj
->fs
, dj
->sect
); 
1407                 if (res 
!= FR_OK
) break; 
1408                 dir 
= dj
->dir
;                                  /* Ptr to the directory entry of current index */ 
1410                 if (c 
== 0) { res 
= FR_NO_FILE
; break; }        /* Reached to end of table */ 
1411 #if _USE_LFN    /* LFN configuration */ 
1412                 a 
= dir
[DIR_Attr
] & AM_MASK
; 
1413                 if (c 
== DDE 
|| ((a 
& AM_VOL
) && a 
!= AM_LFN
)) {        /* An entry without valid data */ 
1416                         if (a 
== AM_LFN
) {                      /* An LFN entry is found */ 
1418                                         if (c 
& LLE
) {          /* Is it start of LFN sequence? */ 
1419                                                 sum 
= dir
[LDIR_Chksum
]; 
1420                                                 c 
&= ~LLE
; ord 
= c
;     /* LFN start order */ 
1421                                                 dj
->lfn_idx 
= dj
->index
; 
1423                                         /* Check validity of the LFN entry and compare it with given name */ 
1424                                         ord 
= (c 
== ord 
&& sum 
== dir
[LDIR_Chksum
] && cmp_lfn(dj
->lfn
, dir
)) ? ord 
- 1 : 0xFF; 
1426                         } else {                                        /* An SFN entry is found */ 
1427                                 if (!ord 
&& sum 
== sum_sfn(dir
)) break; /* LFN matched? */ 
1428                                 ord 
= 0xFF; dj
->lfn_idx 
= 0xFFFF;       /* Reset LFN sequence */ 
1429                                 if (!(dj
->fn
[NS
] & NS_LOSS
) && !mem_cmp(dir
, dj
->fn
, 11)) break;        /* SFN matched? */ 
1432 #else           /* Non LFN configuration */ 
1433                 if (!(dir
[DIR_Attr
] & AM_VOL
) && !mem_cmp(dir
, dj
->fn
, 11)) /* Is it a valid entry? */ 
1436                 res 
= dir_next(dj
, 0);          /* Next entry */ 
1437         } while (res 
== FR_OK
); 
1445 /*-----------------------------------------------------------------------*/ 
1446 /* Read an object from the directory                                     */ 
1447 /*-----------------------------------------------------------------------*/ 
1448 #if _FS_MINIMIZE <= 1 
1451         DIR *dj                 
/* Pointer to the directory object that pointing the entry to be read */ 
1457         BYTE a
, ord 
= 0xFF, sum 
= 0xFF; 
1462                 res 
= move_window(dj
->fs
, dj
->sect
); 
1463                 if (res 
!= FR_OK
) break; 
1464                 dir 
= dj
->dir
;                                  /* Ptr to the directory entry of current index */ 
1466                 if (c 
== 0) { res 
= FR_NO_FILE
; break; }        /* Reached to end of table */ 
1467 #if _USE_LFN    /* LFN configuration */ 
1468                 a 
= dir
[DIR_Attr
] & AM_MASK
; 
1469                 if (c 
== DDE 
|| (!_FS_RPATH 
&& c 
== '.') || ((a 
& AM_VOL
) && a 
!= AM_LFN
)) {    /* An entry without valid data */ 
1472                         if (a 
== AM_LFN
) {                      /* An LFN entry is found */ 
1473                                 if (c 
& LLE
) {                  /* Is it start of LFN sequence? */ 
1474                                         sum 
= dir
[LDIR_Chksum
]; 
1476                                         dj
->lfn_idx 
= dj
->index
; 
1478                                 /* Check LFN validity and capture it */ 
1479                                 ord 
= (c 
== ord 
&& sum 
== dir
[LDIR_Chksum
] && pick_lfn(dj
->lfn
, dir
)) ? ord 
- 1 : 0xFF; 
1480                         } else {                                        /* An SFN entry is found */ 
1481                                 if (ord 
|| sum 
!= sum_sfn(dir
)) /* Is there a valid LFN? */ 
1482                                         dj
->lfn_idx 
= 0xFFFF;           /* It has no LFN. */ 
1486 #else           /* Non LFN configuration */ 
1487                 if (c 
!= DDE 
&& (_FS_RPATH 
|| c 
!= '.') && !(dir
[DIR_Attr
] & AM_VOL
))   /* Is it a valid entry? */ 
1490                 res 
= dir_next(dj
, 0);                          /* Next entry */ 
1491                 if (res 
!= FR_OK
) break; 
1494         if (res 
!= FR_OK
) dj
->sect 
= 0; 
1502 /*-----------------------------------------------------------------------*/ 
1503 /* Register an object to the directory                                   */ 
1504 /*-----------------------------------------------------------------------*/ 
1507 FRESULT 
dir_register (  /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */ 
1508         DIR *dj                         
/* Target directory with object name to be created */ 
1513 #if _USE_LFN    /* LFN configuration */ 
1515         BYTE sn
[12], *fn
, sum
; 
1519         fn 
= dj
->fn
; lfn 
= dj
->lfn
; 
1520         mem_cpy(sn
, fn
, 12); 
1522         if (_FS_RPATH 
&& (sn
[NS
] & NS_DOT
))             /* Cannot create dot entry */ 
1523                 return FR_INVALID_NAME
; 
1525         if (sn
[NS
] & NS_LOSS
) {                 /* When LFN is out of 8.3 format, generate a numbered name */ 
1526                 fn
[NS
] = 0; dj
->lfn 
= 0;                        /* Find only SFN */ 
1527                 for (n 
= 1; n 
< 100; n
++) { 
1528                         gen_numname(fn
, sn
, lfn
, n
);    /* Generate a numbered name */ 
1529                         res 
= dir_find(dj
);                             /* Check if the name collides with existing SFN */ 
1530                         if (res 
!= FR_OK
) break; 
1532                 if (n 
== 100) return FR_DENIED
;         /* Abort if too many collisions */ 
1533                 if (res 
!= FR_NO_FILE
) return res
;      /* Abort if the result is other than 'not collided' */ 
1534                 fn
[NS
] = sn
[NS
]; dj
->lfn 
= lfn
; 
1537         if (sn
[NS
] & NS_LFN
) {                  /* When LFN is to be created, reserve an SFN + LFN entries. */ 
1538                 for (ne 
= 0; lfn
[ne
]; ne
++) ; 
1539                 ne 
= (ne 
+ 25) / 13; 
1540         } else {                                                /* Otherwise reserve only an SFN entry. */ 
1544         /* Reserve contiguous entries */ 
1545         res 
= dir_sdi(dj
, 0); 
1546         if (res 
!= FR_OK
) return res
; 
1549                 res 
= move_window(dj
->fs
, dj
->sect
); 
1550                 if (res 
!= FR_OK
) break; 
1551                 c 
= *dj
->dir
;                           /* Check the entry status */ 
1552                 if (c 
== DDE 
|| c 
== 0) {       /* Is it a blank entry? */ 
1553                         if (n 
== 0) is 
= dj
->index
;     /* First index of the contiguous entry */ 
1554                         if (++n 
== ne
) break;   /* A contiguous entry that required count is found */ 
1556                         n 
= 0;                                  /* Not a blank entry. Restart to search */ 
1558                 res 
= dir_next(dj
, 1);          /* Next entry with table stretch */ 
1559         } while (res 
== FR_OK
); 
1561         if (res 
== FR_OK 
&& ne 
> 1) {   /* Initialize LFN entry if needed */ 
1562                 res 
= dir_sdi(dj
, is
); 
1564                         sum 
= sum_sfn(dj
->fn
);  /* Sum of the SFN tied to the LFN */ 
1566                         do {                                    /* Store LFN entries in bottom first */ 
1567                                 res 
= move_window(dj
->fs
, dj
->sect
); 
1568                                 if (res 
!= FR_OK
) break; 
1569                                 fit_lfn(dj
->lfn
, dj
->dir
, (BYTE
)ne
, sum
); 
1571                                 res 
= dir_next(dj
, 0);  /* Next entry */ 
1572                         } while (res 
== FR_OK 
&& --ne
); 
1576 #else   /* Non LFN configuration */ 
1577         res 
= dir_sdi(dj
, 0); 
1579                 do {    /* Find a blank entry for the SFN */ 
1580                         res 
= move_window(dj
->fs
, dj
->sect
); 
1581                         if (res 
!= FR_OK
) break; 
1583                         if (c 
== DDE 
|| c 
== 0) break;  /* Is it a blank entry? */ 
1584                         res 
= dir_next(dj
, 1);                  /* Next entry with table stretch */ 
1585                 } while (res 
== FR_OK
); 
1589         if (res 
== FR_OK
) {             /* Initialize the SFN entry */ 
1590                 res 
= move_window(dj
->fs
, dj
->sect
); 
1593                         mem_set(dir
, 0, SZ_DIR
);        /* Clean the entry */ 
1594                         mem_cpy(dir
, dj
->fn
, 11);       /* Put SFN */ 
1596                         dir
[DIR_NTres
] = *(dj
->fn
+NS
) & (NS_BODY 
| NS_EXT
);     /* Put NT flag */ 
1604 #endif /* !_FS_READONLY */ 
1609 /*-----------------------------------------------------------------------*/ 
1610 /* Remove an object from the directory                                   */ 
1611 /*-----------------------------------------------------------------------*/ 
1612 #if !_FS_READONLY && !_FS_MINIMIZE 
1614 FRESULT 
dir_remove (    /* FR_OK: Successful, FR_DISK_ERR: A disk error */ 
1615         DIR *dj                         
/* Directory object pointing the entry to be removed */ 
1619 #if _USE_LFN    /* LFN configuration */ 
1622         i 
= dj
->index
;  /* SFN index */ 
1623         res 
= dir_sdi(dj
, (WORD
)((dj
->lfn_idx 
== 0xFFFF) ? i 
: dj
->lfn_idx
));   /* Goto the SFN or top of the LFN entries */ 
1626                         res 
= move_window(dj
->fs
, dj
->sect
); 
1627                         if (res 
!= FR_OK
) break; 
1628                         *dj
->dir 
= DDE
;                 /* Mark the entry "deleted" */ 
1630                         if (dj
->index 
>= i
) break;      /* When reached SFN, all entries of the object has been deleted. */ 
1631                         res 
= dir_next(dj
, 0);          /* Next entry */ 
1632                 } while (res 
== FR_OK
); 
1633                 if (res 
== FR_NO_FILE
) res 
= FR_INT_ERR
; 
1636 #else                   /* Non LFN configuration */ 
1637         res 
= dir_sdi(dj
, dj
->index
); 
1639                 res 
= move_window(dj
->fs
, dj
->sect
); 
1641                         *dj
->dir 
= DDE
;                 /* Mark the entry "deleted" */ 
1649 #endif /* !_FS_READONLY */ 
1654 /*-----------------------------------------------------------------------*/ 
1655 /* Pick a segment and create the object name in directory form           */ 
1656 /*-----------------------------------------------------------------------*/ 
1659 FRESULT 
create_name ( 
1660         DIR *dj
,                        /* Pointer to the directory object */ 
1661         const TCHAR 
**path      
/* Pointer to pointer to the segment in the path string */ 
1665         static const BYTE excvt
[] = _EXCVT
;     /* Upper conversion table for extended chars */ 
1668 #if _USE_LFN    /* LFN configuration */ 
1674         /* Create LFN in Unicode */ 
1675         for (p 
= *path
; *p 
== '/' || *p 
== '\\'; p
++) ; /* Strip duplicated separator */ 
1679                 w 
= p
[si
++];                                    /* Get a character */ 
1680                 if (w 
< ' ' || w 
== '/' || w 
== '\\') break;    /* Break on end of segment */ 
1681                 if (di 
>= _MAX_LFN
)                             /* Reject too long name */ 
1682                         return FR_INVALID_NAME
; 
1685                 if (IsDBCS1(w
)) {                               /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ 
1686                         b 
= (BYTE
)p
[si
++];                      /* Get 2nd byte */ 
1688                                 return FR_INVALID_NAME
; /* Reject invalid sequence */ 
1689                         w 
= (w 
<< 8) + b
;                       /* Create a DBC */ 
1691                 w 
= ff_convert(w
, 1);                   /* Convert ANSI/OEM to Unicode */ 
1692                 if (!w
) return FR_INVALID_NAME
; /* Reject invalid code */ 
1694                 if (w 
< 0x80 && chk_chr("\"*:<>\?|\x7F", w
)) /* Reject illegal chars for LFN */ 
1695                         return FR_INVALID_NAME
; 
1696                 lfn
[di
++] = w
;                                  /* Store the Unicode char */ 
1698         *path 
= &p
[si
];                                         /* Return pointer to the next segment */ 
1699         cf 
= (w 
< ' ') ? NS_LAST 
: 0;           /* Set last segment flag if end of path */ 
1701         if ((di 
== 1 && lfn
[di
-1] == '.') || /* Is this a dot entry? */ 
1702                 (di 
== 2 && lfn
[di
-1] == '.' && lfn
[di
-2] == '.')) { 
1704                 for (i 
= 0; i 
< 11; i
++) 
1705                         dj
->fn
[i
] = (i 
< di
) ? 
'.' : ' '; 
1706                 dj
->fn
[i
] = cf 
| NS_DOT
;                /* This is a dot entry */ 
1710         while (di
) {                                            /* Strip trailing spaces and dots */ 
1712                 if (w 
!= ' ' && w 
!= '.') break; 
1715         if (!di
) return FR_INVALID_NAME
;        /* Reject nul string */ 
1717         lfn
[di
] = 0;                                            /* LFN is created */ 
1719         /* Create SFN in directory form */ 
1720         mem_set(dj
->fn
, ' ', 11); 
1721         for (si 
= 0; lfn
[si
] == ' ' || lfn
[si
] == '.'; si
++) ;  /* Strip leading spaces and dots */ 
1722         if (si
) cf 
|= NS_LOSS 
| NS_LFN
; 
1723         while (di 
&& lfn
[di 
- 1] != '.') di
--;  /* Find extension (di<=si: no extension) */ 
1727                 w 
= lfn
[si
++];                                  /* Get an LFN char */ 
1728                 if (!w
) break;                                  /* Break on end of the LFN */ 
1729                 if (w 
== ' ' || (w 
== '.' && si 
!= di
)) {       /* Remove spaces and dots */ 
1730                         cf 
|= NS_LOSS 
| NS_LFN
; continue; 
1733                 if (i 
>= ni 
|| si 
== di
) {              /* Extension or end of SFN */ 
1734                         if (ni 
== 11) {                         /* Long extension */ 
1735                                 cf 
|= NS_LOSS 
| NS_LFN
; break; 
1737                         if (si 
!= di
) cf 
|= NS_LOSS 
| NS_LFN
;   /* Out of 8.3 format */ 
1738                         if (si 
> di
) break;                     /* No extension */ 
1739                         si 
= di
; i 
= 8; ni 
= 11;        /* Enter extension section */ 
1743                 if (w 
>= 0x80) {                                /* Non ASCII char */ 
1745                         w 
= ff_convert(w
, 0);           /* Unicode -> OEM code */ 
1746                         if (w
) w 
= excvt
[w 
- 0x80];     /* Convert extended char to upper (SBCS) */ 
1748                         w 
= ff_convert(ff_wtoupper(w
), 0);      /* Upper converted Unicode -> OEM code */ 
1750                         cf 
|= NS_LFN
;                           /* Force create LFN entry */ 
1753                 if (_DF1S 
&& w 
>= 0x100) {              /* Double byte char (always false on SBCS cfg) */ 
1755                                 cf 
|= NS_LOSS 
| NS_LFN
; i 
= ni
; continue; 
1757                         dj
->fn
[i
++] = (BYTE
)(w 
>> 8); 
1758                 } else {                                                /* Single byte char */ 
1759                         if (!w 
|| chk_chr("+,;=[]", w
)) {       /* Replace illegal chars for SFN */ 
1760                                 w 
= '_'; cf 
|= NS_LOSS 
| NS_LFN
;/* Lossy conversion */ 
1762                                 if (IsUpper(w
)) {               /* ASCII large capital */ 
1765                                         if (IsLower(w
)) {       /* ASCII small capital */ 
1771                 dj
->fn
[i
++] = (BYTE
)w
; 
1774         if (dj
->fn
[0] == DDE
) dj
->fn
[0] = NDDE
; /* If the first char collides with deleted mark, replace it with 0x05 */ 
1776         if (ni 
== 8) b 
<<= 2; 
1777         if ((b 
& 0x0C) == 0x0C || (b 
& 0x03) == 0x03)   /* Create LFN entry when there are composite capitals */ 
1779         if (!(cf 
& NS_LFN
)) {                                           /* When LFN is in 8.3 format without extended char, NT flags are created */ 
1780                 if ((b 
& 0x03) == 0x01) cf 
|= NS_EXT
;   /* NT flag (Extension has only small capital) */ 
1781                 if ((b 
& 0x0C) == 0x04) cf 
|= NS_BODY
;  /* NT flag (Filename has only small capital) */ 
1784         dj
->fn
[NS
] = cf
;        /* SFN is created */ 
1789 #else   /* Non-LFN configuration */ 
1794         /* Create file name in directory form */ 
1795         for (p 
= *path
; *p 
== '/' || *p 
== '\\'; p
++) ; /* Strip duplicated separator */ 
1797         mem_set(sfn
, ' ', 11); 
1798         si 
= i 
= b 
= 0; ni 
= 8; 
1800         if (p
[si
] == '.') { /* Is this a dot entry? */ 
1803                         if (c 
!= '.' || si 
>= 3) break; 
1806                 if (c 
!= '/' && c 
!= '\\' && c 
> ' ') return FR_INVALID_NAME
; 
1807                 *path 
= &p
[si
];                                                                 /* Return pointer to the next segment */ 
1808                 sfn
[NS
] = (c 
<= ' ') ? NS_LAST 
| NS_DOT 
: NS_DOT
;       /* Set last segment flag if end of path */ 
1814                 if (c 
<= ' ' || c 
== '/' || c 
== '\\') break;   /* Break on end of segment */ 
1815                 if (c 
== '.' || i 
>= ni
) { 
1816                         if (ni 
!= 8 || c 
!= '.') return FR_INVALID_NAME
; 
1820                 if (c 
>= 0x80) {                                /* Extended char? */ 
1821                         b 
|= 3;                                         /* Eliminate NT flag */ 
1823                         c 
= excvt
[c 
- 0x80];            /* Upper conversion (SBCS) */ 
1825 #if !_DF1S      /* ASCII only cfg */ 
1826                         return FR_INVALID_NAME
; 
1830                 if (IsDBCS1(c
)) {                               /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ 
1831                         d 
= (BYTE
)p
[si
++];                      /* Get 2nd byte */ 
1832                         if (!IsDBCS2(d
) || i 
>= ni 
- 1) /* Reject invalid DBC */ 
1833                                 return FR_INVALID_NAME
; 
1836                 } else {                                                /* Single byte code */ 
1837                         if (chk_chr("\"*+,:;<=>\?[]|\x7F", c
))  /* Reject illegal chrs for SFN */ 
1838                                 return FR_INVALID_NAME
; 
1839                         if (IsUpper(c
)) {                       /* ASCII large capital? */ 
1842                                 if (IsLower(c
)) {               /* ASCII small capital? */ 
1849         *path 
= &p
[si
];                                         /* Return pointer to the next segment */ 
1850         c 
= (c 
<= ' ') ? NS_LAST 
: 0;           /* Set last segment flag if end of path */ 
1852         if (!i
) return FR_INVALID_NAME
;         /* Reject nul string */ 
1853         if (sfn
[0] == DDE
) sfn
[0] = NDDE
;       /* When first char collides with DDE, replace it with 0x05 */ 
1855         if (ni 
== 8) b 
<<= 2; 
1856         if ((b 
& 0x03) == 0x01) c 
|= NS_EXT
;    /* NT flag (Name extension has only small capital) */ 
1857         if ((b 
& 0x0C) == 0x04) c 
|= NS_BODY
;   /* NT flag (Name body has only small capital) */ 
1859         sfn
[NS
] = c
;            /* Store NT flag, File name is created */ 
1868 /*-----------------------------------------------------------------------*/ 
1869 /* Get file information from directory entry                             */ 
1870 /*-----------------------------------------------------------------------*/ 
1871 #if _FS_MINIMIZE <= 1 
1873 void get_fileinfo (             /* No return code */ 
1874         DIR *dj
,                        /* Pointer to the directory object */ 
1875         FILINFO 
*fno            
/* Pointer to the file information to be filled */ 
1886                 nt 
= dir
[DIR_NTres
];            /* NT flag */ 
1887                 for (i 
= 0; i 
< 8; i
++) {       /* Copy name body */ 
1889                         if (c 
== ' ') break; 
1890                         if (c 
== NDDE
) c 
= (TCHAR
)DDE
; 
1891                         if (_USE_LFN 
&& (nt 
& NS_BODY
) && IsUpper(c
)) c 
+= 0x20; 
1893                         if (IsDBCS1(c
) && i 
< 7 && IsDBCS2(dir
[i
+1])) 
1894                                 c 
= (c 
<< 8) | dir
[++i
]; 
1895                         c 
= ff_convert(c
, 1); 
1900                 if (dir
[8] != ' ') {            /* Copy name extension */ 
1902                         for (i 
= 8; i 
< 11; i
++) { 
1904                                 if (c 
== ' ') break; 
1905                                 if (_USE_LFN 
&& (nt 
& NS_EXT
) && IsUpper(c
)) c 
+= 0x20; 
1907                                 if (IsDBCS1(c
) && i 
< 10 && IsDBCS2(dir
[i
+1])) 
1908                                         c 
= (c 
<< 8) | dir
[++i
]; 
1909                                 c 
= ff_convert(c
, 1); 
1915                 fno
->fattrib 
= dir
[DIR_Attr
];                           /* Attribute */ 
1916                 fno
->fsize 
= LD_DWORD(dir
+DIR_FileSize
);        /* Size */ 
1917                 fno
->fdate 
= LD_WORD(dir
+DIR_WrtDate
);          /* Date */ 
1918                 fno
->ftime 
= LD_WORD(dir
+DIR_WrtTime
);          /* Time */ 
1920         *p 
= 0;         /* Terminate SFN str by a \0 */ 
1923         if (fno
->lfname 
&& fno
->lfsize
) { 
1924                 TCHAR 
*tp 
= fno
->lfname
; 
1928                 if (dj
->sect 
&& dj
->lfn_idx 
!= 0xFFFF) {/* Get LFN if available */ 
1930                         while ((w 
= *lfn
++) != 0) {                     /* Get an LFN char */ 
1932                                 w 
= ff_convert(w
, 0);                   /* Unicode -> OEM conversion */ 
1933                                 if (!w
) { i 
= 0; break; }               /* Could not convert, no LFN */ 
1934                                 if (_DF1S 
&& w 
>= 0x100)                /* Put 1st byte if it is a DBC (always false on SBCS cfg) */ 
1935                                         tp
[i
++] = (TCHAR
)(w 
>> 8); 
1937                                 if (i 
>= fno
->lfsize 
- 1) { i 
= 0; break; }     /* Buffer overflow, no LFN */ 
1941                 tp
[i
] = 0;      /* Terminate the LFN str by a \0 */ 
1945 #endif /* _FS_MINIMIZE <= 1 */ 
1950 /*-----------------------------------------------------------------------*/ 
1951 /* Follow a file path                                                    */ 
1952 /*-----------------------------------------------------------------------*/ 
1955 FRESULT 
follow_path (   /* FR_OK(0): successful, !=0: error code */ 
1956         DIR *dj
,                        /* Directory object to return last directory and found object */ 
1957         const TCHAR 
*path       
/* Full-path string to find a file or directory */ 
1965         if (*path 
== '/' || *path 
== '\\') { /* There is a heading separator */ 
1966                 path
++; dj
->sclust 
= 0;         /* Strip it and start from the root dir */ 
1967         } else {                                                        /* No heading separator */ 
1968                 dj
->sclust 
= dj
->fs
->cdir
;      /* Start from the current dir */ 
1971         if (*path 
== '/' || *path 
== '\\')      /* Strip heading separator if exist */ 
1973         dj
->sclust 
= 0;                                         /* Start from the root dir */ 
1976         if ((UINT
)*path 
< ' ') {                        /* Nul path means the start directory itself */ 
1977                 res 
= dir_sdi(dj
, 0); 
1979         } else {                                                        /* Follow path */ 
1981                         res 
= create_name(dj
, &path
);   /* Get a segment */ 
1982                         if (res 
!= FR_OK
) break; 
1983                         res 
= dir_find(dj
);                             /* Find it */ 
1985                         if (res 
!= FR_OK
) {                             /* Failed to find the object */ 
1986                                 if (res 
!= FR_NO_FILE
) break;   /* Abort if any hard error occurred */ 
1987                                 /* Object not found */ 
1988                                 if (_FS_RPATH 
&& (ns 
& NS_DOT
)) {       /* If dot entry is not exit */ 
1989                                         dj
->sclust 
= 0; dj
->dir 
= 0;    /* It is the root dir */ 
1991                                         if (!(ns 
& NS_LAST
)) continue; 
1992                                 } else {                                                        /* Could not find the object */ 
1993                                         if (!(ns 
& NS_LAST
)) res 
= FR_NO_PATH
; 
1997                         if (ns 
& NS_LAST
) break;                        /* Last segment match. Function completed. */ 
1998                         dir 
= dj
->dir
;                                          /* There is next segment. Follow the sub directory */ 
1999                         if (!(dir
[DIR_Attr
] & AM_DIR
)) {        /* Cannot follow because it is a file */ 
2000                                 res 
= FR_NO_PATH
; break; 
2002                         dj
->sclust 
= ld_clust(dj
->fs
, dir
); 
2012 /*-----------------------------------------------------------------------*/ 
2013 /* Load a sector and check if it is an FAT Volume Boot Record            */ 
2014 /*-----------------------------------------------------------------------*/ 
2017 BYTE 
check_fs ( /* 0:FAT-VBR, 1:Any BR but not FAT, 2:Not a BR, 3:Disk error */ 
2018         FATFS 
*fs
,      /* File system object */ 
2019         DWORD sect      
/* Sector# (lba) to check if it is an FAT boot record or not */ 
2022         if (disk_read(fs
->drv
, fs
->win
, sect
, 1) != RES_OK
)     /* Load boot record */ 
2024         if (LD_WORD(&fs
->win
[BS_55AA
]) != 0xAA55)               /* Check record signature (always placed at offset 510 even if the sector size is >512) */ 
2027         if ((LD_DWORD(&fs
->win
[BS_FilSysType
]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ 
2029         if ((LD_DWORD(&fs
->win
[BS_FilSysType32
]) & 0xFFFFFF) == 0x544146) 
2038 /*-----------------------------------------------------------------------*/ 
2039 /* Check if the file system object is valid or not                       */ 
2040 /*-----------------------------------------------------------------------*/ 
2043 FRESULT 
chk_mounted (   /* FR_OK(0): successful, !=0: any error occurred */ 
2044         const TCHAR 
**path
,     /* Pointer to pointer to the path name (drive number) */ 
2045         FATFS 
**rfs
,            /* Pointer to pointer to the found file system object */ 
2046         BYTE wmode                      
/* !=0: Check write protection for write access */ 
2049         BYTE fmt
, b
, pi
, *tbl
; 
2052         DWORD bsect
, fasize
, tsect
, sysect
, nclst
, szbfat
; 
2054         const TCHAR 
*p 
= *path
; 
2058         /* Get logical drive number from the path name */ 
2059         vol 
= p
[0] - '0';                                       /* Is there a drive number? */ 
2060         if (vol 
<= 9 && p
[1] == ':') {          /* Found a drive number, get and strip it */ 
2061                 p 
+= 2; *path 
= p
;                              /* Return pointer to the path name */ 
2062         } else {                                                        /* No drive number is given */ 
2064                 vol 
= CurrVol
;                                  /* Use current drive */ 
2066                 vol 
= 0;                                                /* Use drive 0 */ 
2070         /* Check if the file system object is valid or not */ 
2072         if (vol 
>= _VOLUMES
)                            /* Is the drive number valid? */ 
2073                 return FR_INVALID_DRIVE
; 
2074         fs 
= FatFs
[vol
];                                        /* Get corresponding file system object */ 
2075         if (!fs
) return FR_NOT_ENABLED
;         /* Is the file system object available? */ 
2077         ENTER_FF(fs
);                                           /* Lock file system */ 
2079         *rfs 
= fs
;                                                      /* Return pointer to the corresponding file system object */ 
2080         if (fs
->fs_type
) {                                      /* If the volume has been mounted */ 
2081                 stat 
= disk_status(fs
->drv
); 
2082                 if (!(stat 
& STA_NOINIT
)) {             /* and the physical drive is kept initialized (has not been changed), */ 
2083                         if (!_FS_READONLY 
&& wmode 
&& (stat 
& STA_PROTECT
))     /* Check write protection if needed */ 
2084                                 return FR_WRITE_PROTECTED
; 
2085                         return FR_OK
;                           /* The file system object is valid */ 
2089         /* The file system object is not valid. */ 
2090         /* Following code attempts to mount the volume. (analyze BPB and initialize the fs object) */ 
2092         fs
->fs_type 
= 0;                                        /* Clear the file system object */ 
2093         fs
->drv 
= LD2PD(vol
);                           /* Bind the logical drive and a physical drive */ 
2094         stat 
= disk_initialize(fs
->drv
);        /* Initialize the physical drive */ 
2095         if (stat 
& STA_NOINIT
)                          /* Check if the initialization succeeded */ 
2096                 return FR_NOT_READY
;                    /* Failed to initialize due to no medium or hard error */ 
2097         if (!_FS_READONLY 
&& wmode 
&& (stat 
& STA_PROTECT
))     /* Check disk write protection if needed */ 
2098                 return FR_WRITE_PROTECTED
; 
2099 #if _MAX_SS != 512                                              /* Get disk sector size (variable sector size cfg only) */ 
2100         if (disk_ioctl(fs
->drv
, GET_SECTOR_SIZE
, &fs
->ssize
) != RES_OK
) 
2103         /* Search FAT partition on the drive. Supports only generic partitions, FDISK and SFD. */ 
2104         fmt 
= check_fs(fs
, bsect 
= 0);          /* Load sector 0 and check if it is an FAT-VBR (in SFD) */ 
2105         if (LD2PT(vol
) && !fmt
) fmt 
= 1;        /* Force non-SFD if the volume is forced partition */ 
2106         if (fmt 
== 1) {                                         /* Not an FAT-VBR, the physical drive can be partitioned */ 
2107                 /* Check the partition listed in the partition table */ 
2110                 tbl 
= &fs
->win
[MBR_Table 
+ pi 
* SZ_PTE
];/* Partition table */ 
2111                 if (tbl
[4]) {                                           /* Is the partition existing? */ 
2112                         bsect 
= LD_DWORD(&tbl
[8]);              /* Partition offset in LBA */ 
2113                         fmt 
= check_fs(fs
, bsect
);              /* Check the partition */ 
2116         if (fmt 
== 3) return FR_DISK_ERR
; 
2117         if (fmt
) return FR_NO_FILESYSTEM
;               /* No FAT volume is found */ 
2119         /* An FAT volume is found. Following code initializes the file system object */ 
2121         if (LD_WORD(fs
->win
+BPB_BytsPerSec
) != SS(fs
))          /* (BPB_BytsPerSec must be equal to the physical sector size) */ 
2122                 return FR_NO_FILESYSTEM
; 
2124         fasize 
= LD_WORD(fs
->win
+BPB_FATSz16
);                          /* Number of sectors per FAT */ 
2125         if (!fasize
) fasize 
= LD_DWORD(fs
->win
+BPB_FATSz32
); 
2128         fs
->n_fats 
= b 
= fs
->win
[BPB_NumFATs
];                          /* Number of FAT copies */ 
2129         if (b 
!= 1 && b 
!= 2) return FR_NO_FILESYSTEM
;          /* (Must be 1 or 2) */ 
2130         fasize 
*= b
;                                                                            /* Number of sectors for FAT area */ 
2132         fs
->csize 
= b 
= fs
->win
[BPB_SecPerClus
];                        /* Number of sectors per cluster */ 
2133         if (!b 
|| (b 
& (b 
- 1))) return FR_NO_FILESYSTEM
;       /* (Must be power of 2) */ 
2135         fs
->n_rootdir 
= LD_WORD(fs
->win
+BPB_RootEntCnt
);        /* Number of root directory entries */ 
2136         if (fs
->n_rootdir 
% (SS(fs
) / SZ_DIR
)) return FR_NO_FILESYSTEM
; /* (BPB_RootEntCnt must be sector aligned) */ 
2138         tsect 
= LD_WORD(fs
->win
+BPB_TotSec16
);                          /* Number of sectors on the volume */ 
2139         if (!tsect
) tsect 
= LD_DWORD(fs
->win
+BPB_TotSec32
); 
2141         nrsv 
= LD_WORD(fs
->win
+BPB_RsvdSecCnt
);                         /* Number of reserved sectors */ 
2142         if (!nrsv
) return FR_NO_FILESYSTEM
;                                     /* (BPB_RsvdSecCnt must not be 0) */ 
2144         /* Determine the FAT sub type */ 
2145         sysect 
= nrsv 
+ fasize 
+ fs
->n_rootdir 
/ (SS(fs
) / SZ_DIR
);     /* RSV+FAT+DIR */ 
2146         if (tsect 
< sysect
) return FR_NO_FILESYSTEM
;            /* (Invalid volume size) */ 
2147         nclst 
= (tsect 
- sysect
) / fs
->csize
;                           /* Number of clusters */ 
2148         if (!nclst
) return FR_NO_FILESYSTEM
;                            /* (Invalid volume size) */ 
2150         if (nclst 
>= MIN_FAT16
) fmt 
= FS_FAT16
; 
2151         if (nclst 
>= MIN_FAT32
) fmt 
= FS_FAT32
; 
2153         /* Boundaries and Limits */ 
2154         fs
->n_fatent 
= nclst 
+ 2;                                                       /* Number of FAT entries */ 
2155         fs
->database 
= bsect 
+ sysect
;                                          /* Data start sector */ 
2156         fs
->fatbase 
= bsect 
+ nrsv
;                                             /* FAT start sector */ 
2157         if (fmt 
== FS_FAT32
) { 
2158                 if (fs
->n_rootdir
) return FR_NO_FILESYSTEM
;             /* (BPB_RootEntCnt must be 0) */ 
2159                 fs
->dirbase 
= LD_DWORD(fs
->win
+BPB_RootClus
);   /* Root directory start cluster */ 
2160                 szbfat 
= fs
->n_fatent 
* 4;                                              /* (Required FAT size) */ 
2162                 if (!fs
->n_rootdir
)     return FR_NO_FILESYSTEM
;        /* (BPB_RootEntCnt must not be 0) */ 
2163                 fs
->dirbase 
= fs
->fatbase 
+ fasize
;                             /* Root directory start sector */ 
2164                 szbfat 
= (fmt 
== FS_FAT16
) ?                                    
/* (Required FAT size) */ 
2165                         fs
->n_fatent 
* 2 : fs
->n_fatent 
* 3 / 2 + (fs
->n_fatent 
& 1); 
2167         if (fs
->fsize 
< (szbfat 
+ (SS(fs
) - 1)) / SS(fs
))       /* (BPB_FATSz must not be less than required) */ 
2168                 return FR_NO_FILESYSTEM
; 
2171         /* Initialize cluster allocation information */ 
2172         fs
->free_clust 
= 0xFFFFFFFF; 
2175         /* Get fsinfo if available */ 
2176         if (fmt 
== FS_FAT32
) { 
2178                 fs
->fsi_sector 
= bsect 
+ LD_WORD(fs
->win
+BPB_FSInfo
); 
2179                 if (disk_read(fs
->drv
, fs
->win
, fs
->fsi_sector
, 1) == RES_OK 
&& 
2180                         LD_WORD(fs
->win
+BS_55AA
) == 0xAA55 && 
2181                         LD_DWORD(fs
->win
+FSI_LeadSig
) == 0x41615252 && 
2182                         LD_DWORD(fs
->win
+FSI_StrucSig
) == 0x61417272) { 
2183                                 fs
->last_clust 
= LD_DWORD(fs
->win
+FSI_Nxt_Free
); 
2184                                 fs
->free_clust 
= LD_DWORD(fs
->win
+FSI_Free_Count
); 
2188         fs
->fs_type 
= fmt
;              /* FAT sub-type */ 
2189         fs
->id 
= ++Fsid
;                /* File system mount ID */ 
2190         fs
->winsect 
= 0;                /* Invalidate sector cache */ 
2193         fs
->cdir 
= 0;                   /* Current directory (root dir) */ 
2195 #if _FS_LOCK                            /* Clear file lock semaphores */ 
2205 /*-----------------------------------------------------------------------*/ 
2206 /* Check if the file/dir object is valid or not                          */ 
2207 /*-----------------------------------------------------------------------*/ 
2210 FRESULT 
validate (      /* FR_OK(0): The object is valid, !=0: Invalid */ 
2211         void* obj               
/* Pointer to the object FIL/DIR to check validity */ 
2217         fil 
= (FIL
*)obj
;        /* Assuming offset of fs and id in the FIL/DIR is identical */ 
2218         if (!fil
->fs 
|| !fil
->fs
->fs_type 
|| fil
->fs
->id 
!= fil
->id
) 
2219                 return FR_INVALID_OBJECT
; 
2221         ENTER_FF(fil
->fs
);              /* Lock file system */ 
2223         if (disk_status(fil
->fs
->drv
) & STA_NOINIT
) 
2224                 return FR_NOT_READY
; 
2232 /*-------------------------------------------------------------------------- 
2236 --------------------------------------------------------------------------*/ 
2240 /*-----------------------------------------------------------------------*/ 
2241 /* Mount/Unmount a Logical Drive                                         */ 
2242 /*-----------------------------------------------------------------------*/ 
2245         BYTE vol
,               /* Logical drive number to be mounted/unmounted */ 
2246         FATFS 
*fs               
/* Pointer to new file system object (NULL for unmount)*/ 
2252         if (vol 
>= _VOLUMES
)            /* Check if the drive number is valid */ 
2253                 return FR_INVALID_DRIVE
; 
2254         rfs 
= FatFs
[vol
];                       /* Get current fs object */ 
2260 #if _FS_REENTRANT                               /* Discard sync object of the current volume */ 
2261                 if (!ff_del_syncobj(rfs
->sobj
)) return FR_INT_ERR
; 
2263                 rfs
->fs_type 
= 0;               /* Clear old fs object */ 
2267                 fs
->fs_type 
= 0;                /* Clear new fs object */ 
2268 #if _FS_REENTRANT                               /* Create sync object for the new volume */ 
2269                 if (!ff_cre_syncobj(vol
, &fs
->sobj
)) return FR_INT_ERR
; 
2272         FatFs
[vol
] = fs
;                        /* Register new fs object */ 
2280 /*-----------------------------------------------------------------------*/ 
2281 /* Open or Create a File                                                 */ 
2282 /*-----------------------------------------------------------------------*/ 
2285         FIL 
*fp
,                        /* Pointer to the blank file object */ 
2286         const TCHAR 
*path
,      /* Pointer to the file name */ 
2287         BYTE mode                       
/* Access mode and file open mode flags */ 
2296         if (!fp
) return FR_INVALID_OBJECT
; 
2297         fp
->fs 
= 0;                     /* Clear file object */ 
2300         mode 
&= FA_READ 
| FA_WRITE 
| FA_CREATE_ALWAYS 
| FA_OPEN_ALWAYS 
| FA_CREATE_NEW
; 
2301         res 
= chk_mounted(&path
, &dj
.fs
, (BYTE
)(mode 
& ~FA_READ
)); 
2304         res 
= chk_mounted(&path
, &dj
.fs
, 0); 
2308                 res 
= follow_path(&dj
, path
);   /* Follow the file path */ 
2310 #if !_FS_READONLY       /* R/W configuration */ 
2312                         if (!dir
)       /* Current dir itself */ 
2313                                 res 
= FR_INVALID_NAME
; 
2316                                 res 
= chk_lock(&dj
, (mode 
& ~FA_READ
) ? 
1 : 0); 
2319                 /* Create or Open a file */ 
2320                 if (mode 
& (FA_CREATE_ALWAYS 
| FA_OPEN_ALWAYS 
| FA_CREATE_NEW
)) { 
2323                         if (res 
!= FR_OK
) {                                     /* No file, create new */ 
2324                                 if (res 
== FR_NO_FILE
)                  /* There is no file to open, create a new entry */ 
2326                                         res 
= enq_lock() ? 
dir_register(&dj
) : FR_TOO_MANY_OPEN_FILES
; 
2328                                         res 
= dir_register(&dj
); 
2330                                 mode 
|= FA_CREATE_ALWAYS
;               /* File is created */ 
2331                                 dir 
= dj
.dir
;                                   /* New entry */ 
2333                         else {                                                          /* Any object is already existing */ 
2334                                 if (dir
[DIR_Attr
] & (AM_RDO 
| AM_DIR
)) {        /* Cannot overwrite it (R/O or DIR) */ 
2337                                         if (mode 
& FA_CREATE_NEW
)       /* Cannot create as new file */ 
2341                         if (res 
== FR_OK 
&& (mode 
& FA_CREATE_ALWAYS
)) {        /* Truncate it if overwrite mode */ 
2342                                 dw 
= get_fattime();                                     /* Created time */ 
2343                                 ST_DWORD(dir
+DIR_CrtTime
, dw
); 
2344                                 dir
[DIR_Attr
] = 0;                                      /* Reset attribute */ 
2345                                 ST_DWORD(dir
+DIR_FileSize
, 0);          /* size = 0 */ 
2346                                 cl 
= ld_clust(dj
.fs
, dir
);                      /* Get start cluster */ 
2347                                 st_clust(dir
, 0);                                       /* cluster = 0 */ 
2349                                 if (cl
) {                                                       /* Remove the cluster chain if exist */ 
2350                                         dw 
= dj
.fs
->winsect
; 
2351                                         res 
= remove_chain(dj
.fs
, cl
); 
2353                                                 dj
.fs
->last_clust 
= cl 
- 1;     /* Reuse the cluster hole */ 
2354                                                 res 
= move_window(dj
.fs
, dw
); 
2359                 else {  /* Open an existing file */ 
2360                         if (res 
== FR_OK
) {                                             /* Follow succeeded */ 
2361                                 if (dir
[DIR_Attr
] & AM_DIR
) {           /* It is a directory */ 
2364                                         if ((mode 
& FA_WRITE
) && (dir
[DIR_Attr
] & AM_RDO
)) /* R/O violation */ 
2370                         if (mode 
& FA_CREATE_ALWAYS
)                    /* Set file change flag if created or overwritten */ 
2371                                 mode 
|= FA__WRITTEN
; 
2372                         fp
->dir_sect 
= dj
.fs
->winsect
;                  /* Pointer to the directory entry */ 
2375                         fp
->lockid 
= inc_lock(&dj
, (mode 
& ~FA_READ
) ? 
1 : 0); 
2376                         if (!fp
->lockid
) res 
= FR_INT_ERR
; 
2380 #else                           /* R/O configuration */ 
2381                 if (res 
== FR_OK
) {                                     /* Follow succeeded */ 
2383                         if (!dir
) {                                             /* Current dir itself */ 
2384                                 res 
= FR_INVALID_NAME
; 
2386                                 if (dir
[DIR_Attr
] & AM_DIR
)     /* It is a directory */ 
2394                         fp
->flag 
= mode
;                                        /* File access mode */ 
2395                         fp
->sclust 
= ld_clust(dj
.fs
, dir
);      /* File start cluster */ 
2396                         fp
->fsize 
= LD_DWORD(dir
+DIR_FileSize
); /* File size */ 
2397                         fp
->fptr 
= 0;                                           /* File pointer */ 
2400                         fp
->cltbl 
= 0;                                          /* Normal seek mode */ 
2402                         fp
->fs 
= dj
.fs
; fp
->id 
= dj
.fs
->id
;     /* Validate file object */ 
2406         LEAVE_FF(dj
.fs
, res
); 
2412 /*-----------------------------------------------------------------------*/ 
2414 /*-----------------------------------------------------------------------*/ 
2417         FIL 
*fp
,                /* Pointer to the file object */ 
2418         void *buff
,             /* Pointer to data buffer */ 
2419         UINT btr
,               /* Number of bytes to read */ 
2420         UINT 
*br                
/* Pointer to number of bytes read */ 
2424         DWORD clst
, sect
, remain
; 
2426         BYTE csect
, *rbuff 
= buff
; 
2429         *br 
= 0;        /* Clear read byte counter */ 
2431         res 
= validate(fp
);                                                     /* Check validity */ 
2432         if (res 
!= FR_OK
) LEAVE_FF(fp
->fs
, res
); 
2433         if (fp
->flag 
& FA__ERROR
)                                       /* Aborted file? */ 
2434                 LEAVE_FF(fp
->fs
, FR_INT_ERR
); 
2435         if (!(fp
->flag 
& FA_READ
))                                      /* Check access mode */ 
2436                 LEAVE_FF(fp
->fs
, FR_DENIED
); 
2437         remain 
= fp
->fsize 
- fp
->fptr
; 
2438         if (btr 
> remain
) btr 
= (UINT
)remain
;           /* Truncate btr by remaining bytes */ 
2440         for ( ;  btr
;                                                           /* Repeat until all data read */ 
2441                 rbuff 
+= rcnt
, fp
->fptr 
+= rcnt
, *br 
+= rcnt
, btr 
-= rcnt
) { 
2442                 if ((fp
->fptr 
% SS(fp
->fs
)) == 0) {             /* On the sector boundary? */ 
2443                         csect 
= (BYTE
)(fp
->fptr 
/ SS(fp
->fs
) & (fp
->fs
->csize 
- 1));    /* Sector offset in the cluster */ 
2444                         if (!csect
) {                                           /* On the cluster boundary? */ 
2445                                 if (fp
->fptr 
== 0) {                    /* On the top of the file? */ 
2446                                         clst 
= fp
->sclust
;                      /* Follow from the origin */ 
2447                                 } else {                                                /* Middle or end of the file */ 
2450                                                 clst 
= clmt_clust(fp
, fp
->fptr
);        /* Get cluster# from the CLMT */ 
2453                                                 clst 
= get_fat(fp
->fs
, fp
->clust
);      /* Follow cluster chain on the FAT */ 
2455                                 if (clst 
< 2) ABORT(fp
->fs
, FR_INT_ERR
); 
2456                                 if (clst 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
2457                                 fp
->clust 
= clst
;                               /* Update current cluster */ 
2459                         sect 
= clust2sect(fp
->fs
, fp
->clust
);   /* Get current sector */ 
2460                         if (!sect
) ABORT(fp
->fs
, FR_INT_ERR
); 
2462                         cc 
= btr 
/ SS(fp
->fs
);                          /* When remaining bytes >= sector size, */ 
2463                         if (cc
) {                                                       /* Read maximum contiguous sectors directly */ 
2464                                 if (csect 
+ cc 
> fp
->fs
->csize
) /* Clip at cluster boundary */ 
2465                                         cc 
= fp
->fs
->csize 
- csect
; 
2466                                 if (disk_read(fp
->fs
->drv
, rbuff
, sect
, (BYTE
)cc
) != RES_OK
) 
2467                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2468 #if !_FS_READONLY && _FS_MINIMIZE <= 2                  /* Replace one of the read sectors with cached data if it contains a dirty sector */ 
2470                                 if (fp
->fs
->wflag 
&& fp
->fs
->winsect 
- sect 
< cc
) 
2471                                         mem_cpy(rbuff 
+ ((fp
->fs
->winsect 
- sect
) * SS(fp
->fs
)), fp
->fs
->win
, SS(fp
->fs
)); 
2473                                 if ((fp
->flag 
& FA__DIRTY
) && fp
->dsect 
- sect 
< cc
) 
2474                                         mem_cpy(rbuff 
+ ((fp
->dsect 
- sect
) * SS(fp
->fs
)), fp
->buf
, SS(fp
->fs
)); 
2477                                 rcnt 
= SS(fp
->fs
) * cc
;                 /* Number of bytes transferred */ 
2481                         if (fp
->dsect 
!= sect
) {                        /* Load data sector if not in cache */ 
2483                                 if (fp
->flag 
& FA__DIRTY
) {             /* Write-back dirty sector cache */ 
2484                                         if (disk_write(fp
->fs
->drv
, fp
->buf
, fp
->dsect
, 1) != RES_OK
) 
2485                                                 ABORT(fp
->fs
, FR_DISK_ERR
); 
2486                                         fp
->flag 
&= ~FA__DIRTY
; 
2489                                 if (disk_read(fp
->fs
->drv
, fp
->buf
, sect
, 1) != RES_OK
) /* Fill sector cache */ 
2490                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2495                 rcnt 
= SS(fp
->fs
) - ((UINT
)fp
->fptr 
% SS(fp
->fs
));      /* Get partial sector data from sector buffer */ 
2496                 if (rcnt 
> btr
) rcnt 
= btr
; 
2498                 if (move_window(fp
->fs
, fp
->dsect
))             /* Move sector window */ 
2499                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2500                 mem_cpy(rbuff
, &fp
->fs
->win
[fp
->fptr 
% SS(fp
->fs
)], rcnt
);      /* Pick partial sector */ 
2502                 mem_cpy(rbuff
, &fp
->buf
[fp
->fptr 
% SS(fp
->fs
)], rcnt
);  /* Pick partial sector */ 
2506         LEAVE_FF(fp
->fs
, FR_OK
); 
2513 /*-----------------------------------------------------------------------*/ 
2515 /*-----------------------------------------------------------------------*/ 
2518         FIL 
*fp
,                        /* Pointer to the file object */ 
2519         const void *buff
,       /* Pointer to the data to be written */ 
2520         UINT btw
,                       /* Number of bytes to write */ 
2521         UINT 
*bw                        
/* Pointer to number of bytes written */ 
2527         const BYTE 
*wbuff 
= buff
; 
2531         *bw 
= 0;        /* Clear write byte counter */ 
2533         res 
= validate(fp
);                                             /* Check validity */ 
2534         if (res 
!= FR_OK
) LEAVE_FF(fp
->fs
, res
); 
2535         if (fp
->flag 
& FA__ERROR
)                               /* Aborted file? */ 
2536                 LEAVE_FF(fp
->fs
, FR_INT_ERR
); 
2537         if (!(fp
->flag 
& FA_WRITE
))                             /* Check access mode */ 
2538                 LEAVE_FF(fp
->fs
, FR_DENIED
); 
2539         if ((DWORD
)(fp
->fsize 
+ btw
) < fp
->fsize
) btw 
= 0;      /* File size cannot reach 4GB */ 
2541         for ( ;  btw
;                                                   /* Repeat until all data written */ 
2542                 wbuff 
+= wcnt
, fp
->fptr 
+= wcnt
, *bw 
+= wcnt
, btw 
-= wcnt
) { 
2543                 if ((fp
->fptr 
% SS(fp
->fs
)) == 0) {     /* On the sector boundary? */ 
2544                         csect 
= (BYTE
)(fp
->fptr 
/ SS(fp
->fs
) & (fp
->fs
->csize 
- 1));    /* Sector offset in the cluster */ 
2545                         if (!csect
) {                                   /* On the cluster boundary? */ 
2546                                 if (fp
->fptr 
== 0) {            /* On the top of the file? */ 
2547                                         clst 
= fp
->sclust
;              /* Follow from the origin */ 
2548                                         if (clst 
== 0)                  /* When no cluster is allocated, */ 
2549                                                 fp
->sclust 
= clst 
= create_chain(fp
->fs
, 0);    /* Create a new cluster chain */ 
2550                                 } else {                                        /* Middle or end of the file */ 
2553                                                 clst 
= clmt_clust(fp
, fp
->fptr
);        /* Get cluster# from the CLMT */ 
2556                                                 clst 
= create_chain(fp
->fs
, fp
->clust
); /* Follow or stretch cluster chain on the FAT */ 
2558                                 if (clst 
== 0) break;           /* Could not allocate a new cluster (disk full) */ 
2559                                 if (clst 
== 1) ABORT(fp
->fs
, FR_INT_ERR
); 
2560                                 if (clst 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
2561                                 fp
->clust 
= clst
;                       /* Update current cluster */ 
2564                         if (fp
->fs
->winsect 
== fp
->dsect 
&& move_window(fp
->fs
, 0))     /* Write-back sector cache */ 
2565                                 ABORT(fp
->fs
, FR_DISK_ERR
); 
2567                         if (fp
->flag 
& FA__DIRTY
) {             /* Write-back sector cache */ 
2568                                 if (disk_write(fp
->fs
->drv
, fp
->buf
, fp
->dsect
, 1) != RES_OK
) 
2569                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2570                                 fp
->flag 
&= ~FA__DIRTY
; 
2573                         sect 
= clust2sect(fp
->fs
, fp
->clust
);   /* Get current sector */ 
2574                         if (!sect
) ABORT(fp
->fs
, FR_INT_ERR
); 
2576                         cc 
= btw 
/ SS(fp
->fs
);                  /* When remaining bytes >= sector size, */ 
2577                         if (cc
) {                                               /* Write maximum contiguous sectors directly */ 
2578                                 if (csect 
+ cc 
> fp
->fs
->csize
) /* Clip at cluster boundary */ 
2579                                         cc 
= fp
->fs
->csize 
- csect
; 
2580                                 if (disk_write(fp
->fs
->drv
, wbuff
, sect
, (BYTE
)cc
) != RES_OK
) 
2581                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2583                                 if (fp
->fs
->winsect 
- sect 
< cc
) {      /* Refill sector cache if it gets invalidated by the direct write */ 
2584                                         mem_cpy(fp
->fs
->win
, wbuff 
+ ((fp
->fs
->winsect 
- sect
) * SS(fp
->fs
)), SS(fp
->fs
)); 
2588                                 if (fp
->dsect 
- sect 
< cc
) { /* Refill sector cache if it gets invalidated by the direct write */ 
2589                                         mem_cpy(fp
->buf
, wbuff 
+ ((fp
->dsect 
- sect
) * SS(fp
->fs
)), SS(fp
->fs
)); 
2590                                         fp
->flag 
&= ~FA__DIRTY
; 
2593                                 wcnt 
= SS(fp
->fs
) * cc
;         /* Number of bytes transferred */ 
2597                         if (fp
->fptr 
>= fp
->fsize
) {    /* Avoid silly cache filling at growing edge */ 
2598                                 if (move_window(fp
->fs
, 0)) ABORT(fp
->fs
, FR_DISK_ERR
); 
2599                                 fp
->fs
->winsect 
= sect
; 
2602                         if (fp
->dsect 
!= sect
) {                /* Fill sector cache with file data */ 
2603                                 if (fp
->fptr 
< fp
->fsize 
&& 
2604                                         disk_read(fp
->fs
->drv
, fp
->buf
, sect
, 1) != RES_OK
) 
2605                                                 ABORT(fp
->fs
, FR_DISK_ERR
); 
2610                 wcnt 
= SS(fp
->fs
) - ((UINT
)fp
->fptr 
% SS(fp
->fs
));/* Put partial sector into file I/O buffer */ 
2611                 if (wcnt 
> btw
) wcnt 
= btw
; 
2613                 if (move_window(fp
->fs
, fp
->dsect
))     /* Move sector window */ 
2614                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2615                 mem_cpy(&fp
->fs
->win
[fp
->fptr 
% SS(fp
->fs
)], wbuff
, wcnt
);      /* Fit partial sector */ 
2618                 mem_cpy(&fp
->buf
[fp
->fptr 
% SS(fp
->fs
)], wbuff
, wcnt
);  /* Fit partial sector */ 
2619                 fp
->flag 
|= FA__DIRTY
; 
2623         if (fp
->fptr 
> fp
->fsize
) fp
->fsize 
= fp
->fptr
; /* Update file size if needed */ 
2624         fp
->flag 
|= FA__WRITTEN
;                                                /* Set file change flag */ 
2626         LEAVE_FF(fp
->fs
, FR_OK
); 
2632 /*-----------------------------------------------------------------------*/ 
2633 /* Synchronize the File Object                                           */ 
2634 /*-----------------------------------------------------------------------*/ 
2637         FIL 
*fp         
/* Pointer to the file object */ 
2645         res 
= validate(fp
);                                     /* Check validity of the object */ 
2647                 if (fp
->flag 
& FA__WRITTEN
) {   /* Has the file been written? */ 
2648 #if !_FS_TINY   /* Write-back dirty buffer */ 
2649                         if (fp
->flag 
& FA__DIRTY
) { 
2650                                 if (disk_write(fp
->fs
->drv
, fp
->buf
, fp
->dsect
, 1) != RES_OK
) 
2651                                         LEAVE_FF(fp
->fs
, FR_DISK_ERR
); 
2652                                 fp
->flag 
&= ~FA__DIRTY
; 
2655                         /* Update the directory entry */ 
2656                         res 
= move_window(fp
->fs
, fp
->dir_sect
); 
2659                                 dir
[DIR_Attr
] |= AM_ARC
;                                        /* Set archive bit */ 
2660                                 ST_DWORD(dir
+DIR_FileSize
, fp
->fsize
);          /* Update file size */ 
2661                                 st_clust(dir
, fp
->sclust
);                                      /* Update start cluster */ 
2662                                 tim 
= get_fattime();                                            /* Update updated time */ 
2663                                 ST_DWORD(dir
+DIR_WrtTime
, tim
); 
2664                                 ST_WORD(dir
+DIR_LstAccDate
, 0); 
2665                                 fp
->flag 
&= ~FA__WRITTEN
; 
2672         LEAVE_FF(fp
->fs
, res
); 
2675 #endif /* !_FS_READONLY */ 
2680 /*-----------------------------------------------------------------------*/ 
2682 /*-----------------------------------------------------------------------*/ 
2685         FIL 
*fp         
/* Pointer to the file object to be closed */ 
2697                 if (res 
== FR_OK
) fp
->fs 
= 0;   /* Discard file object */ 
2701         res 
= f_sync(fp
);               /* Flush cached data */ 
2703         if (res 
== FR_OK
) {             /* Decrement open counter */ 
2705                 FATFS 
*fs 
= fp
->fs
;; 
2708                         res 
= dec_lock(fp
->lockid
);      
2709                         unlock_fs(fs
, FR_OK
); 
2712                 res 
= dec_lock(fp
->lockid
); 
2716         if (res 
== FR_OK
) fp
->fs 
= 0;   /* Discard file object */ 
2724 /*-----------------------------------------------------------------------*/ 
2725 /* Current Drive/Directory Handlings                                     */ 
2726 /*-----------------------------------------------------------------------*/ 
2731         BYTE drv                
/* Drive number */ 
2734         if (drv 
>= _VOLUMES
) return FR_INVALID_DRIVE
; 
2744         const TCHAR 
*path       
/* Pointer to the directory path */ 
2752         res 
= chk_mounted(&path
, &dj
.fs
, 0); 
2755                 res 
= follow_path(&dj
, path
);           /* Follow the path */ 
2757                 if (res 
== FR_OK
) {                                     /* Follow completed */ 
2759                                 dj
.fs
->cdir 
= dj
.sclust
;        /* Start directory itself */ 
2761                                 if (dj
.dir
[DIR_Attr
] & AM_DIR
)  /* Reached to the directory */ 
2762                                         dj
.fs
->cdir 
= ld_clust(dj
.fs
, dj
.dir
); 
2764                                         res 
= FR_NO_PATH
;               /* Reached but a file */ 
2767                 if (res 
== FR_NO_FILE
) res 
= FR_NO_PATH
; 
2770         LEAVE_FF(dj
.fs
, res
); 
2776         TCHAR 
*path
,    /* Pointer to the directory path */ 
2777         UINT sz_path    
/* Size of path */ 
2790         res 
= chk_mounted((const TCHAR
**)&path
, &dj
.fs
, 0);     /* Get current volume */ 
2793                 i 
= sz_path
;            /* Bottom of buffer (dir stack base) */ 
2794                 dj
.sclust 
= dj
.fs
->cdir
;                        /* Start to follow upper dir from current dir */ 
2795                 while ((ccl 
= dj
.sclust
) != 0) {        /* Repeat while current dir is a sub-dir */ 
2796                         res 
= dir_sdi(&dj
, 1);                  /* Get parent dir */ 
2797                         if (res 
!= FR_OK
) break; 
2798                         res 
= dir_read(&dj
); 
2799                         if (res 
!= FR_OK
) break; 
2800                         dj
.sclust 
= ld_clust(dj
.fs
, dj
.dir
);    /* Goto parent dir */ 
2801                         res 
= dir_sdi(&dj
, 0); 
2802                         if (res 
!= FR_OK
) break; 
2803                         do {                                                    /* Find the entry links to the child dir */ 
2804                                 res 
= dir_read(&dj
); 
2805                                 if (res 
!= FR_OK
) break; 
2806                                 if (ccl 
== ld_clust(dj
.fs
, dj
.dir
)) break;      /* Found the entry */ 
2807                                 res 
= dir_next(&dj
, 0);  
2808                         } while (res 
== FR_OK
); 
2809                         if (res 
== FR_NO_FILE
) res 
= FR_INT_ERR
;/* It cannot be 'not found'. */ 
2810                         if (res 
!= FR_OK
) break; 
2815                         get_fileinfo(&dj
, &fno
);                /* Get the dir name and push it to the buffer */ 
2817                         if (_USE_LFN 
&& *path
) tp 
= path
; 
2818                         for (n 
= 0; tp
[n
]; n
++) ; 
2820                                 res 
= FR_NOT_ENOUGH_CORE
; break; 
2822                         while (n
) path
[--i
] = tp
[--n
]; 
2827                         *tp
++ = '0' + CurrVol
;                  /* Put drive number */ 
2829                         if (i 
== sz_path
) {                             /* Root-dir */ 
2831                         } else {                                                /* Sub-dir */ 
2832                                 do              /* Add stacked path str */ 
2834                                 while (i 
< sz_path
); 
2841         LEAVE_FF(dj
.fs
, res
); 
2843 #endif /* _FS_RPATH >= 2 */ 
2844 #endif /* _FS_RPATH >= 1 */ 
2848 #if _FS_MINIMIZE <= 2 
2849 /*-----------------------------------------------------------------------*/ 
2850 /* Seek File R/W Pointer                                                 */ 
2851 /*-----------------------------------------------------------------------*/ 
2854         FIL 
*fp
,                /* Pointer to the file object */ 
2855         DWORD ofs               
/* File pointer from top of file */ 
2861         res 
= validate(fp
);                                     /* Check validity of the object */ 
2862         if (res 
!= FR_OK
) LEAVE_FF(fp
->fs
, res
); 
2863         if (fp
->flag 
& FA__ERROR
)                       /* Check abort flag */ 
2864                 LEAVE_FF(fp
->fs
, FR_INT_ERR
); 
2867         if (fp
->cltbl
) {        /* Fast seek */ 
2868                 DWORD cl
, pcl
, ncl
, tcl
, dsc
, tlen
, ulen
, *tbl
; 
2870                 if (ofs 
== CREATE_LINKMAP
) {    /* Create CLMT */ 
2872                         tlen 
= *tbl
++; ulen 
= 2;        /* Given table size and required table size */ 
2873                         cl 
= fp
->sclust
;                        /* Top of the chain */ 
2876                                         /* Get a fragment */ 
2877                                         tcl 
= cl
; ncl 
= 0; ulen 
+= 2;   /* Top, length and used items */ 
2880                                                 cl 
= get_fat(fp
->fs
, cl
); 
2881                                                 if (cl 
<= 1) ABORT(fp
->fs
, FR_INT_ERR
); 
2882                                                 if (cl 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
2883                                         } while (cl 
== pcl 
+ 1); 
2884                                         if (ulen 
<= tlen
) {             /* Store the length and top of the fragment */ 
2885                                                 *tbl
++ = ncl
; *tbl
++ = tcl
; 
2887                                 } while (cl 
< fp
->fs
->n_fatent
);        /* Repeat until end of chain */ 
2889                         *fp
->cltbl 
= ulen
;      /* Number of items used */ 
2891                                 *tbl 
= 0;               /* Terminate table */ 
2893                                 res 
= FR_NOT_ENOUGH_CORE
;       /* Given table size is smaller than required */ 
2895                 } else {                                                /* Fast seek */ 
2896                         if (ofs 
> fp
->fsize
)            /* Clip offset at the file size */ 
2898                         fp
->fptr 
= ofs
;                         /* Set file pointer */ 
2900                                 fp
->clust 
= clmt_clust(fp
, ofs 
- 1); 
2901                                 dsc 
= clust2sect(fp
->fs
, fp
->clust
); 
2902                                 if (!dsc
) ABORT(fp
->fs
, FR_INT_ERR
); 
2903                                 dsc 
+= (ofs 
- 1) / SS(fp
->fs
) & (fp
->fs
->csize 
- 1); 
2904                                 if (fp
->fptr 
% SS(fp
->fs
) && dsc 
!= fp
->dsect
) {        /* Refill sector cache if needed */ 
2907                                         if (fp
->flag 
& FA__DIRTY
) {             /* Write-back dirty sector cache */ 
2908                                                 if (disk_write(fp
->fs
->drv
, fp
->buf
, fp
->dsect
, 1) != RES_OK
) 
2909                                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2910                                                 fp
->flag 
&= ~FA__DIRTY
; 
2913                                         if (disk_read(fp
->fs
->drv
, fp
->buf
, dsc
, 1) != RES_OK
)  /* Load current sector */ 
2914                                                 ABORT(fp
->fs
, FR_DISK_ERR
); 
2925                 DWORD clst
, bcs
, nsect
, ifptr
; 
2927                 if (ofs 
> fp
->fsize                                     
/* In read-only mode, clip offset with the file size */ 
2929                          && !(fp
->flag 
& FA_WRITE
) 
2934                 fp
->fptr 
= nsect 
= 0; 
2936                         bcs 
= (DWORD
)fp
->fs
->csize 
* SS(fp
->fs
);        /* Cluster size (byte) */ 
2938                                 (ofs 
- 1) / bcs 
>= (ifptr 
- 1) / bcs
) { /* When seek to same or following cluster, */ 
2939                                 fp
->fptr 
= (ifptr 
- 1) & ~(bcs 
- 1);    /* start from the current cluster */ 
2942                         } else {                                                                        /* When seek to back cluster, */ 
2943                                 clst 
= fp
->sclust
;                                              /* start from the first cluster */ 
2945                                 if (clst 
== 0) {                                                /* If no cluster chain, create a new chain */ 
2946                                         clst 
= create_chain(fp
->fs
, 0); 
2947                                         if (clst 
== 1) ABORT(fp
->fs
, FR_INT_ERR
); 
2948                                         if (clst 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
2955                                 while (ofs 
> bcs
) {                                             /* Cluster following loop */ 
2957                                         if (fp
->flag 
& FA_WRITE
) {                      /* Check if in write mode or not */ 
2958                                                 clst 
= create_chain(fp
->fs
, clst
);      /* Force stretch if in write mode */ 
2959                                                 if (clst 
== 0) {                                /* When disk gets full, clip file size */ 
2964                                                 clst 
= get_fat(fp
->fs
, clst
);   /* Follow cluster chain if not in write mode */ 
2965                                         if (clst 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
2966                                         if (clst 
<= 1 || clst 
>= fp
->fs
->n_fatent
) ABORT(fp
->fs
, FR_INT_ERR
); 
2972                                 if (ofs 
% SS(fp
->fs
)) { 
2973                                         nsect 
= clust2sect(fp
->fs
, clst
);       /* Current sector */ 
2974                                         if (!nsect
) ABORT(fp
->fs
, FR_INT_ERR
); 
2975                                         nsect 
+= ofs 
/ SS(fp
->fs
); 
2979                 if (fp
->fptr 
% SS(fp
->fs
) && nsect 
!= fp
->dsect
) {      /* Fill sector cache if needed */ 
2982                         if (fp
->flag 
& FA__DIRTY
) {                     /* Write-back dirty sector cache */ 
2983                                 if (disk_write(fp
->fs
->drv
, fp
->buf
, fp
->dsect
, 1) != RES_OK
) 
2984                                         ABORT(fp
->fs
, FR_DISK_ERR
); 
2985                                 fp
->flag 
&= ~FA__DIRTY
; 
2988                         if (disk_read(fp
->fs
->drv
, fp
->buf
, nsect
, 1) != RES_OK
)        /* Fill sector cache */ 
2989                                 ABORT(fp
->fs
, FR_DISK_ERR
); 
2994                 if (fp
->fptr 
> fp
->fsize
) {                     /* Set file change flag if the file size is extended */ 
2995                         fp
->fsize 
= fp
->fptr
; 
2996                         fp
->flag 
|= FA__WRITTEN
; 
3001         LEAVE_FF(fp
->fs
, res
); 
3006 #if _FS_MINIMIZE <= 1 
3007 /*-----------------------------------------------------------------------*/ 
3008 /* Create a Directory Object                                             */ 
3009 /*-----------------------------------------------------------------------*/ 
3012         DIR *dj
,                        /* Pointer to directory object to create */ 
3013         const TCHAR 
*path       
/* Pointer to the directory path */ 
3021         if (!dj
) return FR_INVALID_OBJECT
; 
3023         res 
= chk_mounted(&path
, &dj
->fs
, 0); 
3027                 res 
= follow_path(dj
, path
);                    /* Follow the path to the directory */ 
3029                 if (res 
== FR_OK
) {                                             /* Follow completed */ 
3030                         if (dj
->dir
) {                                          /* It is not the root dir */ 
3031                                 if (dj
->dir
[DIR_Attr
] & AM_DIR
) {       /* The object is a directory */ 
3032                                         dj
->sclust 
= ld_clust(fs
, dj
->dir
); 
3033                                 } else {                                                /* The object is not a directory */ 
3039                                 res 
= dir_sdi(dj
, 0);                   /* Rewind dir */ 
3042                 if (res 
== FR_NO_FILE
) res 
= FR_NO_PATH
; 
3043                 if (res 
!= FR_OK
) dj
->fs 
= 0;                   /* Invalidate the dir object if function faild */ 
3054 /*-----------------------------------------------------------------------*/ 
3055 /* Read Directory Entry in Sequence                                      */ 
3056 /*-----------------------------------------------------------------------*/ 
3059         DIR *dj
,                        /* Pointer to the open directory object */ 
3060         FILINFO 
*fno            
/* Pointer to file information to return */ 
3067         res 
= validate(dj
);                                             /* Check validity of the object */ 
3070                         res 
= dir_sdi(dj
, 0);                   /* Rewind the directory object */ 
3073                         res 
= dir_read(dj
);                             /* Read an directory item */ 
3074                         if (res 
== FR_NO_FILE
) {                /* Reached end of dir */ 
3078                         if (res 
== FR_OK
) {                             /* A valid entry is found */ 
3079                                 get_fileinfo(dj
, fno
);          /* Get the object information */ 
3080                                 res 
= dir_next(dj
, 0);          /* Increment index for next */ 
3081                                 if (res 
== FR_NO_FILE
) { 
3090         LEAVE_FF(dj
->fs
, res
); 
3095 #if _FS_MINIMIZE == 0 
3096 /*-----------------------------------------------------------------------*/ 
3097 /* Get File Status                                                       */ 
3098 /*-----------------------------------------------------------------------*/ 
3101         const TCHAR 
*path
,      /* Pointer to the file path */ 
3102         FILINFO 
*fno            
/* Pointer to file information to return */ 
3110         res 
= chk_mounted(&path
, &dj
.fs
, 0); 
3113                 res 
= follow_path(&dj
, path
);   /* Follow the file path */ 
3114                 if (res 
== FR_OK
) {                             /* Follow completed */ 
3115                         if (dj
.dir
)             /* Found an object */ 
3116                                 get_fileinfo(&dj
, fno
); 
3117                         else                    /* It is root dir */ 
3118                                 res 
= FR_INVALID_NAME
; 
3123         LEAVE_FF(dj
.fs
, res
); 
3129 /*-----------------------------------------------------------------------*/ 
3130 /* Get Number of Free Clusters                                           */ 
3131 /*-----------------------------------------------------------------------*/ 
3134         const TCHAR 
*path
,      /* Pointer to the logical drive number (root dir) */ 
3135         DWORD 
*nclst
,           /* Pointer to the variable to return number of free clusters */ 
3136         FATFS 
**fatfs           
/* Pointer to pointer to corresponding file system object to return */ 
3141         DWORD n
, clst
, sect
, stat
; 
3146         /* Get drive number */ 
3147         res 
= chk_mounted(&path
, fatfs
, 0); 
3150                 /* If free_clust is valid, return it without full cluster scan */ 
3151                 if (fs
->free_clust 
<= fs
->n_fatent 
- 2) { 
3152                         *nclst 
= fs
->free_clust
; 
3154                         /* Get number of free clusters */ 
3157                         if (fat 
== FS_FAT12
) { 
3160                                         stat 
= get_fat(fs
, clst
); 
3161                                         if (stat 
== 0xFFFFFFFF) { res 
= FR_DISK_ERR
; break; } 
3162                                         if (stat 
== 1) { res 
= FR_INT_ERR
; break; } 
3164                                 } while (++clst 
< fs
->n_fatent
); 
3166                                 clst 
= fs
->n_fatent
; 
3171                                                 res 
= move_window(fs
, sect
++); 
3172                                                 if (res 
!= FR_OK
) break; 
3176                                         if (fat 
== FS_FAT16
) { 
3177                                                 if (LD_WORD(p
) == 0) n
++; 
3180                                                 if ((LD_DWORD(p
) & 0x0FFFFFFF) == 0) n
++; 
3186                         if (fat 
== FS_FAT32
) fs
->fsi_flag 
= 1; 
3196 /*-----------------------------------------------------------------------*/ 
3198 /*-----------------------------------------------------------------------*/ 
3200 FRESULT 
f_truncate ( 
3201         FIL 
*fp         
/* Pointer to the file object */ 
3208         if (!fp
) return FR_INVALID_OBJECT
; 
3210         res 
= validate(fp
);                                             /* Check validity of the object */ 
3212                 if (fp
->flag 
& FA__ERROR
) {                     /* Check abort flag */ 
3215                         if (!(fp
->flag 
& FA_WRITE
))             /* Check access mode */ 
3220                 if (fp
->fsize 
> fp
->fptr
) { 
3221                         fp
->fsize 
= fp
->fptr
;   /* Set file size to current R/W point */ 
3222                         fp
->flag 
|= FA__WRITTEN
; 
3223                         if (fp
->fptr 
== 0) {    /* When set file size to zero, remove entire cluster chain */ 
3224                                 res 
= remove_chain(fp
->fs
, fp
->sclust
); 
3226                         } else {                                /* When truncate a part of the file, remove remaining clusters */ 
3227                                 ncl 
= get_fat(fp
->fs
, fp
->clust
); 
3229                                 if (ncl 
== 0xFFFFFFFF) res 
= FR_DISK_ERR
; 
3230                                 if (ncl 
== 1) res 
= FR_INT_ERR
; 
3231                                 if (res 
== FR_OK 
&& ncl 
< fp
->fs
->n_fatent
) { 
3232                                         res 
= put_fat(fp
->fs
, fp
->clust
, 0x0FFFFFFF); 
3233                                         if (res 
== FR_OK
) res 
= remove_chain(fp
->fs
, ncl
); 
3237                 if (res 
!= FR_OK
) fp
->flag 
|= FA__ERROR
; 
3240         LEAVE_FF(fp
->fs
, res
); 
3246 /*-----------------------------------------------------------------------*/ 
3247 /* Delete a File or Directory                                            */ 
3248 /*-----------------------------------------------------------------------*/ 
3251         const TCHAR 
*path               
/* Pointer to the file or directory path */ 
3261         res 
= chk_mounted(&path
, &dj
.fs
, 1); 
3264                 res 
= follow_path(&dj
, path
);           /* Follow the file path */ 
3265                 if (_FS_RPATH 
&& res 
== FR_OK 
&& (dj
.fn
[NS
] & NS_DOT
)) 
3266                         res 
= FR_INVALID_NAME
;                  /* Cannot remove dot entry */ 
3268                 if (res 
== FR_OK
) res 
= chk_lock(&dj
, 2);       /* Cannot remove open file */ 
3270                 if (res 
== FR_OK
) {                                     /* The object is accessible */ 
3273                                 res 
= FR_INVALID_NAME
;          /* Cannot remove the start directory */ 
3275                                 if (dir
[DIR_Attr
] & AM_RDO
) 
3276                                         res 
= FR_DENIED
;                /* Cannot remove R/O object */ 
3278                         dclst 
= ld_clust(dj
.fs
, dir
); 
3279                         if (res 
== FR_OK 
&& (dir
[DIR_Attr
] & AM_DIR
)) { /* Is it a sub-dir? */ 
3283                                         mem_cpy(&sdj
, &dj
, sizeof (DIR));       /* Check if the sub-dir is empty or not */ 
3285                                         res 
= dir_sdi(&sdj
, 2);         /* Exclude dot entries */ 
3287                                                 res 
= dir_read(&sdj
); 
3288                                                 if (res 
== FR_OK                
/* Not empty dir */ 
3290                                                 || dclst 
== dj
.fs
->cdir 
/* Current dir */ 
3293                                                 if (res 
== FR_NO_FILE
) res 
= FR_OK
;     /* Empty */ 
3298                                 res 
= dir_remove(&dj
);          /* Remove the directory entry */ 
3300                                         if (dclst
)                              /* Remove the cluster chain if exist */ 
3301                                                 res 
= remove_chain(dj
.fs
, dclst
); 
3302                                         if (res 
== FR_OK
) res 
= sync(dj
.fs
); 
3308         LEAVE_FF(dj
.fs
, res
); 
3314 /*-----------------------------------------------------------------------*/ 
3315 /* Create a Directory                                                    */ 
3316 /*-----------------------------------------------------------------------*/ 
3319         const TCHAR 
*path               
/* Pointer to the directory path */ 
3325         DWORD dsc
, dcl
, pcl
, tim 
= get_fattime(); 
3329         res 
= chk_mounted(&path
, &dj
.fs
, 1); 
3332                 res 
= follow_path(&dj
, path
);                   /* Follow the file path */ 
3333                 if (res 
== FR_OK
) res 
= FR_EXIST
;               /* Any object with same name is already existing */ 
3334                 if (_FS_RPATH 
&& res 
== FR_NO_FILE 
&& (dj
.fn
[NS
] & NS_DOT
)) 
3335                         res 
= FR_INVALID_NAME
; 
3336                 if (res 
== FR_NO_FILE
) {                                /* Can create a new directory */ 
3337                         dcl 
= create_chain(dj
.fs
, 0);           /* Allocate a cluster for the new directory table */ 
3339                         if (dcl 
== 0) res 
= FR_DENIED
;          /* No space to allocate a new cluster */ 
3340                         if (dcl 
== 1) res 
= FR_INT_ERR
; 
3341                         if (dcl 
== 0xFFFFFFFF) res 
= FR_DISK_ERR
; 
3342                         if (res 
== FR_OK
)                                       /* Flush FAT */ 
3343                                 res 
= move_window(dj
.fs
, 0); 
3344                         if (res 
== FR_OK
) {                                     /* Initialize the new directory table */ 
3345                                 dsc 
= clust2sect(dj
.fs
, dcl
); 
3347                                 mem_set(dir
, 0, SS(dj
.fs
)); 
3348                                 mem_set(dir
+DIR_Name
, ' ', 8+3);        /* Create "." entry */ 
3349                                 dir
[DIR_Name
] = '.'; 
3350                                 dir
[DIR_Attr
] = AM_DIR
; 
3351                                 ST_DWORD(dir
+DIR_WrtTime
, tim
); 
3353                                 mem_cpy(dir
+SZ_DIR
, dir
, SZ_DIR
);       /* Create ".." entry */ 
3354                                 dir
[33] = '.'; pcl 
= dj
.sclust
; 
3355                                 if (dj
.fs
->fs_type 
== FS_FAT32 
&& pcl 
== dj
.fs
->dirbase
) 
3357                                 st_clust(dir
+SZ_DIR
, pcl
); 
3358                                 for (n 
= dj
.fs
->csize
; n
; n
--) {        /* Write dot entries and clear following sectors */ 
3359                                         dj
.fs
->winsect 
= dsc
++; 
3361                                         res 
= move_window(dj
.fs
, 0); 
3362                                         if (res 
!= FR_OK
) break; 
3363                                         mem_set(dir
, 0, SS(dj
.fs
)); 
3366                         if (res 
== FR_OK
) res 
= dir_register(&dj
);      /* Register the object to the directoy */ 
3368                                 remove_chain(dj
.fs
, dcl
);                       /* Could not register, remove cluster chain */ 
3371                                 dir
[DIR_Attr
] = AM_DIR
;                         /* Attribute */ 
3372                                 ST_DWORD(dir
+DIR_WrtTime
, tim
);         /* Created time */ 
3373                                 st_clust(dir
, dcl
);                                     /* Table start cluster */ 
3381         LEAVE_FF(dj
.fs
, res
); 
3387 /*-----------------------------------------------------------------------*/ 
3388 /* Change Attribute                                                      */ 
3389 /*-----------------------------------------------------------------------*/ 
3392         const TCHAR 
*path
,      /* Pointer to the file path */ 
3393         BYTE value
,                     /* Attribute bits */ 
3394         BYTE mask                       
/* Attribute mask to change */ 
3403         res 
= chk_mounted(&path
, &dj
.fs
, 1); 
3406                 res 
= follow_path(&dj
, path
);           /* Follow the file path */ 
3408                 if (_FS_RPATH 
&& res 
== FR_OK 
&& (dj
.fn
[NS
] & NS_DOT
)) 
3409                         res 
= FR_INVALID_NAME
; 
3412                         if (!dir
) {                                             /* Is it a root directory? */ 
3413                                 res 
= FR_INVALID_NAME
; 
3414                         } else {                                                /* File or sub directory */ 
3415                                 mask 
&= AM_RDO
|AM_HID
|AM_SYS
|AM_ARC
;    /* Valid attribute mask */ 
3416                                 dir
[DIR_Attr
] = (value 
& mask
) | (dir
[DIR_Attr
] & (BYTE
)~mask
); /* Apply attribute change */ 
3423         LEAVE_FF(dj
.fs
, res
); 
3429 /*-----------------------------------------------------------------------*/ 
3430 /* Change Timestamp                                                      */ 
3431 /*-----------------------------------------------------------------------*/ 
3434         const TCHAR 
*path
,      /* Pointer to the file/directory name */ 
3435         const FILINFO 
*fno      
/* Pointer to the time stamp to be set */ 
3444         res 
= chk_mounted(&path
, &dj
.fs
, 1); 
3447                 res 
= follow_path(&dj
, path
);   /* Follow the file path */ 
3449                 if (_FS_RPATH 
&& res 
== FR_OK 
&& (dj
.fn
[NS
] & NS_DOT
)) 
3450                         res 
= FR_INVALID_NAME
; 
3453                         if (!dir
) {                                     /* Root directory */ 
3454                                 res 
= FR_INVALID_NAME
; 
3455                         } else {                                        /* File or sub-directory */ 
3456                                 ST_WORD(dir
+DIR_WrtTime
, fno
->ftime
); 
3457                                 ST_WORD(dir
+DIR_WrtDate
, fno
->fdate
); 
3464         LEAVE_FF(dj
.fs
, res
); 
3470 /*-----------------------------------------------------------------------*/ 
3471 /* Rename File/Directory                                                 */ 
3472 /*-----------------------------------------------------------------------*/ 
3475         const TCHAR 
*path_old
,  /* Pointer to the old name */ 
3476         const TCHAR 
*path_new   
/* Pointer to the new name */ 
3486         res 
= chk_mounted(&path_old
, &djo
.fs
, 1); 
3490                 res 
= follow_path(&djo
, path_old
);              /* Check old object */ 
3491                 if (_FS_RPATH 
&& res 
== FR_OK 
&& (djo
.fn
[NS
] & NS_DOT
)) 
3492                         res 
= FR_INVALID_NAME
; 
3494                 if (res 
== FR_OK
) res 
= chk_lock(&djo
, 2); 
3496                 if (res 
== FR_OK
) {                                             /* Old object is found */ 
3497                         if (!djo
.dir
) {                                         /* Is root dir? */ 
3500                                 mem_cpy(buf
, djo
.dir
+DIR_Attr
, 21);             /* Save the object information except for name */ 
3501                                 mem_cpy(&djn
, &djo
, sizeof (DIR));              /* Check new object */ 
3502                                 res 
= follow_path(&djn
, path_new
); 
3503                                 if (res 
== FR_OK
) res 
= FR_EXIST
;               /* The new object name is already existing */ 
3504                                 if (res 
== FR_NO_FILE
) {                                /* Is it a valid path and no name collision? */ 
3505 /* Start critical section that an interruption or error can cause cross-link */ 
3506                                         res 
= dir_register(&djn
);                       /* Register the new entry */ 
3508                                                 dir 
= djn
.dir
;                                  /* Copy object information except for name */ 
3509                                                 mem_cpy(dir
+13, buf
+2, 19); 
3510                                                 dir
[DIR_Attr
] = buf
[0] | AM_ARC
; 
3512                                                 if (djo
.sclust 
!= djn
.sclust 
&& (dir
[DIR_Attr
] & AM_DIR
)) {             /* Update .. entry in the directory if needed */ 
3513                                                         dw 
= clust2sect(djo
.fs
, ld_clust(djo
.fs
, dir
)); 
3517                                                                 res 
= move_window(djo
.fs
, dw
); 
3518                                                                 dir 
= djo
.fs
->win
+SZ_DIR
;       /* .. entry */ 
3519                                                                 if (res 
== FR_OK 
&& dir
[1] == '.') { 
3520                                                                         dw 
= (djo
.fs
->fs_type 
== FS_FAT32 
&& djn
.sclust 
== djo
.fs
->dirbase
) ? 
0 : djn
.sclust
; 
3527                                                         res 
= dir_remove(&djo
);         /* Remove old entry */ 
3532 /* End critical section */ 
3538         LEAVE_FF(djo
.fs
, res
); 
3541 #endif /* !_FS_READONLY */ 
3542 #endif /* _FS_MINIMIZE == 0 */ 
3543 #endif /* _FS_MINIMIZE <= 1 */ 
3544 #endif /* _FS_MINIMIZE <= 2 */ 
3548 /*-----------------------------------------------------------------------*/ 
3549 /* Forward data to the stream directly (available on only tiny cfg)      */ 
3550 /*-----------------------------------------------------------------------*/ 
3551 #if _USE_FORWARD && _FS_TINY 
3554         FIL 
*fp
,                                                /* Pointer to the file object */ 
3555         UINT (*func
)(const BYTE
*,UINT
), /* Pointer to the streaming function */ 
3556         UINT btr
,                                               /* Number of bytes to forward */ 
3557         UINT 
*bf                                                
/* Pointer to number of bytes forwarded */ 
3561         DWORD remain
, clst
, sect
; 
3566         *bf 
= 0;        /* Clear transfer byte counter */ 
3568         if (!fp
) return FR_INVALID_OBJECT
; 
3570         res 
= validate(fp
);                                                             /* Check validity of the object */ 
3571         if (res 
!= FR_OK
) LEAVE_FF(fp
->fs
, res
); 
3572         if (fp
->flag 
& FA__ERROR
)                                               /* Check error flag */ 
3573                 LEAVE_FF(fp
->fs
, FR_INT_ERR
); 
3574         if (!(fp
->flag 
& FA_READ
))                                              /* Check access mode */ 
3575                 LEAVE_FF(fp
->fs
, FR_DENIED
); 
3577         remain 
= fp
->fsize 
- fp
->fptr
; 
3578         if (btr 
> remain
) btr 
= (UINT
)remain
;                   /* Truncate btr by remaining bytes */ 
3580         for ( ;  btr 
&& (*func
)(0, 0);                                  /* Repeat until all data transferred or stream becomes busy */ 
3581                 fp
->fptr 
+= rcnt
, *bf 
+= rcnt
, btr 
-= rcnt
) { 
3582                 csect 
= (BYTE
)(fp
->fptr 
/ SS(fp
->fs
) & (fp
->fs
->csize 
- 1));    /* Sector offset in the cluster */ 
3583                 if ((fp
->fptr 
% SS(fp
->fs
)) == 0) {                     /* On the sector boundary? */ 
3584                         if (!csect
) {                                                   /* On the cluster boundary? */ 
3585                                 clst 
= (fp
->fptr 
== 0) ?                        
/* On the top of the file? */ 
3586                                         fp
->sclust 
: get_fat(fp
->fs
, fp
->clust
); 
3587                                 if (clst 
<= 1) ABORT(fp
->fs
, FR_INT_ERR
); 
3588                                 if (clst 
== 0xFFFFFFFF) ABORT(fp
->fs
, FR_DISK_ERR
); 
3589                                 fp
->clust 
= clst
;                                       /* Update current cluster */ 
3592                 sect 
= clust2sect(fp
->fs
, fp
->clust
);           /* Get current data sector */ 
3593                 if (!sect
) ABORT(fp
->fs
, FR_INT_ERR
); 
3595                 if (move_window(fp
->fs
, sect
))                          /* Move sector window */ 
3596                         ABORT(fp
->fs
, FR_DISK_ERR
); 
3598                 rcnt 
= SS(fp
->fs
) - (WORD
)(fp
->fptr 
% SS(fp
->fs
));      /* Forward data from sector window */ 
3599                 if (rcnt 
> btr
) rcnt 
= btr
; 
3600                 rcnt 
= (*func
)(&fp
->fs
->win
[(WORD
)fp
->fptr 
% SS(fp
->fs
)], rcnt
); 
3601                 if (!rcnt
) ABORT(fp
->fs
, FR_INT_ERR
); 
3604         LEAVE_FF(fp
->fs
, FR_OK
); 
3606 #endif /* _USE_FORWARD */ 
3610 #if _USE_MKFS && !_FS_READONLY 
3611 /*-----------------------------------------------------------------------*/ 
3612 /* Create File System on the Drive                                       */ 
3613 /*-----------------------------------------------------------------------*/ 
3614 #define N_ROOTDIR       512             /* Number of root dir entries for FAT12/16 */ 
3615 #define N_FATS          1               /* Number of FAT copies (1 or 2) */ 
3619         BYTE drv
,               /* Logical drive number */ 
3620         BYTE sfd
,               /* Partitioning rule 0:FDISK, 1:SFD */ 
3621         UINT au                 
/* Allocation unit size [bytes] */ 
3624         static const WORD vst
[] = { 1024,   512,  256,  128,   64,    32,   16,    8,    4,    2,   0}; 
3625         static const WORD cst
[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512}; 
3626         BYTE fmt
, md
, sys
, *tbl
, pdrv
, part
; 
3627         DWORD n_clst
, vs
, n
, wsect
; 
3629         DWORD b_vol
, b_fat
, b_dir
, b_data
;      /* LBA */ 
3630         DWORD n_vol
, n_rsv
, n_fat
, n_dir
;       /* Size */ 
3635         /* Check mounted drive and clear work area */ 
3636         if (drv 
>= _VOLUMES
) return FR_INVALID_DRIVE
; 
3637         if (sfd 
> 1) return FR_INVALID_PARAMETER
; 
3638         if (au 
& (au 
- 1)) return FR_INVALID_PARAMETER
; 
3640         if (!fs
) return FR_NOT_ENABLED
; 
3642         pdrv 
= LD2PD(drv
);      /* Physical drive */ 
3643         part 
= LD2PT(drv
);      /* Partition (0:auto detect, 1-4:get from partition table)*/ 
3645         /* Get disk statics */ 
3646         stat 
= disk_initialize(pdrv
); 
3647         if (stat 
& STA_NOINIT
) return FR_NOT_READY
; 
3648         if (stat 
& STA_PROTECT
) return FR_WRITE_PROTECTED
; 
3649 #if _MAX_SS != 512                                      /* Get disk sector size */ 
3650         if (disk_ioctl(pdrv
, GET_SECTOR_SIZE
, &SS(fs
)) != RES_OK 
|| SS(fs
) > _MAX_SS
) 
3653         if (_MULTI_PARTITION 
&& part
) { 
3654                 /* Get partition information from partition table in the MBR */ 
3655                 if (disk_read(pdrv
, fs
->win
, 0, 1) != RES_OK
) return FR_DISK_ERR
; 
3656                 if (LD_WORD(fs
->win
+BS_55AA
) != 0xAA55) return FR_MKFS_ABORTED
; 
3657                 tbl 
= &fs
->win
[MBR_Table 
+ (part 
- 1) * SZ_PTE
]; 
3658                 if (!tbl
[4]) return FR_MKFS_ABORTED
;    /* No partition? */ 
3659                 b_vol 
= LD_DWORD(tbl
+8);        /* Volume start sector */ 
3660                 n_vol 
= LD_DWORD(tbl
+12);       /* Volume size */ 
3662                 /* Create a partition in this function */ 
3663                 if (disk_ioctl(pdrv
, GET_SECTOR_COUNT
, &n_vol
) != RES_OK 
|| n_vol 
< 128) 
3665                 b_vol 
= (sfd
) ? 
0 : 63;         /* Volume start sector */ 
3666                 n_vol 
-= b_vol
;                         /* Volume size */ 
3669         if (!au
) {                              /* AU auto selection */ 
3670                 vs 
= n_vol 
/ (2000 / (SS(fs
) / 512)); 
3671                 for (i 
= 0; vs 
< vst
[i
]; i
++) ; 
3674         au 
/= SS(fs
);           /* Number of sectors per cluster */ 
3675         if (au 
== 0) au 
= 1; 
3676         if (au 
> 128) au 
= 128; 
3678         /* Pre-compute number of clusters and FAT sub-type */ 
3679         n_clst 
= n_vol 
/ au
; 
3681         if (n_clst 
>= MIN_FAT16
) fmt 
= FS_FAT16
; 
3682         if (n_clst 
>= MIN_FAT32
) fmt 
= FS_FAT32
; 
3684         /* Determine offset and size of FAT structure */ 
3685         if (fmt 
== FS_FAT32
) { 
3686                 n_fat 
= ((n_clst 
* 4) + 8 + SS(fs
) - 1) / SS(fs
); 
3690                 n_fat 
= (fmt 
== FS_FAT12
) ? 
(n_clst 
* 3 + 1) / 2 + 3 : (n_clst 
* 2) + 4; 
3691                 n_fat 
= (n_fat 
+ SS(fs
) - 1) / SS(fs
); 
3693                 n_dir 
= (DWORD
)N_ROOTDIR 
* SZ_DIR 
/ SS(fs
); 
3695         b_fat 
= b_vol 
+ n_rsv
;                          /* FAT area start sector */ 
3696         b_dir 
= b_fat 
+ n_fat 
* N_FATS
;         /* Directory area start sector */ 
3697         b_data 
= b_dir 
+ n_dir
;                         /* Data area start sector */ 
3698         if (n_vol 
< b_data 
+ au 
- b_vol
) return FR_MKFS_ABORTED
;        /* Too small volume */ 
3700         /* Align data start sector to erase block boundary (for flash memory media) */ 
3701         if (disk_ioctl(pdrv
, GET_BLOCK_SIZE
, &n
) != RES_OK 
|| !n 
|| n 
> 32768) n 
= 1; 
3702         n 
= (b_data 
+ n 
- 1) & ~(n 
- 1);        /* Next nearest erase block from current data start */ 
3703         n 
= (n 
- b_data
) / N_FATS
; 
3704         if (fmt 
== FS_FAT32
) {          /* FAT32: Move FAT offset */ 
3707         } else {                                        /* FAT12/16: Expand FAT size */ 
3711         /* Determine number of clusters and final check of validity of the FAT sub-type */ 
3712         n_clst 
= (n_vol 
- n_rsv 
- n_fat 
* N_FATS 
- n_dir
) / au
; 
3713         if (   (fmt 
== FS_FAT16 
&& n_clst 
< MIN_FAT16
) 
3714                 || (fmt 
== FS_FAT32 
&& n_clst 
< MIN_FAT32
)) 
3715                 return FR_MKFS_ABORTED
; 
3717         switch (fmt
) {  /* Determine system ID for partition table */ 
3718         case FS_FAT12
:  sys 
= 0x01; break; 
3719         case FS_FAT16
:  sys 
= (n_vol 
< 0x10000) ? 
0x04 : 0x06; break; 
3720         default:                sys 
= 0x0C; 
3723         if (_MULTI_PARTITION 
&& part
) { 
3724                 /* Update system ID in the partition table */ 
3725                 tbl 
= &fs
->win
[MBR_Table 
+ (part 
- 1) * SZ_PTE
]; 
3727                 if (disk_write(pdrv
, fs
->win
, 0, 1) != RES_OK
) return FR_DISK_ERR
; 
3730                 if (sfd
) {      /* No partition table (SFD) */ 
3732                 } else {        /* Create partition table (FDISK) */ 
3733                         mem_set(fs
->win
, 0, SS(fs
)); 
3734                         tbl 
= fs
->win
+MBR_Table
;        /* Create partition table for single partition in the drive */ 
3735                         tbl
[1] = 1;                                             /* Partition start head */ 
3736                         tbl
[2] = 1;                                             /* Partition start sector */ 
3737                         tbl
[3] = 0;                                             /* Partition start cylinder */ 
3738                         tbl
[4] = sys
;                                   /* System type */ 
3739                         tbl
[5] = 254;                                   /* Partition end head */ 
3740                         n 
= (b_vol 
+ n_vol
) / 63 / 255; 
3741                         tbl
[6] = (BYTE
)((n 
>> 2) | 63); /* Partition end sector */ 
3742                         tbl
[7] = (BYTE
)n
;                               /* End cylinder */ 
3743                         ST_DWORD(tbl
+8, 63);                    /* Partition start in LBA */ 
3744                         ST_DWORD(tbl
+12, n_vol
);                /* Partition size in LBA */ 
3745                         ST_WORD(fs
->win
+BS_55AA
, 0xAA55);       /* MBR signature */ 
3746                         if (disk_write(pdrv
, fs
->win
, 0, 1) != RES_OK
)  /* Write it to the MBR sector */ 
3752         /* Create BPB in the VBR */ 
3753         tbl 
= fs
->win
;                                                  /* Clear sector */ 
3754         mem_set(tbl
, 0, SS(fs
)); 
3755         mem_cpy(tbl
, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code, OEM name */ 
3756         i 
= SS(fs
);                                                             /* Sector size */ 
3757         ST_WORD(tbl
+BPB_BytsPerSec
, i
); 
3758         tbl
[BPB_SecPerClus
] = (BYTE
)au
;                 /* Sectors per cluster */ 
3759         ST_WORD(tbl
+BPB_RsvdSecCnt
, n_rsv
);             /* Reserved sectors */ 
3760         tbl
[BPB_NumFATs
] = N_FATS
;                              /* Number of FATs */ 
3761         i 
= (fmt 
== FS_FAT32
) ? 
0 : N_ROOTDIR
;  /* Number of rootdir entries */ 
3762         ST_WORD(tbl
+BPB_RootEntCnt
, i
); 
3763         if (n_vol 
< 0x10000) {                                  /* Number of total sectors */ 
3764                 ST_WORD(tbl
+BPB_TotSec16
, n_vol
); 
3766                 ST_DWORD(tbl
+BPB_TotSec32
, n_vol
); 
3768         tbl
[BPB_Media
] = md
;                                    /* Media descriptor */ 
3769         ST_WORD(tbl
+BPB_SecPerTrk
, 63);                 /* Number of sectors per track */ 
3770         ST_WORD(tbl
+BPB_NumHeads
, 255);                 /* Number of heads */ 
3771         ST_DWORD(tbl
+BPB_HiddSec
, b_vol
);               /* Hidden sectors */ 
3772         n 
= get_fattime();                                              /* Use current time as VSN */ 
3773         if (fmt 
== FS_FAT32
) { 
3774                 ST_DWORD(tbl
+BS_VolID32
, n
);            /* VSN */ 
3775                 ST_DWORD(tbl
+BPB_FATSz32
, n_fat
);       /* Number of sectors per FAT */ 
3776                 ST_DWORD(tbl
+BPB_RootClus
, 2);          /* Root directory start cluster (2) */ 
3777                 ST_WORD(tbl
+BPB_FSInfo
, 1);                     /* FSInfo record offset (VBR+1) */ 
3778                 ST_WORD(tbl
+BPB_BkBootSec
, 6);          /* Backup boot record offset (VBR+6) */ 
3779                 tbl
[BS_DrvNum32
] = 0x80;                        /* Drive number */ 
3780                 tbl
[BS_BootSig32
] = 0x29;                       /* Extended boot signature */ 
3781                 mem_cpy(tbl
+BS_VolLab32
, "NO NAME    " "FAT32   ", 19); /* Volume label, FAT signature */ 
3783                 ST_DWORD(tbl
+BS_VolID
, n
);                      /* VSN */ 
3784                 ST_WORD(tbl
+BPB_FATSz16
, n_fat
);        /* Number of sectors per FAT */ 
3785                 tbl
[BS_DrvNum
] = 0x80;                          /* Drive number */ 
3786                 tbl
[BS_BootSig
] = 0x29;                         /* Extended boot signature */ 
3787                 mem_cpy(tbl
+BS_VolLab
, "NO NAME    " "FAT     ", 19);   /* Volume label, FAT signature */ 
3789         ST_WORD(tbl
+BS_55AA
, 0xAA55);                   /* Signature (Offset is fixed here regardless of sector size) */ 
3790         if (disk_write(pdrv
, tbl
, b_vol
, 1) != RES_OK
)  /* Write it to the VBR sector */ 
3792         if (fmt 
== FS_FAT32
)                                                    /* Write backup VBR if needed (VBR+6) */ 
3793                 disk_write(pdrv
, tbl
, b_vol 
+ 6, 1); 
3795         /* Initialize FAT area */ 
3797         for (i 
= 0; i 
< N_FATS
; i
++) {          /* Initialize each FAT copy */ 
3798                 mem_set(tbl
, 0, SS(fs
));                        /* 1st sector of the FAT  */ 
3799                 n 
= md
;                                                         /* Media descriptor byte */ 
3800                 if (fmt 
!= FS_FAT32
) { 
3801                         n 
|= (fmt 
== FS_FAT12
) ? 
0x00FFFF00 : 0xFFFFFF00; 
3802                         ST_DWORD(tbl
+0, n
);                             /* Reserve cluster #0-1 (FAT12/16) */ 
3805                         ST_DWORD(tbl
+0, n
);                             /* Reserve cluster #0-1 (FAT32) */ 
3806                         ST_DWORD(tbl
+4, 0xFFFFFFFF); 
3807                         ST_DWORD(tbl
+8, 0x0FFFFFFF);    /* Reserve cluster #2 for root dir */ 
3809                 if (disk_write(pdrv
, tbl
, wsect
++, 1) != RES_OK
) 
3811                 mem_set(tbl
, 0, SS(fs
));                        /* Fill following FAT entries with zero */ 
3812                 for (n 
= 1; n 
< n_fat
; n
++) {           /* This loop may take a time on FAT32 volume due to many single sector writes */ 
3813                         if (disk_write(pdrv
, tbl
, wsect
++, 1) != RES_OK
) 
3818         /* Initialize root directory */ 
3819         i 
= (fmt 
== FS_FAT32
) ? au 
: n_dir
; 
3821                 if (disk_write(pdrv
, tbl
, wsect
++, 1) != RES_OK
) 
3825 #if _USE_ERASE  /* Erase data area if needed */ 
3829                 eb
[0] = wsect
; eb
[1] = wsect 
+ (n_clst 
- ((fmt 
== FS_FAT32
) ? 
1 : 0)) * au 
- 1; 
3830                 disk_ioctl(pdrv
, CTRL_ERASE_SECTOR
, eb
); 
3834         /* Create FSInfo if needed */ 
3835         if (fmt 
== FS_FAT32
) { 
3836                 ST_DWORD(tbl
+FSI_LeadSig
, 0x41615252); 
3837                 ST_DWORD(tbl
+FSI_StrucSig
, 0x61417272); 
3838                 ST_DWORD(tbl
+FSI_Free_Count
, n_clst 
- 1);       /* Number of free clusters */ 
3839                 ST_DWORD(tbl
+FSI_Nxt_Free
, 2);                          /* Last allocated cluster# */ 
3840                 ST_WORD(tbl
+BS_55AA
, 0xAA55); 
3841                 disk_write(pdrv
, tbl
, b_vol 
+ 1, 1);    /* Write original (VBR+1) */ 
3842                 disk_write(pdrv
, tbl
, b_vol 
+ 7, 1);    /* Write backup (VBR+7) */ 
3845         return (disk_ioctl(pdrv
, CTRL_SYNC
, 0) == RES_OK
) ? FR_OK 
: FR_DISK_ERR
; 
3849 #if _MULTI_PARTITION == 2 
3850 /*-----------------------------------------------------------------------*/ 
3851 /* Divide Physical Drive                                                 */ 
3852 /*-----------------------------------------------------------------------*/ 
3855         BYTE pdrv
,                      /* Physical drive number */ 
3856         const DWORD szt
[],      /* Pointer to the size table for each partitions */ 
3857         void* work                      
/* Pointer to the working buffer */ 
3860         UINT i
, n
, sz_cyl
, tot_cyl
, b_cyl
, e_cyl
, p_cyl
; 
3861         BYTE s_hd
, e_hd
, *p
, *buf 
= (BYTE
*)work
; 
3863         DWORD sz_disk
, sz_part
, s_part
; 
3866         stat 
= disk_initialize(pdrv
); 
3867         if (stat 
& STA_NOINIT
) return FR_NOT_READY
; 
3868         if (stat 
& STA_PROTECT
) return FR_WRITE_PROTECTED
; 
3869         if (disk_ioctl(pdrv
, GET_SECTOR_COUNT
, &sz_disk
)) return FR_DISK_ERR
; 
3871         /* Determine CHS in the table regardless of the drive geometry */ 
3872         for (n 
= 16; n 
< 256 && sz_disk 
/ n 
/ 63 > 1024; n 
*= 2) ; 
3876         tot_cyl 
= sz_disk 
/ sz_cyl
; 
3878         /* Create partition table */ 
3879         mem_set(buf
, 0, _MAX_SS
); 
3880         p 
= buf 
+ MBR_Table
; b_cyl 
= 0; 
3881         for (i 
= 0; i 
< 4; i
++, p 
+= SZ_PTE
) { 
3882                 p_cyl 
= (szt
[i
] <= 100) ? 
(DWORD
)tot_cyl 
* szt
[i
] / 100 : szt
[i
] / sz_cyl
; 
3883                 if (!p_cyl
) continue; 
3884                 s_part 
= (DWORD
)sz_cyl 
* b_cyl
; 
3885                 sz_part 
= (DWORD
)sz_cyl 
* p_cyl
; 
3886                 if (i 
== 0) {   /* Exclude first track of cylinder 0 */ 
3888                         s_part 
+= 63; sz_part 
-= 63; 
3892                 e_cyl 
= b_cyl 
+ p_cyl 
- 1; 
3893                 if (e_cyl 
>= tot_cyl
) return FR_INVALID_PARAMETER
; 
3895                 /* Set partition table */ 
3896                 p
[1] = s_hd
;                                            /* Start head */ 
3897                 p
[2] = (BYTE
)((b_cyl 
>> 2) + 1);        /* Start sector */ 
3898                 p
[3] = (BYTE
)b_cyl
;                                     /* Start cylinder */ 
3899                 p
[4] = 0x06;                                            /* System type (temporary setting) */ 
3900                 p
[5] = e_hd
;                                            /* End head */ 
3901                 p
[6] = (BYTE
)((e_cyl 
>> 2) + 63);       /* End sector */ 
3902                 p
[7] = (BYTE
)e_cyl
;                                     /* End cylinder */ 
3903                 ST_DWORD(p 
+ 8, s_part
);                        /* Start sector in LBA */ 
3904                 ST_DWORD(p 
+ 12, sz_part
);                      /* Partition size */ 
3906                 /* Next partition */ 
3911         /* Write it to the MBR */ 
3912         return (disk_write(pdrv
, buf
, 0, 1) || disk_ioctl(pdrv
, CTRL_SYNC
, 0)) ? FR_DISK_ERR 
: FR_OK
; 
3916 #endif /* _MULTI_PARTITION == 2 */ 
3917 #endif /* _USE_MKFS && !_FS_READONLY */ 
3923 /*-----------------------------------------------------------------------*/ 
3924 /* Get a string from the file                                            */ 
3925 /*-----------------------------------------------------------------------*/ 
3927         TCHAR
* buff
,    /* Pointer to the string buffer to read */ 
3928         int len
,                /* Size of string buffer (characters) */ 
3929         FIL
* fil                
/* Pointer to the file object */ 
3938         while (n 
< len 
- 1) {                   /* Read bytes until buffer gets filled */ 
3939                 f_read(fil
, s
, 1, &rc
); 
3940                 if (rc 
!= 1) break;                     /* Break on EOF or error */ 
3942 #if _LFN_UNICODE                                        /* Read a character in UTF-8 encoding */ 
3944                         if (c 
< 0xC0) continue; /* Skip stray trailer */ 
3945                         if (c 
< 0xE0) {                 /* Two-byte sequence */ 
3946                                 f_read(fil
, s
, 1, &rc
); 
3948                                 c 
= ((c 
& 0x1F) << 6) | (s
[0] & 0x3F); 
3949                                 if (c 
< 0x80) c 
= '?'; 
3951                                 if (c 
< 0xF0) {         /* Three-byte sequence */ 
3952                                         f_read(fil
, s
, 2, &rc
); 
3954                                         c 
= (c 
<< 12) | ((s
[0] & 0x3F) << 6) | (s
[1] & 0x3F); 
3955                                         if (c 
< 0x800) c 
= '?'; 
3956                                 } else {                        /* Reject four-byte sequence */ 
3962 #if _USE_STRFUNC >= 2 
3963                 if (c 
== '\r') continue;        /* Strip '\r' */ 
3967                 if (c 
== '\n') break;           /* Break on EOL */ 
3970         return n ? buff 
: 0;                    /* When no data read (eof or error), return with error. */ 
3977 /*-----------------------------------------------------------------------*/ 
3978 /* Put a character to the file                                           */ 
3979 /*-----------------------------------------------------------------------*/ 
3981         TCHAR c
,        /* A character to be output */ 
3982         FIL
* fil        
/* Pointer to the file object */ 
3989 #if _USE_STRFUNC >= 2 
3990         if (c 
== '\n') f_putc ('\r', fil
);      /* LF -> CRLF conversion */ 
3993 #if _LFN_UNICODE        /* Write the character in UTF-8 encoding */ 
3994         if (c 
< 0x80) {                 /* 7-bit */ 
3998                 if (c 
< 0x800) {        /* 11-bit */ 
3999                         s
[0] = (BYTE
)(0xC0 | (c 
>> 6)); 
4000                         s
[1] = (BYTE
)(0x80 | (c 
& 0x3F)); 
4002                 } else {                        /* 16-bit */ 
4003                         s
[0] = (BYTE
)(0xE0 | (c 
>> 12)); 
4004                         s
[1] = (BYTE
)(0x80 | ((c 
>> 6) & 0x3F)); 
4005                         s
[2] = (BYTE
)(0x80 | (c 
& 0x3F)); 
4009 #else                           /* Write the character without conversion */ 
4013         f_write(fil
, s
, btw
, &bw
);              /* Write the char to the file */ 
4014         return (bw 
== btw
) ? 
1 : EOF
;   /* Return the result */ 
4020 /*-----------------------------------------------------------------------*/ 
4021 /* Put a string to the file                                              */ 
4022 /*-----------------------------------------------------------------------*/ 
4024         const TCHAR
* str
,       /* Pointer to the string to be output */ 
4025         FIL
* fil                        
/* Pointer to the file object */ 
4031         for (n 
= 0; *str
; str
++, n
++) { 
4032                 if (f_putc(*str
, fil
) == EOF
) return EOF
; 
4040 /*-----------------------------------------------------------------------*/ 
4041 /* Put a formatted string to the file                                    */ 
4042 /*-----------------------------------------------------------------------*/ 
4044         FIL
* fil
,                       /* Pointer to the file object */ 
4045         const TCHAR
* str
,       /* Pointer to the format string */ 
4046         ...                                     /* Optional arguments... */ 
4053         TCHAR c
, d
, s
[16], *p
; 
4059         for (cc 
= res 
= 0; cc 
!= EOF
; res 
+= cc
) { 
4061                 if (c 
== 0) break;                      /* End of string */ 
4062                 if (c 
!= '%') {                         /* Non escape character */ 
4063                         cc 
= f_putc(c
, fil
); 
4064                         if (cc 
!= EOF
) cc 
= 1; 
4069                 if (c 
== '0') {                         /* Flag: '0' padding */ 
4072                         if (c 
== '-') {                 /* Flag: left justified */ 
4076                 while (IsDigit(c
)) {            /* Precision */ 
4077                         w 
= w 
* 10 + c 
- '0'; 
4080                 if (c 
== 'l' || c 
== 'L') {     /* Prefix: Size is long int */ 
4085                 if (IsLower(d
)) d 
-= 0x20; 
4086                 switch (d
) {                            /* Type is... */ 
4087                 case 'S' :                                      /* String */ 
4088                         p 
= va_arg(arp
, TCHAR
*); 
4089                         for (j 
= 0; p
[j
]; j
++) ; 
4092                                 while (j
++ < w
) chc 
+= (cc 
= f_putc(' ', fil
)); 
4094                         chc 
+= (cc 
= f_puts(p
, fil
)); 
4095                         while (j
++ < w
) chc 
+= (cc 
= f_putc(' ', fil
)); 
4096                         if (cc 
!= EOF
) cc 
= chc
; 
4098                 case 'C' :                                      /* Character */ 
4099                         cc 
= f_putc((TCHAR
)va_arg(arp
, int), fil
); continue; 
4100                 case 'B' :                                      /* Binary */ 
4102                 case 'O' :                                      /* Octal */ 
4104                 case 'D' :                                      /* Signed decimal */ 
4105                 case 'U' :                                      /* Unsigned decimal */ 
4107                 case 'X' :                                      /* Hexdecimal */ 
4109                 default:                                        /* Unknown type (pass-through) */ 
4110                         cc 
= f_putc(c
, fil
); continue; 
4113                 /* Get an argument and put it in numeral */ 
4114                 v 
= (f 
& 4) ? 
(ULONG
)va_arg(arp
, long) : ((d 
== 'D') ? 
(ULONG
)(long)va_arg(arp
, int) : (ULONG
)va_arg(arp
, unsigned int)); 
4115                 if (d 
== 'D' && (v 
& 0x80000000)) { 
4121                         d 
= (TCHAR
)(v 
% r
); v 
/= r
; 
4122                         if (d 
> 9) d 
+= (c 
== 'x') ? 
0x27 : 0x07; 
4124                 } while (v 
&& i 
< sizeof s 
/ sizeof s
[0]); 
4125                 if (f 
& 8) s
[i
++] = '-'; 
4126                 j 
= i
; d 
= (f 
& 1) ? 
'0' : ' '; 
4128                 while (!(f 
& 2) && j
++ < w
) res 
+= (cc 
= f_putc(d
, fil
)); 
4129                 do res 
+= (cc 
= f_putc(s
[--i
], fil
)); while(i
); 
4130                 while (j
++ < w
) res 
+= (cc 
= f_putc(' ', fil
)); 
4131                 if (cc 
!= EOF
) cc 
= res
; 
4135         return (cc 
== EOF
) ? cc 
: res
; 
4138 #endif /* !_FS_READONLY */ 
4139 #endif /* _USE_STRFUNC */