/*----------------------------------------------------------------------------/
/ FatFs - Generic FAT Filesystem Module R0 .13 /
/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /
/
/ Copyright ( C ) 2017 , ChaN , all right reserved .
/
/ FatFs module is an open source software . Redistribution and use of FatFs in
/ source and binary forms , with or without modification , are permitted provided
/ that the following condition is met :
/
/ 1. Redistributions of source code must retain the above copyright notice ,
/ this condition and the following disclaimer .
/
/ This software is provided by the copyright holder and contributors " AS IS "
/ and any warranties related to this software are DISCLAIMED .
/ The copyright owner or contributors be NOT LIABLE for any damages caused
/ by use of this software .
/
/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
# include "ff.h" /* Declarations of FatFs API */
# include "diskio.h" /* Declarations of device I/O functions */
/*--------------------------------------------------------------------------
Module Private Definitions
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
# if FF_DEFINED != 87030 /* Revision ID */
# error Wrong include file (ff.h).
# endif
/* ASCII code support macros */
# define IsUpper(c) ((c) >= 'A' && (c) <= 'Z')
# define IsLower(c) ((c) >= 'a' && (c) <= 'z')
# define IsDigit(c) ((c) >= '0' && (c) <= '9')
/* Additional file attribute bits for internal use */
# define AM_VOL 0x08 /* Volume label */
# define AM_LFN 0x0F /* LFN entry */
# define AM_MASK 0x3F /* Mask of defined bits */
/* Additional file access control and file status flags for internal use */
# define FA_SEEKEND 0x20 /* Seek to end of the file on file open */
# define FA_MODIFIED 0x40 /* File has been modified */
# define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */
/* Name status flags in fn[11] */
# define NSFLAG 11 /* Index of the name status byte */
# define NS_LOSS 0x01 /* Out of 8.3 format */
# define NS_LFN 0x02 /* Force to create LFN entry */
# define NS_LAST 0x04 /* Last segment */
# define NS_BODY 0x08 /* Lower case flag (body) */
# define NS_EXT 0x10 /* Lower case flag (ext) */
# define NS_DOT 0x20 /* Dot entry */
# define NS_NOLFN 0x40 /* Do not find LFN */
# define NS_NONAME 0x80 /* Not followed */
/* Limits and boundaries */
# define MAX_DIR 0x200000 /* Max size of FAT directory */
# define MAX_DIR_EX 0x10000000 /* Max size of exFAT directory */
# define MAX_FAT12 0xFF5 /* Max FAT12 clusters (differs from specs, but correct for real DOS/Windows behavior) */
# define MAX_FAT16 0xFFF5 /* Max FAT16 clusters (differs from specs, but correct for real DOS/Windows behavior) */
# define MAX_FAT32 0x0FFFFFF5 /* Max FAT32 clusters (not specified, practical limit) */
# define MAX_EXFAT 0x7FFFFFFD /* Max exFAT clusters (differs from specs, implementation limit) */
/* FatFs refers the FAT structure as simple byte array instead of structure member
/ because the C structure is not binary compatible between different platforms */
# define BS_JmpBoot 0 /* x86 jump instruction (3-byte) */
# define BS_OEMName 3 /* OEM name (8-byte) */
# define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */
# define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */
# define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD) */
# define BPB_NumFATs 16 /* Number of FATs (BYTE) */
# define BPB_RootEntCnt 17 /* Size of root directory area for FAT [entry] (WORD) */
# define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */
# define BPB_Media 21 /* Media descriptor byte (BYTE) */
# define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */
# define BPB_SecPerTrk 24 /* Number of sectors per track for int13h [sector] (WORD) */
# define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */
# define BPB_HiddSec 28 /* Volume offset from top of the drive (DWORD) */
# define BPB_TotSec32 32 /* Volume size (32-bit) [sector] (DWORD) */
# define BS_DrvNum 36 /* Physical drive number for int13h (BYTE) */
# define BS_NTres 37 /* WindowsNT error flag (BYTE) */
# define BS_BootSig 38 /* Extended boot signature (BYTE) */
# define BS_VolID 39 /* Volume serial number (DWORD) */
# define BS_VolLab 43 /* Volume label string (8-byte) */
# define BS_FilSysType 54 /* Filesystem type string (8-byte) */
# define BS_BootCode 62 /* Boot code (448-byte) */
# define BS_55AA 510 /* Signature word (WORD) */
# define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */
# define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */
# define BPB_FSVer32 42 /* FAT32: Filesystem version (WORD) */
# define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */
# define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */
# define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD) */
# define BS_DrvNum32 64 /* FAT32: Physical drive number for int13h (BYTE) */
# define BS_NTres32 65 /* FAT32: Error flag (BYTE) */
# define BS_BootSig32 66 /* FAT32: Extended boot signature (BYTE) */
# define BS_VolID32 67 /* FAT32: Volume serial number (DWORD) */
# define BS_VolLab32 71 /* FAT32: Volume label string (8-byte) */
# define BS_FilSysType32 82 /* FAT32: Filesystem type string (8-byte) */
# define BS_BootCode32 90 /* FAT32: Boot code (420-byte) */
# define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */
# define BPB_VolOfsEx 64 /* exFAT: Volume offset from top of the drive [sector] (QWORD) */
# define BPB_TotSecEx 72 /* exFAT: Volume size [sector] (QWORD) */
# define BPB_FatOfsEx 80 /* exFAT: FAT offset from top of the volume [sector] (DWORD) */
# define BPB_FatSzEx 84 /* exFAT: FAT size [sector] (DWORD) */
# define BPB_DataOfsEx 88 /* exFAT: Data offset from top of the volume [sector] (DWORD) */
# define BPB_NumClusEx 92 /* exFAT: Number of clusters (DWORD) */
# define BPB_RootClusEx 96 /* exFAT: Root directory start cluster (DWORD) */
# define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */
# define BPB_FSVerEx 104 /* exFAT: Filesystem version (WORD) */
# define BPB_VolFlagEx 106 /* exFAT: Volume flags (BYTE) */
# define BPB_ActFatEx 107 /* exFAT: Active FAT flags (BYTE) */
# define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in unit of byte (BYTE) */
# define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in unit of sector (BYTE) */
# define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */
# define BPB_DrvNumEx 111 /* exFAT: Physical drive number for int13h (BYTE) */
# define BPB_PercInUseEx 112 /* exFAT: Percent in use (BYTE) */
# define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */
# define BS_BootCodeEx 120 /* exFAT: Boot code (390-byte) */
# define DIR_Name 0 /* Short file name (11-byte) */
# define DIR_Attr 11 /* Attribute (BYTE) */
# define DIR_NTres 12 /* Lower case flag (BYTE) */
# define DIR_CrtTime10 13 /* Created time sub-second (BYTE) */
# define DIR_CrtTime 14 /* Created time (DWORD) */
# define DIR_LstAccDate 18 /* Last accessed date (WORD) */
# define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */
# define DIR_ModTime 22 /* Modified time (DWORD) */
# define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */
# define DIR_FileSize 28 /* File size (DWORD) */
# define LDIR_Ord 0 /* LFN: LFN order and LLE flag (BYTE) */
# define LDIR_Attr 11 /* LFN: LFN attribute (BYTE) */
# define LDIR_Type 12 /* LFN: Entry type (BYTE) */
# define LDIR_Chksum 13 /* LFN: Checksum of the SFN (BYTE) */
# define LDIR_FstClusLO 26 /* LFN: MBZ field (WORD) */
# define XDIR_Type 0 /* exFAT: Type of exFAT directory entry (BYTE) */
# define XDIR_NumLabel 1 /* exFAT: Number of volume label characters (BYTE) */
# define XDIR_Label 2 /* exFAT: Volume label (11-WORD) */
# define XDIR_CaseSum 4 /* exFAT: Sum of case conversion table (DWORD) */
# define XDIR_NumSec 1 /* exFAT: Number of secondary entries (BYTE) */
# define XDIR_SetSum 2 /* exFAT: Sum of the set of directory entries (WORD) */
# define XDIR_Attr 4 /* exFAT: File attribute (WORD) */
# define XDIR_CrtTime 8 /* exFAT: Created time (DWORD) */
# define XDIR_ModTime 12 /* exFAT: Modified time (DWORD) */
# define XDIR_AccTime 16 /* exFAT: Last accessed time (DWORD) */
# define XDIR_CrtTime10 20 /* exFAT: Created time subsecond (BYTE) */
# define XDIR_ModTime10 21 /* exFAT: Modified time subsecond (BYTE) */
# define XDIR_CrtTZ 22 /* exFAT: Created timezone (BYTE) */
# define XDIR_ModTZ 23 /* exFAT: Modified timezone (BYTE) */
# define XDIR_AccTZ 24 /* exFAT: Last accessed timezone (BYTE) */
# define XDIR_GenFlags 33 /* exFAT: General secondary flags (BYTE) */
# define XDIR_NumName 35 /* exFAT: Number of file name characters (BYTE) */
# define XDIR_NameHash 36 /* exFAT: Hash of file name (WORD) */
# define XDIR_ValidFileSize 40 /* exFAT: Valid file size (QWORD) */
# define XDIR_FstClus 52 /* exFAT: First cluster of the file data (DWORD) */
# define XDIR_FileSize 56 /* exFAT: File/Directory size (QWORD) */
# define SZDIRE 32 /* Size of a directory entry */
# define DDEM 0xE5 /* Deleted directory entry mark set to DIR_Name[0] */
# define RDDEM 0x05 /* Replacement of the character collides with DDEM */
# define LLEF 0x40 /* Last long entry flag in LDIR_Ord */
# define FSI_LeadSig 0 /* FAT32 FSI: Leading signature (DWORD) */
# define FSI_StrucSig 484 /* FAT32 FSI: Structure signature (DWORD) */
# define FSI_Free_Count 488 /* FAT32 FSI: Number of free clusters (DWORD) */
# define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated cluster (DWORD) */
# define MBR_Table 446 /* MBR: Offset of partition table in the MBR */
# define SZ_PTE 16 /* MBR: Size of a partition table entry */
# define PTE_Boot 0 /* MBR PTE: Boot indicator */
# define PTE_StHead 1 /* MBR PTE: Start head */
# define PTE_StSec 2 /* MBR PTE: Start sector */
# define PTE_StCyl 3 /* MBR PTE: Start cylinder */
# define PTE_System 4 /* MBR PTE: System ID */
# define PTE_EdHead 5 /* MBR PTE: End head */
# define PTE_EdSec 6 /* MBR PTE: End sector */
# define PTE_EdCyl 7 /* MBR PTE: End cylinder */
# define PTE_StLba 8 /* MBR PTE: Start in LBA */
# define PTE_SizLba 12 /* MBR PTE: Size in LBA */
/* Post process after fatal error on file operation */
# define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); }
/* Reentrancy related */
# if FF_FS_REENTRANT
# if FF_USE_LFN == 1
# error Static LFN work area cannot be used at thread-safe configuration
# endif
# define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; }
# else
# define LEAVE_FF(fs, res) return res
# endif
/* Definitions of volume - partition conversion */
# if FF_MULTI_PARTITION
# define LD2PD(vol) VolToPart[vol].pd /* Get physical drive number */
# define LD2PT(vol) VolToPart[vol].pt /* Get partition index */
# else
# define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */
# define LD2PT(vol) 0 /* Find first valid partition or in SFD */
# endif
/* Definitions of sector size */
# if (FF_MAX_SS < FF_MIN_SS) || (FF_MAX_SS != 512 && FF_MAX_SS != 1024 && FF_MAX_SS != 2048 && FF_MAX_SS != 4096) || (FF_MIN_SS != 512 && FF_MIN_SS != 1024 && FF_MIN_SS != 2048 && FF_MIN_SS != 4096)
# error Wrong sector size configuration
# endif
# if FF_MAX_SS == FF_MIN_SS
# define SS(fs) ((UINT)FF_MAX_SS) /* Fixed sector size */
# else
# define SS(fs) ((fs)->ssize) /* Variable sector size */
# endif
/* Timestamp */
# if FF_FS_NORTC == 1
# if FF_NORTC_YEAR < 1980 || FF_NORTC_YEAR > 2107 || FF_NORTC_MON < 1 || FF_NORTC_MON > 12 || FF_NORTC_MDAY < 1 || FF_NORTC_MDAY > 31
# error Invalid FF_FS_NORTC settings
# endif
# define GET_FATTIME() ((DWORD)(FF_NORTC_YEAR - 1980) << 25 | (DWORD)FF_NORTC_MON << 21 | (DWORD)FF_NORTC_MDAY << 16)
# else
# define GET_FATTIME() get_fattime()
# endif
/* File lock controls */
# if FF_FS_LOCK != 0
# if FF_FS_READONLY
# error FF_FS_LOCK must be 0 at read-only configuration
# endif
typedef struct {
FATFS * fs ; /* Object ID 1, volume (NULL:blank entry) */
DWORD clu ; /* Object ID 2, containing directory (0:root) */
DWORD ofs ; /* Object ID 3, offset in the directory */
WORD ctr ; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */
} FILESEM ;
# endif
/* SBCS up-case tables (\x80-\xFF) */
# define TBL_CT437 {0x80,0x9A,0x45,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 , \
0x41 , 0x49 , 0x4F , 0x55 , 0xA5 , 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 , \
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 , \
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 }
# define TBL_CT720 {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 , \
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 , \
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 , \
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 }
# define TBL_CT737 {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 , \
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 , \
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 , \
0x97 , 0xEA , 0xEB , 0xEC , 0xE4 , 0xED , 0xEE , 0xEF , 0xF5 , 0xF0 , 0xEA , 0xEB , 0xEC , 0xED , 0xEE , 0xEF , \
0xF0 , 0xF1 , 0xF2 , 0xF3 , 0xF4 , 0xF5 , 0xF6 , 0xF7 , 0xF8 , 0xF9 , 0xFA , 0xFB , 0xFC , 0xFD , 0xFE , 0xFF }
# define TBL_CT771 {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 , \
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 , \
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 , 0xDC , 0xDE , 0xDE , \
0x90 , 0x91 , 0x92 , 0x93 , 0x94 , 0x95 , 0x96 , 0x97 , 0x98 , 0x99 , 0x9A , 0x9B , 0x9C , 0x9D , 0x9E , 0x9F , \
0xF0 , 0xF0 , 0xF2 , 0xF2 , 0xF4 , 0xF4 , 0xF6 , 0xF6 , 0xF8 , 0xF8 , 0xFA , 0xFA , 0xFC , 0xFC , 0xFE , 0xFF }
# define TBL_CT775 {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 , \
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 , \
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 , \
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 }
# define TBL_CT850 {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \
0x45 , 0x92 , 0x92 , 0x4F , 0x4F , 0x4F , 0x55 , 0x55 , 0x59 , 0x4F , 0x55 , 0x4F , 0x9C , 0x4F , 0x9E , 0x9F , \
0x41 , 0x49 , 0x4F , 0x55 , 0xA5 , 0xA5 , 0xA6 , 0xA7 , 0xA8 , 0xA9 , 0xAA , 0xAB , 0xAC , 0xAD , 0xAE , 0xAF , \
0xB0 , 0xB1 , 0xB2 , 0xB3 , 0xB4 , 0x41 , 0x41 , 0x41 , 0xB8 , 0xB9 , 0xBA , 0xBB , 0xBC , 0xBD , 0xBE , 0xBF , \
0xC0 , 0xC1 , 0xC2 , 0xC3 , 0xC4 , 0xC5 , 0x41 , 0x41 , 0xC8 , 0xC9 , 0xCA , 0xCB , 0xCC , 0xCD , 0xCE , 0xCF , \
0xD1 , 0xD1 , 0x45 , 0x45 , 0x45 , 0x49 , 0x49 , 0x49 , 0x49 , 0xD9 , 0xDA , 0xDB , 0xDC , 0xDD , 0x49 , 0xDF , \
0x4F , 0xE1 , 0x4F , 0x4F , 0x4F , 0x4F , 0xE6 , 0xE8 , 0xE8 , 0x55 , 0x55 , 0x55 , 0x59 , 0x59 , 0xEE , 0xEF , \
0xF0 , 0xF1 , 0xF2 , 0xF3 , 0xF4 , 0xF5 , 0xF6 , 0xF7 , 0xF8 , 0xF9 , 0xFA , 0xFB , 0xFC , 0xFD , 0xFE , 0xFF }
# define TBL_CT852 {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 , 0xAC , \
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 , \
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 , \
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 }
# define TBL_CT855 {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 , \
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 , \
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 , \
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 }
# define TBL_CT857 {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, \
0x90 , 0x92 , 0x92 , 0xE2 , 0x99 , 0xE3 , 0xEA , 0xEB , 0x98 , 0x99 , 0x9A , 0x9D , 0x9C , 0x9D , 0x9E , 0x9E , \
0xB5 , 0xD6 , 0xE0 , 0xE9 , 0xA5 , 0xA5 , 0xA6 , 0xA6 , 0xA8 , 0xA9 , 0xAA , 0xAB , 0xAC , 0xAD , 0xAE , 0xAF , \
0xB0 , 0xB1 , 0xB2 , 0xB3 , 0xB4 , 0xB5 , 0xB6 , 0xB7 , 0xB8 , 0xB9 , 0xBA , 0xBB , 0xBC , 0xBD , 0xBE , 0xBF , \
0xC0 , 0xC1 , 0xC2 , 0xC3 , 0xC4 , 0xC5 , 0xC7 , 0xC7 , 0xC8 , 0xC9 , 0xCA , 0xCB , 0xCC , 0xCD , 0xCE , 0xCF , \
0xD0 , 0xD1 , 0xD2 , 0xD3 , 0xD4 , 0x49 , 0xD6 , 0xD7 , 0xD8 , 0xD9 , 0xDA , 0xDB , 0xDC , 0xDD , 0xDE , 0xDF , \
0xE0 , 0xE1 , 0xE2 , 0xE3 , 0xE5 , 0xE5 , 0xE6 , 0xE7 , 0xE8 , 0xE9 , 0xEA , 0xEB , 0xDE , 0xED , 0xEE , 0xEF , \
0xF0 , 0xF1 , 0xF2 , 0xF3 , 0xF4 , 0xF5 , 0xF6 , 0xF7 , 0xF8 , 0xF9 , 0xFA , 0xFB , 0xFC , 0xFD , 0xFE , 0xFF }
# define TBL_CT860 {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \
0x90 , 0x91 , 0x92 , 0x8C , 0x99 , 0xA9 , 0x96 , 0x9D , 0x98 , 0x99 , 0x9A , 0x9B , 0x9C , 0x9D , 0x9E , 0x9F , \
0x86 , 0x8B , 0x9F , 0x96 , 0xA5 , 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 , \
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 , \
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 }
# define TBL_CT861 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \
0x90 , 0x92 , 0x92 , 0x4F , 0x99 , 0x8D , 0x55 , 0x97 , 0x97 , 0x99 , 0x9A , 0x9D , 0x9C , 0x9D , 0x9E , 0x9F , \
0xA4 , 0xA5 , 0xA6 , 0xA7 , 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 , \
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 , \
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 }
# define TBL_CT862 {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 , \
0x41 , 0x49 , 0x4F , 0x55 , 0xA5 , 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 , \
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 , \
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 }
# define TBL_CT863 {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \
0x45 , 0x45 , 0x45 , 0x4F , 0x45 , 0x49 , 0x55 , 0x55 , 0x98 , 0x4F , 0x55 , 0x9B , 0x9C , 0x55 , 0x55 , 0x9F , \
0xA0 , 0xA1 , 0x4F , 0x55 , 0xA4 , 0xA5 , 0xA6 , 0xA7 , 0x49 , 0xA9 , 0xAA , 0xAB , 0xAC , 0xAD , 0xAE , 0xAF , \
0xB0 , 0xB1 , 0xB2 , 0xB3 , 0xB4 , 0xB5 , 0xB6 , 0xB7 , 0xB8 , 0xB9 , 0xBA , 0xBB , 0xBC , 0xBD , 0xBE , 0xBF , \
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 , \
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 }
# define TBL_CT864 {0x80,0x9A,0x45,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 , \
0x41 , 0x49 , 0x4F , 0x55 , 0xA5 , 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 , \
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 , \
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 }
# define TBL_CT865 {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 , \
0x41 , 0x49 , 0x4F , 0x55 , 0xA5 , 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 , \
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 , \
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 }
# define TBL_CT866 {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 , \
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 , \
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 , \
0x90 , 0x91 , 0x92 , 0x93 , 0x94 , 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 }
# define TBL_CT869 {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 , 0x86 , 0x9C , 0x8D , 0x8F , 0x90 , \
0x91 , 0x90 , 0x92 , 0x95 , 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 , \
0xC0 , 0xC1 , 0xC2 , 0xC3 , 0xC4 , 0xC5 , 0xC6 , 0xC7 , 0xC8 , 0xC9 , 0xCA , 0xCB , 0xCC , 0xCD , 0xCE , 0xCF , \
0xD0 , 0xD1 , 0xD2 , 0xD3 , 0xD4 , 0xD5 , 0xA4 , 0xA5 , 0xA6 , 0xD9 , 0xDA , 0xDB , 0xDC , 0xA7 , 0xA8 , 0xDF , \
0xA9 , 0xAA , 0xAC , 0xAD , 0xB5 , 0xB6 , 0xB7 , 0xB8 , 0xBD , 0xBE , 0xC6 , 0xC7 , 0xCF , 0xCF , 0xD0 , 0xEF , \
0xF0 , 0xF1 , 0xD1 , 0xD2 , 0xD3 , 0xF5 , 0xD4 , 0xF7 , 0xF8 , 0xF9 , 0xD5 , 0x96 , 0x95 , 0x98 , 0xFE , 0xFF }
/* DBCS code range |----- 1st byte -----| |----------- 2nd byte -----------| */
# define TBL_DC932 {0x81, 0x9F, 0xE0, 0xFC, 0x40, 0x7E, 0x80, 0xFC, 0x00, 0x00}
# define TBL_DC936 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0x80, 0xFE, 0x00, 0x00}
# define TBL_DC949 {0x81, 0xFE, 0x00, 0x00, 0x41, 0x5A, 0x61, 0x7A, 0x81, 0xFE}
# define TBL_DC950 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0xA1, 0xFE, 0x00, 0x00}
/* Macros for table definitions */
# define MERGE_2STR(a, b) a ## b
# define MKCVTBL(hd, cp) MERGE_2STR(hd, cp)
/*--------------------------------------------------------------------------
Module Private Work Area
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Remark: Variables defined here without initial value shall be guaranteed
/ zero / null at start - up . If not , the linker option or start - up routine is
/ not compliance with C standard . */
/*--------------------------------*/
/* File/Volume controls */
/*--------------------------------*/
# if FF_VOLUMES < 1 || FF_VOLUMES > 10
# error Wrong FF_VOLUMES setting
# endif
static FATFS * FatFs [ FF_VOLUMES ] ; /* Pointer to the filesystem objects (logical drives) */
static WORD Fsid ; /* File system mount ID */
# if FF_FS_RPATH != 0 && FF_VOLUMES >= 2
static BYTE CurrVol ; /* Current drive */
# endif
# if FF_FS_LOCK != 0
static FILESEM Files [ FF_FS_LOCK ] ; /* Open object lock semaphores */
# endif
/*--------------------------------*/
/* LFN/Directory working buffer */
/*--------------------------------*/
# if FF_USE_LFN == 0 /* Non-LFN configuration */
# define DEF_NAMBUF
# define INIT_NAMBUF(fs)
# define FREE_NAMBUF()
# else /* LFN configurations */
# if FF_MAX_LFN < 12 || FF_MAX_LFN > 255
# error Wrong FF_MAX_LFN setting
# endif
static const BYTE LfnOfs [ ] = { 1 , 3 , 5 , 7 , 9 , 14 , 16 , 18 , 20 , 22 , 24 , 28 , 30 } ; /* FAT: Offset of LFN characters in the directory entry */
# define MAXDIRB(nc) ((nc + 44U) / 15 * SZDIRE) /* exFAT: Size of directory entry block scratchpad buffer needed for the max name length */
# if FF_USE_LFN == 1 /* LFN enabled with static working buffer */
# if FF_FS_EXFAT
static BYTE DirBuf [ MAXDIRB ( FF_MAX_LFN ) ] ; /* Directory entry block scratchpad buffer */
# endif
static WCHAR LfnBuf [ FF_MAX_LFN + 1 ] ; /* LFN working buffer */
# define DEF_NAMBUF
# define INIT_NAMBUF(fs)
# define FREE_NAMBUF()
# elif FF_USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */
# if FF_FS_EXFAT
# define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; BYTE dbuf[MAXDIRB(FF_MAX_LFN)]; /* LFN working buffer and directory entry block scratchpad buffer */
# define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; }
# define FREE_NAMBUF()
# else
# define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; /* LFN working buffer */
# define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; }
# define FREE_NAMBUF()
# endif
# elif FF_USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */
# if FF_FS_EXFAT
# define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working buffer and directory entry block scratchpad buffer */
# define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2 + MAXDIRB(FF_MAX_LFN)); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+FF_MAX_LFN+1); }
# define FREE_NAMBUF() ff_memfree(lfn)
# else
# define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working buffer */
# define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; }
# define FREE_NAMBUF() ff_memfree(lfn)
# endif
# else
# error Wrong FF_USE_LFN setting
# endif
# endif
/*--------------------------------*/
/* Code conversion tables */
/*--------------------------------*/
# if FF_CODE_PAGE == 0 /* Run-time code page configuration */
# define CODEPAGE CodePage
static WORD CodePage ; /* Current code page */
static const BYTE * ExCvt , * DbcTbl ; /* Pointer to current SBCS up-case table and DBCS code range table below */
static const BYTE Ct437 [ ] = TBL_CT437 ;
static const BYTE Ct720 [ ] = TBL_CT720 ;
static const BYTE Ct737 [ ] = TBL_CT737 ;
static const BYTE Ct771 [ ] = TBL_CT771 ;
static const BYTE Ct775 [ ] = TBL_CT775 ;
static const BYTE Ct850 [ ] = TBL_CT850 ;
static const BYTE Ct852 [ ] = TBL_CT852 ;
static const BYTE Ct855 [ ] = TBL_CT855 ;
static const BYTE Ct857 [ ] = TBL_CT857 ;
static const BYTE Ct860 [ ] = TBL_CT860 ;
static const BYTE Ct861 [ ] = TBL_CT861 ;
static const BYTE Ct862 [ ] = TBL_CT862 ;
static const BYTE Ct863 [ ] = TBL_CT863 ;
static const BYTE Ct864 [ ] = TBL_CT864 ;
static const BYTE Ct865 [ ] = TBL_CT865 ;
static const BYTE Ct866 [ ] = TBL_CT866 ;
static const BYTE Ct869 [ ] = TBL_CT869 ;
static const BYTE Dc932 [ ] = TBL_DC932 ;
static const BYTE Dc936 [ ] = TBL_DC936 ;
static const BYTE Dc949 [ ] = TBL_DC949 ;
static const BYTE Dc950 [ ] = TBL_DC950 ;
# elif FF_CODE_PAGE < 900 /* Static code page configuration (SBCS) */
# define CODEPAGE FF_CODE_PAGE
static const BYTE ExCvt [ ] = MKCVTBL ( TBL_CT , FF_CODE_PAGE ) ;
# else /* Static code page configuration (DBCS) */
# define CODEPAGE FF_CODE_PAGE
static const BYTE DbcTbl [ ] = MKCVTBL ( TBL_DC , FF_CODE_PAGE ) ;
# endif
/*--------------------------------------------------------------------------
Module Private Functions
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*-----------------------------------------------------------------------*/
/* Load/Store multi-byte word in the FAT structure */
/*-----------------------------------------------------------------------*/
static
WORD ld_word ( const BYTE * ptr ) /* Load a 2-byte little-endian word */
{
WORD rv ;
rv = ptr [ 1 ] ;
rv = rv < < 8 | ptr [ 0 ] ;
return rv ;
}
static
DWORD ld_dword ( const BYTE * ptr ) /* Load a 4-byte little-endian word */
{
DWORD rv ;
rv = ptr [ 3 ] ;
rv = rv < < 8 | ptr [ 2 ] ;
rv = rv < < 8 | ptr [ 1 ] ;
rv = rv < < 8 | ptr [ 0 ] ;
return rv ;
}
# if FF_FS_EXFAT
static
QWORD ld_qword ( const BYTE * ptr ) /* Load an 8-byte little-endian word */
{
QWORD rv ;
rv = ptr [ 7 ] ;
rv = rv < < 8 | ptr [ 6 ] ;
rv = rv < < 8 | ptr [ 5 ] ;
rv = rv < < 8 | ptr [ 4 ] ;
rv = rv < < 8 | ptr [ 3 ] ;
rv = rv < < 8 | ptr [ 2 ] ;
rv = rv < < 8 | ptr [ 1 ] ;
rv = rv < < 8 | ptr [ 0 ] ;
return rv ;
}
# endif
# if !FF_FS_READONLY
static
void st_word ( BYTE * ptr , WORD val ) /* Store a 2-byte word in little-endian */
{
* ptr + + = ( BYTE ) val ; val > > = 8 ;
* ptr + + = ( BYTE ) val ;
}
static
void st_dword ( BYTE * ptr , DWORD val ) /* Store a 4-byte word in little-endian */
{
* ptr + + = ( BYTE ) val ; val > > = 8 ;
* ptr + + = ( BYTE ) val ; val > > = 8 ;
* ptr + + = ( BYTE ) val ; val > > = 8 ;
* ptr + + = ( BYTE ) val ;
}
# if FF_FS_EXFAT
static
void st_qword ( BYTE * ptr , QWORD val ) /* Store an 8-byte word in little-endian */
{
* ptr + + = ( BYTE ) val ; val > > = 8 ;
* ptr + + = ( BYTE ) val ; val > > = 8 ;
* ptr + + = ( BYTE ) val ; val > > = 8 ;
* ptr + + = ( BYTE ) val ; val > > = 8 ;
* ptr + + = ( BYTE ) val ; val > > = 8 ;
* ptr + + = ( BYTE ) val ; val > > = 8 ;
* ptr + + = ( BYTE ) val ; val > > = 8 ;
* ptr + + = ( BYTE ) val ;
}
# endif
# endif /* !FF_FS_READONLY */
/*-----------------------------------------------------------------------*/
/* String functions */
/*-----------------------------------------------------------------------*/
/* Copy memory to memory */
static
void mem_cpy ( void * dst , const void * src , UINT cnt )
{
BYTE * d = ( BYTE * ) dst ;
const BYTE * s = ( const BYTE * ) src ;
if ( cnt ! = 0 ) {
do {
* d + + = * s + + ;
} while ( - - cnt ) ;
}
}
/* Fill memory block */
static
void mem_set ( void * dst , int val , UINT cnt )
{
BYTE * d = ( BYTE * ) dst ;
do {
* d + + = ( BYTE ) val ;
} while ( - - cnt ) ;
}
/* Compare memory block */
static
int mem_cmp ( const void * dst , const void * src , UINT cnt ) /* ZR:same, NZ:different */
{
const BYTE * d = ( const BYTE * ) dst , * s = ( const BYTE * ) src ;
int r = 0 ;
do {
r = * d + + - * s + + ;
} while ( - - cnt & & r = = 0 ) ;
return r ;
}
/* Check if chr is contained in the string */
static
int chk_chr ( const char * str , int chr ) /* NZ:contained, ZR:not contained */
{
while ( * str & & * str ! = chr ) str + + ;
return * str ;
}
/* Test if the character is DBC 1st byte */
static
int dbc_1st ( BYTE c )
{
# if FF_CODE_PAGE == 0 /* Variable code page */
if ( DbcTbl & & c > = DbcTbl [ 0 ] ) {
if ( c < = DbcTbl [ 1 ] ) return 1 ; /* 1st byte range 1 */
if ( c > = DbcTbl [ 2 ] & & c < = DbcTbl [ 3 ] ) return 1 ; /* 1st byte range 2 */
}
# elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */
if ( c > = DbcTbl [ 0 ] ) {
if ( c < = DbcTbl [ 1 ] ) return 1 ;
if ( c > = DbcTbl [ 2 ] & & c < = DbcTbl [ 3 ] ) return 1 ;
}
# else /* SBCS fixed code page */
if ( c ) return 0 ; /* Always false */
# endif
return 0 ;
}
/* Test if the character is DBC 2nd byte */
static
int dbc_2nd ( BYTE c )
{
# if FF_CODE_PAGE == 0 /* Variable code page */
if ( DbcTbl & & c > = DbcTbl [ 4 ] ) {
if ( c < = DbcTbl [ 5 ] ) return 1 ; /* 2nd byte range 1 */
if ( c > = DbcTbl [ 6 ] & & c < = DbcTbl [ 7 ] ) return 1 ; /* 2nd byte range 2 */
if ( c > = DbcTbl [ 8 ] & & c < = DbcTbl [ 9 ] ) return 1 ; /* 2nd byte range 3 */
}
# elif FF_CODE_PAGE >= 900 /* DBCD fixed code page */
if ( c > = DbcTbl [ 4 ] ) {
if ( c < = DbcTbl [ 5 ] ) return 1 ;
if ( c > = DbcTbl [ 6 ] & & c < = DbcTbl [ 7 ] ) return 1 ;
if ( c > = DbcTbl [ 8 ] & & c < = DbcTbl [ 9 ] ) return 1 ;
}
# else /* SBCS fixed code page */
if ( c ) return 0 ; /* Always false */
# endif
return 0 ;
}
# if FF_FS_REENTRANT
/*-----------------------------------------------------------------------*/
/* Request/Release grant to access the volume */
/*-----------------------------------------------------------------------*/
static
int lock_fs ( /* 1:Ok, 0:timeout */
FATFS * fs /* Filesystem object */
)
{
return ff_req_grant ( fs - > sobj ) ;
}
static
void unlock_fs (
FATFS * fs , /* Filesystem object */
FRESULT res /* Result code to be returned */
)
{
if ( fs & & res ! = FR_NOT_ENABLED & & res ! = FR_INVALID_DRIVE & & res ! = FR_TIMEOUT ) {
ff_rel_grant ( fs - > sobj ) ;
}
}
# endif
# if FF_FS_LOCK != 0
/*-----------------------------------------------------------------------*/
/* File lock control functions */
/*-----------------------------------------------------------------------*/
static
FRESULT chk_lock ( /* Check if the file can be accessed */
DIR * dp , /* Directory object pointing the file to be checked */
int acc /* Desired access type (0:Read mode open, 1:Write mode open, 2:Delete or rename) */
)
{
UINT i , be ;
/* Search open object table for the object */
be = 0 ;
for ( i = 0 ; i < FF_FS_LOCK ; i + + ) {
if ( Files [ i ] . fs ) { /* Existing entry */
if ( Files [ i ] . fs = = dp - > obj . fs & & /* Check if the object matches with an open object */
Files [ i ] . clu = = dp - > obj . sclust & &
Files [ i ] . ofs = = dp - > dptr ) break ;
} else { /* Blank entry */
be = 1 ;
}
}
if ( i = = FF_FS_LOCK ) { /* The object has not been opened */
return ( ! be & & acc ! = 2 ) ? FR_TOO_MANY_OPEN_FILES : FR_OK ; /* Is there a blank entry for new object? */
}
/* The object was opened. Reject any open against writing file and all write mode open */
return ( acc ! = 0 | | Files [ i ] . ctr = = 0x100 ) ? FR_LOCKED : FR_OK ;
}
static
int enq_lock ( void ) /* Check if an entry is available for a new object */
{
UINT i ;
for ( i = 0 ; i < FF_FS_LOCK & & Files [ i ] . fs ; i + + ) ;
return ( i = = FF_FS_LOCK ) ? 0 : 1 ;
}
static
UINT inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */
DIR * dp , /* Directory object pointing the file to register or increment */
int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */
)
{
UINT i ;
for ( i = 0 ; i < FF_FS_LOCK ; i + + ) { /* Find the object */
if ( Files [ i ] . fs = = dp - > obj . fs & &
Files [ i ] . clu = = dp - > obj . sclust & &
Files [ i ] . ofs = = dp - > dptr ) break ;
}
if ( i = = FF_FS_LOCK ) { /* Not opened. Register it as new. */
for ( i = 0 ; i < FF_FS_LOCK & & Files [ i ] . fs ; i + + ) ;
if ( i = = FF_FS_LOCK ) return 0 ; /* No free entry to register (int err) */
Files [ i ] . fs = dp - > obj . fs ;
Files [ i ] . clu = dp - > obj . sclust ;
Files [ i ] . ofs = dp - > dptr ;
Files [ i ] . ctr = 0 ;
}
if ( acc & & Files [ i ] . ctr ) return 0 ; /* Access violation (int err) */
Files [ i ] . ctr = acc ? 0x100 : Files [ i ] . ctr + 1 ; /* Set semaphore value */
return i + 1 ; /* Index number origin from 1 */
}
static
FRESULT dec_lock ( /* Decrement object open counter */
UINT i /* Semaphore index (1..) */
)
{
WORD n ;
FRESULT res ;
if ( - - i < FF_FS_LOCK ) { /* Index number origin from 0 */
n = Files [ i ] . ctr ;
if ( n = = 0x100 ) n = 0 ; /* If write mode open, delete the entry */
if ( n > 0 ) n - - ; /* Decrement read mode open count */
Files [ i ] . ctr = n ;
if ( n = = 0 ) Files [ i ] . fs = 0 ; /* Delete the entry if open count gets zero */
res = FR_OK ;
} else {
res = FR_INT_ERR ; /* Invalid index nunber */
}
return res ;
}
static
void clear_lock ( /* Clear lock entries of the volume */
FATFS * fs
)
{
UINT i ;
for ( i = 0 ; i < FF_FS_LOCK ; i + + ) {
if ( Files [ i ] . fs = = fs ) Files [ i ] . fs = 0 ;
}
}
# endif /* FF_FS_LOCK != 0 */
/*-----------------------------------------------------------------------*/
/* Move/Flush disk access window in the filesystem object */
/*-----------------------------------------------------------------------*/
# if !FF_FS_READONLY
static
FRESULT sync_window ( /* Returns FR_OK or FR_DISK_ERR */
FATFS * fs /* Filesystem object */
)
{
FRESULT res = FR_OK ;
if ( fs - > wflag ) { /* Is the disk access window dirty */
if ( disk_write ( fs - > pdrv , fs - > win , fs - > winsect , 1 ) = = RES_OK ) { /* Write back the window */
fs - > wflag = 0 ; /* Clear window dirty flag */
if ( fs - > winsect - fs - > fatbase < fs - > fsize ) { /* Is it in the 1st FAT? */
if ( fs - > n_fats = = 2 ) disk_write ( fs - > pdrv , fs - > win , fs - > winsect + fs - > fsize , 1 ) ; /* Reflect it to 2nd FAT if needed */
}
} else {
res = FR_DISK_ERR ;
}
}
return res ;
}
# endif
static
FRESULT move_window ( /* Returns FR_OK or FR_DISK_ERR */
FATFS * fs , /* Filesystem object */
DWORD sector /* Sector number to make appearance in the fs->win[] */
)
{
FRESULT res = FR_OK ;
if ( sector ! = fs - > winsect ) { /* Window offset changed? */
# if !FF_FS_READONLY
res = sync_window ( fs ) ; /* Write-back changes */
# endif
if ( res = = FR_OK ) { /* Fill sector window with new data */
if ( disk_read ( fs - > pdrv , fs - > win , sector , 1 ) ! = RES_OK ) {
sector = 0xFFFFFFFF ; /* Invalidate window if read data is not valid */
res = FR_DISK_ERR ;
}
fs - > winsect = sector ;
}
}
return res ;
}
# if !FF_FS_READONLY
/*-----------------------------------------------------------------------*/
/* Synchronize filesystem and data on the storage */
/*-----------------------------------------------------------------------*/
static
FRESULT sync_fs ( /* Returns FR_OK or FR_DISK_ERR */
FATFS * fs /* Filesystem object */
)
{
FRESULT res ;
res = sync_window ( fs ) ;
if ( res = = FR_OK ) {
if ( fs - > fs_type = = FS_FAT32 & & fs - > fsi_flag = = 1 ) { /* FAT32: Update FSInfo sector if needed */
/* Create FSInfo structure */
mem_set ( fs - > win , 0 , SS ( fs ) ) ;
st_word ( fs - > win + BS_55AA , 0xAA55 ) ;
st_dword ( fs - > win + FSI_LeadSig , 0x41615252 ) ;
st_dword ( fs - > win + FSI_StrucSig , 0x61417272 ) ;
st_dword ( fs - > win + FSI_Free_Count , fs - > free_clst ) ;
st_dword ( fs - > win + FSI_Nxt_Free , fs - > last_clst ) ;
/* Write it into the FSInfo sector */
fs - > winsect = fs - > volbase + 1 ;
disk_write ( fs - > pdrv , fs - > win , fs - > winsect , 1 ) ;
fs - > fsi_flag = 0 ;
}
/* Make sure that no pending write process in the lower layer */
if ( disk_ioctl ( fs - > pdrv , CTRL_SYNC , 0 ) ! = RES_OK ) res = FR_DISK_ERR ;
}
return res ;
}
# endif
/*-----------------------------------------------------------------------*/
/* Get physical sector number from cluster number */
/*-----------------------------------------------------------------------*/
static
DWORD clst2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */
FATFS * fs , /* Filesystem object */
DWORD clst /* Cluster# to be converted */
)
{
clst - = 2 ; /* Cluster number is origin from 2 */
if ( clst > = fs - > n_fatent - 2 ) return 0 ; /* Is it invalid cluster number? */
return fs - > database + fs - > csize * clst ; /* Start sector number of the cluster */
}
/*-----------------------------------------------------------------------*/
/* FAT access - Read value of a FAT entry */
/*-----------------------------------------------------------------------*/
static
DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */
FFOBJID * obj , /* Corresponding object */
DWORD clst /* Cluster number to get the value */
)
{
UINT wc , bc ;
DWORD val ;
FATFS * fs = obj - > fs ;
if ( clst < 2 | | clst > = fs - > n_fatent ) { /* Check if in valid range */
val = 1 ; /* Internal error */
} else {
val = 0xFFFFFFFF ; /* Default value falls on disk error */
switch ( fs - > fs_type ) {
case FS_FAT12 :
bc = ( UINT ) clst ; bc + = bc / 2 ;
if ( move_window ( fs , fs - > fatbase + ( bc / SS ( fs ) ) ) ! = FR_OK ) break ;
wc = fs - > win [ bc + + % SS ( fs ) ] ; /* Get 1st byte of the entry */
if ( move_window ( fs , fs - > fatbase + ( bc / SS ( fs ) ) ) ! = FR_OK ) break ;
wc | = fs - > win [ bc % SS ( fs ) ] < < 8 ; /* Merge 2nd byte of the entry */
val = ( clst & 1 ) ? ( wc > > 4 ) : ( wc & 0xFFF ) ; /* Adjust bit position */
break ;
case FS_FAT16 :
if ( move_window ( fs , fs - > fatbase + ( clst / ( SS ( fs ) / 2 ) ) ) ! = FR_OK ) break ;
val = ld_word ( fs - > win + clst * 2 % SS ( fs ) ) ; /* Simple WORD array */
break ;
case FS_FAT32 :
if ( move_window ( fs , fs - > fatbase + ( clst / ( SS ( fs ) / 4 ) ) ) ! = FR_OK ) break ;
val = ld_dword ( fs - > win + clst * 4 % SS ( fs ) ) & 0x0FFFFFFF ; /* Simple DWORD array but mask out upper 4 bits */
break ;
# if FF_FS_EXFAT
case FS_EXFAT :
if ( obj - > objsize ! = 0 ) {
DWORD cofs = clst - obj - > sclust ; /* Offset from start cluster */
DWORD clen = ( DWORD ) ( ( obj - > objsize - 1 ) / SS ( fs ) ) / fs - > csize ; /* Number of clusters - 1 */
if ( obj - > stat = = 2 & & cofs < = clen ) { /* Is it a contiguous chain? */
val = ( cofs = = clen ) ? 0x7FFFFFFF : clst + 1 ; /* No data on the FAT, generate the value */
break ;
}
if ( obj - > stat = = 3 & & cofs < obj - > n_cont ) { /* Is it in the 1st fragment? */
val = clst + 1 ; /* Generate the value */
break ;
}
if ( obj - > stat ! = 2 ) { /* Get value from FAT if FAT chain is valid */
if ( obj - > n_frag ! = 0 ) { /* Is it on the growing edge? */
val = 0x7FFFFFFF ; /* Generate EOC */
} else {
if ( move_window ( fs , fs - > fatbase + ( clst / ( SS ( fs ) / 4 ) ) ) ! = FR_OK ) break ;
val = ld_dword ( fs - > win + clst * 4 % SS ( fs ) ) & 0x7FFFFFFF ;
}
break ;
}
}
/* go to default */
# endif
default :
val = 1 ; /* Internal error */
}
}
return val ;
}
# if !FF_FS_READONLY
/*-----------------------------------------------------------------------*/
/* FAT access - Change value of a FAT entry */
/*-----------------------------------------------------------------------*/
static
FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */
FATFS * fs , /* Corresponding filesystem object */
DWORD clst , /* FAT index number (cluster number) to be changed */
DWORD val /* New value to be set to the entry */
)
{
UINT bc ;
BYTE * p ;
FRESULT res = FR_INT_ERR ;
if ( clst > = 2 & & clst < fs - > n_fatent ) { /* Check if in valid range */
switch ( fs - > fs_type ) {
case FS_FAT12 :
bc = ( UINT ) clst ; bc + = bc / 2 ; /* bc: byte offset of the entry */
res = move_window ( fs , fs - > fatbase + ( bc / SS ( fs ) ) ) ;
if ( res ! = FR_OK ) break ;
p = fs - > win + bc + + % SS ( fs ) ;
* p = ( clst & 1 ) ? ( ( * p & 0x0F ) | ( ( BYTE ) val < < 4 ) ) : ( BYTE ) val ; /* Put 1st byte */
fs - > wflag = 1 ;
res = move_window ( fs , fs - > fatbase + ( bc / SS ( fs ) ) ) ;
if ( res ! = FR_OK ) break ;
p = fs - > win + bc % SS ( fs ) ;
* p = ( clst & 1 ) ? ( BYTE ) ( val > > 4 ) : ( ( * p & 0xF0 ) | ( ( BYTE ) ( val > > 8 ) & 0x0F ) ) ; /* Put 2nd byte */
fs - > wflag = 1 ;
break ;
case FS_FAT16 :
res = move_window ( fs , fs - > fatbase + ( clst / ( SS ( fs ) / 2 ) ) ) ;
if ( res ! = FR_OK ) break ;
st_word ( fs - > win + clst * 2 % SS ( fs ) , ( WORD ) val ) ; /* Simple WORD array */
fs - > wflag = 1 ;
break ;
case FS_FAT32 :
# if FF_FS_EXFAT
case FS_EXFAT :
# endif
res = move_window ( fs , fs - > fatbase + ( clst / ( SS ( fs ) / 4 ) ) ) ;
if ( res ! = FR_OK ) break ;
if ( ! FF_FS_EXFAT | | fs - > fs_type ! = FS_EXFAT ) {
val = ( val & 0x0FFFFFFF ) | ( ld_dword ( fs - > win + clst * 4 % SS ( fs ) ) & 0xF0000000 ) ;
}
st_dword ( fs - > win + clst * 4 % SS ( fs ) , val ) ;
fs - > wflag = 1 ;
break ;
}
}
return res ;
}
# endif /* !FF_FS_READONLY */
# if FF_FS_EXFAT && !FF_FS_READONLY
/*-----------------------------------------------------------------------*/
/* exFAT: Accessing FAT and Allocation Bitmap */
/*-----------------------------------------------------------------------*/
/*--------------------------------------*/
/* Find a contiguous free cluster block */
/*--------------------------------------*/
static
DWORD find_bitmap ( /* 0:Not found, 2..:Cluster block found, 0xFFFFFFFF:Disk error */
FATFS * fs , /* Filesystem object */
DWORD clst , /* Cluster number to scan from */
DWORD ncl /* Number of contiguous clusters to find (1..) */
)
{
BYTE bm , bv ;
UINT i ;
DWORD val , scl , ctr ;
clst - = 2 ; /* The first bit in the bitmap corresponds to cluster #2 */
if ( clst > = fs - > n_fatent - 2 ) clst = 0 ;
scl = val = clst ; ctr = 0 ;
for ( ; ; ) {
if ( move_window ( fs , fs - > database + val / 8 / SS ( fs ) ) ! = FR_OK ) return 0xFFFFFFFF ; /* (assuming bitmap is located top of the cluster heap) */
i = val / 8 % SS ( fs ) ; bm = 1 < < ( val % 8 ) ;
do {
do {
bv = fs - > win [ i ] & bm ; bm < < = 1 ; /* Get bit value */
if ( + + val > = fs - > n_fatent - 2 ) { /* Next cluster (with wrap-around) */
val = 0 ; bm = 0 ; i = SS ( fs ) ;
}
if ( ! bv ) { /* Is it a free cluster? */
if ( + + ctr = = ncl ) return scl + 2 ; /* Check if run length is sufficient for required */
} else {
scl = val ; ctr = 0 ; /* Encountered a cluster in-use, restart to scan */
}
if ( val = = clst ) return 0 ; /* All cluster scanned? */
} while ( bm ) ;
bm = 1 ;
} while ( + + i < SS ( fs ) ) ;
}
}
/*----------------------------------------*/
/* Set/Clear a block of allocation bitmap */
/*----------------------------------------*/
static
FRESULT change_bitmap (
FATFS * fs , /* Filesystem object */
DWORD clst , /* Cluster number to change from */
DWORD ncl , /* Number of clusters to be changed */
int bv /* bit value to be set (0 or 1) */
)
{
BYTE bm ;
UINT i ;
DWORD sect ;
clst - = 2 ; /* The first bit corresponds to cluster #2 */
sect = fs - > database + clst / 8 / SS ( fs ) ; /* Sector address (assuming bitmap is located top of the cluster heap) */
i = clst / 8 % SS ( fs ) ; /* Byte offset in the sector */
bm = 1 < < ( clst % 8 ) ; /* Bit mask in the byte */
for ( ; ; ) {
if ( move_window ( fs , sect + + ) ! = FR_OK ) return FR_DISK_ERR ;
do {
do {
if ( bv = = ( int ) ( ( fs - > win [ i ] & bm ) ! = 0 ) ) return FR_INT_ERR ; /* Is the bit expected value? */
fs - > win [ i ] ^ = bm ; /* Flip the bit */
fs - > wflag = 1 ;
if ( - - ncl = = 0 ) return FR_OK ; /* All bits processed? */
} while ( bm < < = 1 ) ; /* Next bit */
bm = 1 ;
} while ( + + i < SS ( fs ) ) ; /* Next byte */
i = 0 ;
}
}
/*---------------------------------------------*/
/* Fill the first fragment of the FAT chain */
/*---------------------------------------------*/
static
FRESULT fill_first_frag (
FFOBJID * obj /* Pointer to the corresponding object */
)
{
FRESULT res ;
DWORD cl , n ;
if ( obj - > stat = = 3 ) { /* Has the object been changed 'fragmented' in this session? */
for ( cl = obj - > sclust , n = obj - > n_cont ; n ; cl + + , n - - ) { /* Create cluster chain on the FAT */
res = put_fat ( obj - > fs , cl , cl + 1 ) ;
if ( res ! = FR_OK ) return res ;
}
obj - > stat = 0 ; /* Change status 'FAT chain is valid' */
}
return FR_OK ;
}
/*---------------------------------------------*/
/* Fill the last fragment of the FAT chain */
/*---------------------------------------------*/
static
FRESULT fill_last_frag (
FFOBJID * obj , /* Pointer to the corresponding object */
DWORD lcl , /* Last cluster of the fragment */
DWORD term /* Value to set the last FAT entry */
)
{
FRESULT res ;
while ( obj - > n_frag > 0 ) { /* Create the last chain on the FAT */
res = put_fat ( obj - > fs , lcl - obj - > n_frag + 1 , ( obj - > n_frag > 1 ) ? lcl - obj - > n_frag + 2 : term ) ;
if ( res ! = FR_OK ) return res ;
obj - > n_frag - - ;
}
return FR_OK ;
}
# endif /* FF_FS_EXFAT && !FF_FS_READONLY */
# if !FF_FS_READONLY
/*-----------------------------------------------------------------------*/
/* FAT handling - Remove a cluster chain */
/*-----------------------------------------------------------------------*/
static
FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */
FFOBJID * obj , /* Corresponding object */
DWORD clst , /* Cluster to remove a chain from */
DWORD pclst /* Previous cluster of clst (0:entire chain) */
)
{
FRESULT res = FR_OK ;
DWORD nxt ;
FATFS * fs = obj - > fs ;
# if FF_FS_EXFAT || FF_USE_TRIM
DWORD scl = clst , ecl = clst ;
# endif
# if FF_USE_TRIM
DWORD rt [ 2 ] ;
# endif
if ( clst < 2 | | clst > = fs - > n_fatent ) return FR_INT_ERR ; /* Check if in valid range */
/* Mark the previous cluster 'EOC' on the FAT if it exists */
if ( pclst ! = 0 & & ( ! FF_FS_EXFAT | | fs - > fs_type ! = FS_EXFAT | | obj - > stat ! = 2 ) ) {
res = put_fat ( fs , pclst , 0xFFFFFFFF ) ;
if ( res ! = FR_OK ) return res ;
}
/* Remove the chain */
do {
nxt = get_fat ( obj , clst ) ; /* Get cluster status */
if ( nxt = = 0 ) break ; /* Empty cluster? */
if ( nxt = = 1 ) return FR_INT_ERR ; /* Internal error? */
if ( nxt = = 0xFFFFFFFF ) return FR_DISK_ERR ; /* Disk error? */
if ( ! FF_FS_EXFAT | | fs - > fs_type ! = FS_EXFAT ) {
res = put_fat ( fs , clst , 0 ) ; /* Mark the cluster 'free' on the FAT */
if ( res ! = FR_OK ) return res ;
}
if ( fs - > free_clst < fs - > n_fatent - 2 ) { /* Update FSINFO */
fs - > free_clst + + ;
fs - > fsi_flag | = 1 ;
}
# if FF_FS_EXFAT || FF_USE_TRIM
if ( ecl + 1 = = nxt ) { /* Is next cluster contiguous? */
ecl = nxt ;
} else { /* End of contiguous cluster block */
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) {
res = change_bitmap ( fs , scl , ecl - scl + 1 , 0 ) ; /* Mark the cluster block 'free' on the bitmap */
if ( res ! = FR_OK ) return res ;
}
# endif
# if FF_USE_TRIM
rt [ 0 ] = clst2sect ( fs , scl ) ; /* Start of data area freed */
rt [ 1 ] = clst2sect ( fs , ecl ) + fs - > csize - 1 ; /* End of data area freed */
disk_ioctl ( fs - > pdrv , CTRL_TRIM , rt ) ; /* Inform device the data in the block is no longer needed */
# endif
scl = ecl = nxt ;
}
# endif
clst = nxt ; /* Next cluster */
} while ( clst < fs - > n_fatent ) ; /* Repeat while not the last link */
# if FF_FS_EXFAT
/* Some post processes for chain status */
if ( fs - > fs_type = = FS_EXFAT ) {
if ( pclst = = 0 ) { /* Has the entire chain been removed? */
obj - > stat = 0 ; /* Change the chain status 'initial' */
} else {
if ( obj - > stat = = 0 ) { /* Is it a fragmented chain from the beginning of this session? */
clst = obj - > sclust ; /* Follow the chain to check if it gets contiguous */
while ( clst ! = pclst ) {
nxt = get_fat ( obj , clst ) ;
if ( nxt < 2 ) return FR_INT_ERR ;
if ( nxt = = 0xFFFFFFFF ) return FR_DISK_ERR ;
if ( nxt ! = clst + 1 ) break ; /* Not contiguous? */
clst + + ;
}
if ( clst = = pclst ) { /* Has the chain got contiguous again? */
obj - > stat = 2 ; /* Change the chain status 'contiguous' */
}
} else {
if ( obj - > stat = = 3 & & pclst > = obj - > sclust & & pclst < = obj - > sclust + obj - > n_cont ) { /* Was the chain fragmented in this session and got contiguous again? */
obj - > stat = 2 ; /* Change the chain status 'contiguous' */
}
}
}
}
# endif
return FR_OK ;
}
/*-----------------------------------------------------------------------*/
/* FAT handling - Stretch a chain or Create a new chain */
/*-----------------------------------------------------------------------*/
static
DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
FFOBJID * obj , /* Corresponding object */
DWORD clst /* Cluster# to stretch, 0:Create a new chain */
)
{
DWORD cs , ncl , scl ;
FRESULT res ;
FATFS * fs = obj - > fs ;
if ( clst = = 0 ) { /* Create a new chain */
scl = fs - > last_clst ; /* Suggested cluster to start to find */
if ( scl = = 0 | | scl > = fs - > n_fatent ) scl = 1 ;
}
else { /* Stretch a chain */
cs = get_fat ( obj , clst ) ; /* Check the cluster status */
if ( cs < 2 ) return 1 ; /* Test for insanity */
if ( cs = = 0xFFFFFFFF ) return cs ; /* Test for disk error */
if ( cs < fs - > n_fatent ) return cs ; /* It is already followed by next cluster */
scl = clst ; /* Cluster to start to find */
}
if ( fs - > free_clst = = 0 ) return 0 ; /* No free cluster */
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) { /* On the exFAT volume */
ncl = find_bitmap ( fs , scl , 1 ) ; /* Find a free cluster */
if ( ncl = = 0 | | ncl = = 0xFFFFFFFF ) return ncl ; /* No free cluster or hard error? */
res = change_bitmap ( fs , ncl , 1 , 1 ) ; /* Mark the cluster 'in use' */
if ( res = = FR_INT_ERR ) return 1 ;
if ( res = = FR_DISK_ERR ) return 0xFFFFFFFF ;
if ( clst = = 0 ) { /* Is it a new chain? */
obj - > stat = 2 ; /* Set status 'contiguous' */
} else { /* It is a stretched chain */
if ( obj - > stat = = 2 & & ncl ! = scl + 1 ) { /* Is the chain got fragmented? */
obj - > n_cont = scl - obj - > sclust ; /* Set size of the contiguous part */
obj - > stat = 3 ; /* Change status 'just fragmented' */
}
}
if ( obj - > stat ! = 2 ) { /* Is the file non-contiguous? */
if ( ncl = = clst + 1 ) { /* Is the cluster next to previous one? */
obj - > n_frag = obj - > n_frag ? obj - > n_frag + 1 : 2 ; /* Increment size of last framgent */
} else { /* New fragment */
if ( obj - > n_frag = = 0 ) obj - > n_frag = 1 ;
res = fill_last_frag ( obj , clst , ncl ) ; /* Fill last fragment on the FAT and link it to new one */
if ( res = = FR_OK ) obj - > n_frag = 1 ;
}
}
} else
# endif
{ /* On the FAT/FAT32 volume */
ncl = 0 ;
if ( scl = = clst ) { /* Stretching an existing chain? */
ncl = scl + 1 ; /* Test if next cluster is free */
if ( ncl > = fs - > n_fatent ) ncl = 2 ;
cs = get_fat ( obj , ncl ) ; /* Get next cluster status */
if ( cs = = 1 | | cs = = 0xFFFFFFFF ) return cs ; /* Test for error */
if ( cs ! = 0 ) { /* Not free? */
cs = fs - > last_clst ; /* Start at suggested cluster if it is valid */
if ( cs > = 2 & & cs < fs - > n_fatent ) scl = cs ;
ncl = 0 ;
}
}
if ( ncl = = 0 ) { /* The new cluster cannot be contiguous and find another fragment */
ncl = scl ; /* Start cluster */
for ( ; ; ) {
ncl + + ; /* Next cluster */
if ( ncl > = fs - > n_fatent ) { /* Check wrap-around */
ncl = 2 ;
if ( ncl > scl ) return 0 ; /* No free cluster found? */
}
cs = get_fat ( obj , ncl ) ; /* Get the cluster status */
if ( cs = = 0 ) break ; /* Found a free cluster? */
if ( cs = = 1 | | cs = = 0xFFFFFFFF ) return cs ; /* Test for error */
if ( ncl = = scl ) return 0 ; /* No free cluster found? */
}
}
res = put_fat ( fs , ncl , 0xFFFFFFFF ) ; /* Mark the new cluster 'EOC' */
if ( res = = FR_OK & & clst ! = 0 ) {
res = put_fat ( fs , clst , ncl ) ; /* Link it from the previous one if needed */
}
}
if ( res = = FR_OK ) { /* Update FSINFO if function succeeded. */
fs - > last_clst = ncl ;
if ( fs - > free_clst < = fs - > n_fatent - 2 ) fs - > free_clst - - ;
fs - > fsi_flag | = 1 ;
} else {
ncl = ( res = = FR_DISK_ERR ) ? 0xFFFFFFFF : 1 ; /* Failed. Generate error status */
}
return ncl ; /* Return new cluster number or error status */
}
# endif /* !FF_FS_READONLY */
# if FF_USE_FASTSEEK
/*-----------------------------------------------------------------------*/
/* FAT handling - Convert offset into cluster with link map table */
/*-----------------------------------------------------------------------*/
static
DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */
FIL * fp , /* Pointer to the file object */
FSIZE_t ofs /* File offset to be converted to cluster# */
)
{
DWORD cl , ncl , * tbl ;
FATFS * fs = fp - > obj . fs ;
tbl = fp - > cltbl + 1 ; /* Top of CLMT */
cl = ( DWORD ) ( ofs / SS ( fs ) / fs - > csize ) ; /* Cluster order from top of the file */
for ( ; ; ) {
ncl = * tbl + + ; /* Number of cluters in the fragment */
if ( ncl = = 0 ) return 0 ; /* End of table? (error) */
if ( cl < ncl ) break ; /* In this fragment? */
cl - = ncl ; tbl + + ; /* Next fragment */
}
return cl + * tbl ; /* Return the cluster number */
}
# endif /* FF_USE_FASTSEEK */
/*-----------------------------------------------------------------------*/
/* Directory handling - Fill a cluster with zeros */
/*-----------------------------------------------------------------------*/
# if !FF_FS_READONLY
static
FRESULT dir_clear ( /* Returns FR_OK or FR_DISK_ERR */
FATFS * fs , /* Filesystem object */
DWORD clst /* Directory table to clear */
)
{
DWORD sect ;
UINT n , szb ;
BYTE * ibuf ;
if ( sync_window ( fs ) ! = FR_OK ) return FR_DISK_ERR ; /* Flush disk access window */
sect = clst2sect ( fs , clst ) ; /* Top of the cluster */
fs - > winsect = sect ; /* Set window to top of the cluster */
mem_set ( fs - > win , 0 , SS ( fs ) ) ; /* Clear window buffer */
# if FF_USE_LFN == 3 /* Quick table clear by using multi-secter write */
/* Allocate a temporary buffer (32 KB max) */
for ( szb = ( ( DWORD ) fs - > csize * SS ( fs ) > = 0x8000 ) ? 0x8000 : fs - > csize * SS ( fs ) ; szb > SS ( fs ) & & ! ( ibuf = ff_memalloc ( szb ) ) ; szb / = 2 ) ;
if ( szb > SS ( fs ) ) { /* Buffer allocated? */
mem_set ( ibuf , 0 , szb ) ;
szb / = SS ( fs ) ; /* Bytes -> Sectors */
for ( n = 0 ; n < fs - > csize & & disk_write ( fs - > pdrv , ibuf , sect + n , szb ) = = RES_OK ; n + = szb ) ; /* Fill the cluster with 0 */
ff_memfree ( ibuf ) ;
} else
# endif
{
ibuf = fs - > win ; szb = 1 ; /* Use window buffer (single-sector writes may take a time) */
for ( n = 0 ; n < fs - > csize & & disk_write ( fs - > pdrv , ibuf , sect + n , szb ) = = RES_OK ; n + = szb ) ; /* Fill the cluster with 0 */
}
return ( n = = fs - > csize ) ? FR_OK : FR_DISK_ERR ;
}
# endif /* !FF_FS_READONLY */
/*-----------------------------------------------------------------------*/
/* Directory handling - Set directory index */
/*-----------------------------------------------------------------------*/
static
FRESULT dir_sdi ( /* FR_OK(0):succeeded, !=0:error */
DIR * dp , /* Pointer to directory object */
DWORD ofs /* Offset of directory table */
)
{
DWORD csz , clst ;
FATFS * fs = dp - > obj . fs ;
if ( ofs > = ( DWORD ) ( ( FF_FS_EXFAT & & fs - > fs_type = = FS_EXFAT ) ? MAX_DIR_EX : MAX_DIR ) | | ofs % SZDIRE ) { /* Check range of offset and alignment */
return FR_INT_ERR ;
}
dp - > dptr = ofs ; /* Set current offset */
clst = dp - > obj . sclust ; /* Table start cluster (0:root) */
if ( clst = = 0 & & fs - > fs_type > = FS_FAT32 ) { /* Replace cluster# 0 with root cluster# */
clst = fs - > dirbase ;
if ( FF_FS_EXFAT ) dp - > obj . stat = 0 ; /* exFAT: Root dir has an FAT chain */
}
if ( clst = = 0 ) { /* Static table (root-directory on the FAT volume) */
if ( ofs / SZDIRE > = fs - > n_rootdir ) return FR_INT_ERR ; /* Is index out of range? */
dp - > sect = fs - > dirbase ;
} else { /* Dynamic table (sub-directory or root-directory on the FAT32/exFAT volume) */
csz = ( DWORD ) fs - > csize * SS ( fs ) ; /* Bytes per cluster */
while ( ofs > = csz ) { /* Follow cluster chain */
clst = get_fat ( & dp - > obj , clst ) ; /* Get next cluster */
if ( clst = = 0xFFFFFFFF ) return FR_DISK_ERR ; /* Disk error */
if ( clst < 2 | | clst > = fs - > n_fatent ) return FR_INT_ERR ; /* Reached to end of table or internal error */
ofs - = csz ;
}
dp - > sect = clst2sect ( fs , clst ) ;
}
dp - > clust = clst ; /* Current cluster# */
if ( dp - > sect = = 0 ) return FR_INT_ERR ;
dp - > sect + = ofs / SS ( fs ) ; /* Sector# of the directory entry */
dp - > dir = fs - > win + ( ofs % SS ( fs ) ) ; /* Pointer to the entry in the win[] */
return FR_OK ;
}
/*-----------------------------------------------------------------------*/
/* Directory handling - Move directory table index next */
/*-----------------------------------------------------------------------*/
static
FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */
DIR * dp , /* Pointer to the directory object */
int stretch /* 0: Do not stretch table, 1: Stretch table if needed */
)
{
DWORD ofs , clst ;
FATFS * fs = dp - > obj . fs ;
ofs = dp - > dptr + SZDIRE ; /* Next entry */
if ( dp - > sect = = 0 | | ofs > = ( DWORD ) ( ( FF_FS_EXFAT & & fs - > fs_type = = FS_EXFAT ) ? MAX_DIR_EX : MAX_DIR ) ) return FR_NO_FILE ; /* Report EOT when offset has reached max value */
if ( ofs % SS ( fs ) = = 0 ) { /* Sector changed? */
dp - > sect + + ; /* Next sector */
if ( dp - > clust = = 0 ) { /* Static table */
if ( ofs / SZDIRE > = fs - > n_rootdir ) { /* Report EOT if it reached end of static table */
dp - > sect = 0 ; return FR_NO_FILE ;
}
}
else { /* Dynamic table */
if ( ( ofs / SS ( fs ) & ( fs - > csize - 1 ) ) = = 0 ) { /* Cluster changed? */
clst = get_fat ( & dp - > obj , dp - > clust ) ; /* Get next cluster */
if ( clst < = 1 ) return FR_INT_ERR ; /* Internal error */
if ( clst = = 0xFFFFFFFF ) return FR_DISK_ERR ; /* Disk error */
if ( clst > = fs - > n_fatent ) { /* It reached end of dynamic table */
# if !FF_FS_READONLY
if ( ! stretch ) { /* If no stretch, report EOT */
dp - > sect = 0 ; return FR_NO_FILE ;
}
clst = create_chain ( & dp - > obj , dp - > clust ) ; /* Allocate a cluster */
if ( clst = = 0 ) return FR_DENIED ; /* No free cluster */
if ( clst = = 1 ) return FR_INT_ERR ; /* Internal error */
if ( clst = = 0xFFFFFFFF ) return FR_DISK_ERR ; /* Disk error */
if ( dir_clear ( fs , clst ) ! = FR_OK ) return FR_DISK_ERR ; /* Clean up the stretched table */
if ( FF_FS_EXFAT ) dp - > obj . stat | = 4 ; /* exFAT: The directory has been stretched */
# else
if ( ! stretch ) dp - > sect = 0 ; /* (this line is to suppress compiler warning) */
dp - > sect = 0 ; return FR_NO_FILE ; /* Report EOT */
# endif
}
dp - > clust = clst ; /* Initialize data for new cluster */
dp - > sect = clst2sect ( fs , clst ) ;
}
}
}
dp - > dptr = ofs ; /* Current entry */
dp - > dir = fs - > win + ofs % SS ( fs ) ; /* Pointer to the entry in the win[] */
return FR_OK ;
}
# if !FF_FS_READONLY
/*-----------------------------------------------------------------------*/
/* Directory handling - Reserve a block of directory entries */
/*-----------------------------------------------------------------------*/
static
FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */
DIR * dp , /* Pointer to the directory object */
UINT nent /* Number of contiguous entries to allocate */
)
{
FRESULT res ;
UINT n ;
FATFS * fs = dp - > obj . fs ;
res = dir_sdi ( dp , 0 ) ;
if ( res = = FR_OK ) {
n = 0 ;
do {
res = move_window ( fs , dp - > sect ) ;
if ( res ! = FR_OK ) break ;
# if FF_FS_EXFAT
if ( ( fs - > fs_type = = FS_EXFAT ) ? ( int ) ( ( dp - > dir [ XDIR_Type ] & 0x80 ) = = 0 ) : ( int ) ( dp - > dir [ DIR_Name ] = = DDEM | | dp - > dir [ DIR_Name ] = = 0 ) ) {
# else
if ( dp - > dir [ DIR_Name ] = = DDEM | | dp - > dir [ DIR_Name ] = = 0 ) {
# endif
if ( + + n = = nent ) break ; /* A block of contiguous free entries is found */
} else {
n = 0 ; /* Not a blank entry. Restart to search */
}
res = dir_next ( dp , 1 ) ;
} while ( res = = FR_OK ) ; /* Next entry with table stretch enabled */
}
if ( res = = FR_NO_FILE ) res = FR_DENIED ; /* No directory entry to allocate */
return res ;
}
# endif /* !FF_FS_READONLY */
/*-----------------------------------------------------------------------*/
/* FAT: Directory handling - Load/Store start cluster number */
/*-----------------------------------------------------------------------*/
static
DWORD ld_clust ( /* Returns the top cluster value of the SFN entry */
FATFS * fs , /* Pointer to the fs object */
const BYTE * dir /* Pointer to the key entry */
)
{
DWORD cl ;
cl = ld_word ( dir + DIR_FstClusLO ) ;
if ( fs - > fs_type = = FS_FAT32 ) {
cl | = ( DWORD ) ld_word ( dir + DIR_FstClusHI ) < < 16 ;
}
return cl ;
}
# if !FF_FS_READONLY
static
void st_clust (
FATFS * fs , /* Pointer to the fs object */
BYTE * dir , /* Pointer to the key entry */
DWORD cl /* Value to be set */
)
{
st_word ( dir + DIR_FstClusLO , ( WORD ) cl ) ;
if ( fs - > fs_type = = FS_FAT32 ) {
st_word ( dir + DIR_FstClusHI , ( WORD ) ( cl > > 16 ) ) ;
}
}
# endif
# if FF_USE_LFN
/*--------------------------------------------------------*/
/* FAT-LFN: Compare a part of file name with an LFN entry */
/*--------------------------------------------------------*/
static
int cmp_lfn ( /* 1:matched, 0:not matched */
const WCHAR * lfnbuf , /* Pointer to the LFN working buffer to be compared */
BYTE * dir /* Pointer to the directory entry containing the part of LFN */
)
{
UINT i , s ;
WCHAR wc , uc ;
if ( ld_word ( dir + LDIR_FstClusLO ) ! = 0 ) return 0 ; /* Check LDIR_FstClusLO */
i = ( ( dir [ LDIR_Ord ] & 0x3F ) - 1 ) * 13 ; /* Offset in the LFN buffer */
for ( wc = 1 , s = 0 ; s < 13 ; s + + ) { /* Process all characters in the entry */
uc = ld_word ( dir + LfnOfs [ s ] ) ; /* Pick an LFN character */
if ( wc ) {
if ( i > = FF_MAX_LFN | | ff_wtoupper ( uc ) ! = ff_wtoupper ( lfnbuf [ i + + ] ) ) { /* Compare it */
return 0 ; /* Not matched */
}
wc = uc ;
} else {
if ( uc ! = 0xFFFF ) return 0 ; /* Check filler */
}
}
if ( ( dir [ LDIR_Ord ] & LLEF ) & & wc & & lfnbuf [ i ] ) return 0 ; /* Last segment matched but different length */
return 1 ; /* The part of LFN matched */
}
# if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
/*-----------------------------------------------------*/
/* FAT-LFN: Pick a part of file name from an LFN entry */
/*-----------------------------------------------------*/
static
int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */
WCHAR * lfnbuf , /* Pointer to the LFN working buffer */
BYTE * dir /* Pointer to the LFN entry */
)
{
UINT i , s ;
WCHAR wc , uc ;
if ( ld_word ( dir + LDIR_FstClusLO ) ! = 0 ) return 0 ; /* Check LDIR_FstClusLO is 0 */
i = ( ( dir [ LDIR_Ord ] & ~ LLEF ) - 1 ) * 13 ; /* Offset in the LFN buffer */
for ( wc = 1 , s = 0 ; s < 13 ; s + + ) { /* Process all characters in the entry */
uc = ld_word ( dir + LfnOfs [ s ] ) ; /* Pick an LFN character */
if ( wc ) {
if ( i > = FF_MAX_LFN ) return 0 ; /* Buffer overflow? */
lfnbuf [ i + + ] = wc = uc ; /* Store it */
} else {
if ( uc ! = 0xFFFF ) return 0 ; /* Check filler */
}
}
if ( dir [ LDIR_Ord ] & LLEF ) { /* Put terminator if it is the last LFN part */
if ( i > = FF_MAX_LFN ) return 0 ; /* Buffer overflow? */
lfnbuf [ i ] = 0 ;
}
return 1 ; /* The part of LFN is valid */
}
# endif
# if !FF_FS_READONLY
/*-----------------------------------------*/
/* FAT-LFN: Create an entry of LFN entries */
/*-----------------------------------------*/
static
void put_lfn (
const WCHAR * lfn , /* Pointer to the LFN */
BYTE * dir , /* Pointer to the LFN entry to be created */
BYTE ord , /* LFN order (1-20) */
BYTE sum /* Checksum of the corresponding SFN */
)
{
UINT i , s ;
WCHAR wc ;
dir [ LDIR_Chksum ] = sum ; /* Set checksum */
dir [ LDIR_Attr ] = AM_LFN ; /* Set attribute. LFN entry */
dir [ LDIR_Type ] = 0 ;
st_word ( dir + LDIR_FstClusLO , 0 ) ;
i = ( ord - 1 ) * 13 ; /* Get offset in the LFN working buffer */
s = wc = 0 ;
do {
if ( wc ! = 0xFFFF ) wc = lfn [ i + + ] ; /* Get an effective character */
st_word ( dir + LfnOfs [ s ] , wc ) ; /* Put it */
if ( wc = = 0 ) wc = 0xFFFF ; /* Padding characters for left locations */
} while ( + + s < 13 ) ;
if ( wc = = 0xFFFF | | ! lfn [ i ] ) ord | = LLEF ; /* Last LFN part is the start of LFN sequence */
dir [ LDIR_Ord ] = ord ; /* Set the LFN order */
}
# endif /* !FF_FS_READONLY */
# endif /* FF_USE_LFN */
# if FF_USE_LFN && !FF_FS_READONLY
/*-----------------------------------------------------------------------*/
/* FAT-LFN: Create a Numbered SFN */
/*-----------------------------------------------------------------------*/
static
void gen_numname (
BYTE * dst , /* Pointer to the buffer to store numbered SFN */
const BYTE * src , /* Pointer to SFN */
const WCHAR * lfn , /* Pointer to LFN */
UINT seq /* Sequence number */
)
{
BYTE ns [ 8 ] , c ;
UINT i , j ;
WCHAR wc ;
DWORD sr ;
mem_cpy ( dst , src , 11 ) ;
if ( seq > 5 ) { /* In case of many collisions, generate a hash number instead of sequential number */
sr = seq ;
while ( * lfn ) { /* Create a CRC */
wc = * lfn + + ;
for ( i = 0 ; i < 16 ; i + + ) {
sr = ( sr < < 1 ) + ( wc & 1 ) ;
wc > > = 1 ;
if ( sr & 0x10000 ) sr ^ = 0x11021 ;
}
}
seq = ( UINT ) sr ;
}
/* itoa (hexdecimal) */
i = 7 ;
do {
c = ( BYTE ) ( ( seq % 16 ) + ' 0 ' ) ;
if ( c > ' 9 ' ) c + = 7 ;
ns [ i - - ] = c ;
seq / = 16 ;
} while ( seq ) ;
ns [ i ] = ' ~ ' ;
/* Append the number to the SFN body */
for ( j = 0 ; j < i & & dst [ j ] ! = ' ' ; j + + ) {
if ( dbc_1st ( dst [ j ] ) ) {
if ( j = = i - 1 ) break ;
j + + ;
}
}
do {
dst [ j + + ] = ( i < 8 ) ? ns [ i + + ] : ' ' ;
} while ( j < 8 ) ;
}
# endif /* FF_USE_LFN && !FF_FS_READONLY */
# if FF_USE_LFN
/*-----------------------------------------------------------------------*/
/* FAT-LFN: Calculate checksum of an SFN entry */
/*-----------------------------------------------------------------------*/
static
BYTE sum_sfn (
const BYTE * dir /* Pointer to the SFN entry */
)
{
BYTE sum = 0 ;
UINT n = 11 ;
do {
sum = ( sum > > 1 ) + ( sum < < 7 ) + * dir + + ;
} while ( - - n ) ;
return sum ;
}
# endif /* FF_USE_LFN */
# if FF_FS_EXFAT
/*-----------------------------------------------------------------------*/
/* exFAT: Checksum */
/*-----------------------------------------------------------------------*/
static
WORD xdir_sum ( /* Get checksum of the directoly entry block */
const BYTE * dir /* Directory entry block to be calculated */
)
{
UINT i , szblk ;
WORD sum ;
szblk = ( dir [ XDIR_NumSec ] + 1 ) * SZDIRE ;
for ( i = sum = 0 ; i < szblk ; i + + ) {
if ( i = = XDIR_SetSum ) { /* Skip sum field */
i + + ;
} else {
sum = ( ( sum & 1 ) ? 0x8000 : 0 ) + ( sum > > 1 ) + dir [ i ] ;
}
}
return sum ;
}
static
WORD xname_sum ( /* Get check sum (to be used as hash) of the name */
const WCHAR * name /* File name to be calculated */
)
{
WCHAR chr ;
WORD sum = 0 ;
while ( ( chr = * name + + ) ! = 0 ) {
chr = ff_wtoupper ( chr ) ; /* File name needs to be upper-case converted */
sum = ( ( sum & 1 ) ? 0x8000 : 0 ) + ( sum > > 1 ) + ( chr & 0xFF ) ;
sum = ( ( sum & 1 ) ? 0x8000 : 0 ) + ( sum > > 1 ) + ( chr > > 8 ) ;
}
return sum ;
}
# if !FF_FS_READONLY && FF_USE_MKFS
static
DWORD xsum32 (
BYTE dat , /* Byte to be calculated */
DWORD sum /* Previous sum */
)
{
sum = ( ( sum & 1 ) ? 0x80000000 : 0 ) + ( sum > > 1 ) + dat ;
return sum ;
}
# endif
# if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2
/*------------------------------------------------------*/
/* exFAT: Get object information from a directory block */
/*------------------------------------------------------*/
static
void get_xdir_info (
BYTE * dirb , /* Pointer to the direcotry entry block 85+C0+C1s */
FILINFO * fno /* Buffer to store the extracted file information */
)
{
WCHAR w ;
UINT di , si , nc ;
/* Get file name */
for ( si = SZDIRE * 2 , nc = di = 0 ; nc < dirb [ XDIR_NumName ] ; si + = 2 , nc + + ) {
if ( ( si % SZDIRE ) = = 0 ) si + = 2 ; /* Skip entry type field */
w = ld_word ( dirb + si ) ; /* Get a character */
# if !FF_LFN_UNICODE /* ANSI/OEM API */
w = ff_uni2oem ( w , CODEPAGE ) ; /* Convert it to OEM code */
if ( w > = 0x100 ) { /* Is it a double byte char? */
fno - > fname [ di + + ] = ( char ) ( w > > 8 ) ; /* Store 1st byte of the DBC */
}
# endif
if ( w = = 0 | | di > = FF_MAX_LFN ) { di = 0 ; break ; } /* Invalid char or buffer overflow --> inaccessible object name */
fno - > fname [ di + + ] = ( TCHAR ) w ; /* Store the character */
}
if ( di = = 0 ) fno - > fname [ di + + ] = ' ? ' ; /* Inaccessible object name? */
fno - > fname [ di ] = 0 ; /* Terminate file name */
fno - > altname [ 0 ] = 0 ; /* No SFN */
fno - > fattrib = dirb [ XDIR_Attr ] ; /* Attribute */
fno - > fsize = ( fno - > fattrib & AM_DIR ) ? 0 : ld_qword ( dirb + XDIR_FileSize ) ; /* Size */
fno - > ftime = ld_word ( dirb + XDIR_ModTime + 0 ) ; /* Time */
fno - > fdate = ld_word ( dirb + XDIR_ModTime + 2 ) ; /* Date */
}
# endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */
/*-----------------------------------*/
/* exFAT: Get a directry entry block */
/*-----------------------------------*/
static
FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */
DIR * dp /* Reading direcotry object pointing top of the entry block to load */
)
{
FRESULT res ;
UINT i , sz_ent ;
BYTE * dirb = dp - > obj . fs - > dirbuf ; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */
/* Load 85 entry */
res = move_window ( dp - > obj . fs , dp - > sect ) ;
if ( res ! = FR_OK ) return res ;
if ( dp - > dir [ XDIR_Type ] ! = 0x85 ) return FR_INT_ERR ; /* Invalid order */
mem_cpy ( dirb + 0 * SZDIRE , dp - > dir , SZDIRE ) ;
sz_ent = ( dirb [ XDIR_NumSec ] + 1 ) * SZDIRE ;
if ( sz_ent < 3 * SZDIRE | | sz_ent > 19 * SZDIRE ) return FR_INT_ERR ;
/* Load C0 entry */
res = dir_next ( dp , 0 ) ;
if ( res = = FR_NO_FILE ) res = FR_INT_ERR ; /* It cannot be */
if ( res ! = FR_OK ) return res ;
res = move_window ( dp - > obj . fs , dp - > sect ) ;
if ( res ! = FR_OK ) return res ;
if ( dp - > dir [ XDIR_Type ] ! = 0xC0 ) return FR_INT_ERR ; /* Invalid order */
mem_cpy ( dirb + 1 * SZDIRE , dp - > dir , SZDIRE ) ;
if ( MAXDIRB ( dirb [ XDIR_NumName ] ) > sz_ent ) return FR_INT_ERR ;
/* Load C1 entries */
i = 2 * SZDIRE ; /* C1 offset to load */
do {
res = dir_next ( dp , 0 ) ;
if ( res = = FR_NO_FILE ) res = FR_INT_ERR ; /* It cannot be */
if ( res ! = FR_OK ) return res ;
res = move_window ( dp - > obj . fs , dp - > sect ) ;
if ( res ! = FR_OK ) return res ;
if ( dp - > dir [ XDIR_Type ] ! = 0xC1 ) return FR_INT_ERR ; /* Invalid order */
if ( i < MAXDIRB ( FF_MAX_LFN ) ) mem_cpy ( dirb + i , dp - > dir , SZDIRE ) ;
} while ( ( i + = SZDIRE ) < sz_ent ) ;
/* Sanity check (do it for only accessible object) */
if ( i < = MAXDIRB ( FF_MAX_LFN ) ) {
if ( xdir_sum ( dirb ) ! = ld_word ( dirb + XDIR_SetSum ) ) return FR_INT_ERR ;
}
return FR_OK ;
}
# if !FF_FS_READONLY || FF_FS_RPATH != 0
/*------------------------------------------------*/
/* exFAT: Load the object's directory entry block */
/*------------------------------------------------*/
static
FRESULT load_obj_xdir (
DIR * dp , /* Blank directory object to be used to access containing direcotry */
const FFOBJID * obj /* Object with its containing directory information */
)
{
FRESULT res ;
/* Open object containing directory */
dp - > obj . fs = obj - > fs ;
dp - > obj . sclust = obj - > c_scl ;
dp - > obj . stat = ( BYTE ) obj - > c_size ;
dp - > obj . objsize = obj - > c_size & 0xFFFFFF00 ;
dp - > obj . n_frag = 0 ;
dp - > blk_ofs = obj - > c_ofs ;
res = dir_sdi ( dp , dp - > blk_ofs ) ; /* Goto object's entry block */
if ( res = = FR_OK ) {
res = load_xdir ( dp ) ; /* Load the object's entry block */
}
return res ;
}
# endif
# if !FF_FS_READONLY
/*----------------------------------------*/
/* exFAT: Store the directory entry block */
/*----------------------------------------*/
static
FRESULT store_xdir (
DIR * dp /* Pointer to the direcotry object */
)
{
FRESULT res ;
UINT nent ;
BYTE * dirb = dp - > obj . fs - > dirbuf ; /* Pointer to the direcotry entry block 85+C0+C1s */
/* Create set sum */
st_word ( dirb + XDIR_SetSum , xdir_sum ( dirb ) ) ;
nent = dirb [ XDIR_NumSec ] + 1 ;
/* Store the direcotry entry block to the directory */
res = dir_sdi ( dp , dp - > blk_ofs ) ;
while ( res = = FR_OK ) {
res = move_window ( dp - > obj . fs , dp - > sect ) ;
if ( res ! = FR_OK ) break ;
mem_cpy ( dp - > dir , dirb , SZDIRE ) ;
dp - > obj . fs - > wflag = 1 ;
if ( - - nent = = 0 ) break ;
dirb + = SZDIRE ;
res = dir_next ( dp , 0 ) ;
}
return ( res = = FR_OK | | res = = FR_DISK_ERR ) ? res : FR_INT_ERR ;
}
/*-------------------------------------------*/
/* exFAT: Create a new directory enrty block */
/*-------------------------------------------*/
static
void create_xdir (
BYTE * dirb , /* Pointer to the direcotry entry block buffer */
const WCHAR * lfn /* Pointer to the object name */
)
{
UINT i ;
BYTE nc1 , nlen ;
WCHAR chr ;
/* Create 85+C0 entry */
mem_set ( dirb , 0 , 2 * SZDIRE ) ;
dirb [ 0 * SZDIRE + XDIR_Type ] = 0x85 ;
dirb [ 1 * SZDIRE + XDIR_Type ] = 0xC0 ;
/* Create C1 entries */
nlen = nc1 = 0 ; chr = 1 ; i = SZDIRE * 2 ;
do {
dirb [ i + + ] = 0xC1 ; dirb [ i + + ] = 0 ; /* Entry type C1 */
do { /* Fill name field */
if ( chr & & ( chr = lfn [ nlen ] ) ! = 0 ) nlen + + ; /* Get a character if exist */
st_word ( dirb + i , chr ) ; /* Store it */
i + = 2 ;
} while ( i % SZDIRE ! = 0 ) ;
nc1 + + ;
} while ( lfn [ nlen ] ) ; /* Fill next entry if any char follows */
dirb [ XDIR_NumName ] = nlen ; /* Set name length */
dirb [ XDIR_NumSec ] = 1 + nc1 ; /* Set secondary count */
st_word ( dirb + XDIR_NameHash , xname_sum ( lfn ) ) ; /* Set name hash */
}
# endif /* !FF_FS_READONLY */
# endif /* FF_FS_EXFAT */
# if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
/*-----------------------------------------------------------------------*/
/* Read an object from the directory */
/*-----------------------------------------------------------------------*/
static
FRESULT dir_read (
DIR * dp , /* Pointer to the directory object */
int vol /* Filtered by 0:file/directory or 1:volume label */
)
{
FRESULT res = FR_NO_FILE ;
FATFS * fs = dp - > obj . fs ;
BYTE a , c ;
# if FF_USE_LFN
BYTE ord = 0xFF , sum = 0xFF ;
# endif
while ( dp - > sect ) {
res = move_window ( fs , dp - > sect ) ;
if ( res ! = FR_OK ) break ;
c = dp - > dir [ DIR_Name ] ; /* Test for the entry type */
if ( c = = 0 ) {
res = FR_NO_FILE ; break ; /* Reached to end of the directory */
}
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) { /* On the exFAT volume */
if ( FF_USE_LABEL & & vol ) {
if ( c = = 0x83 ) break ; /* Volume label entry? */
} else {
if ( c = = 0x85 ) { /* Start of the file entry block? */
dp - > blk_ofs = dp - > dptr ; /* Get location of the block */
res = load_xdir ( dp ) ; /* Load the entry block */
if ( res = = FR_OK ) {
dp - > obj . attr = fs - > dirbuf [ XDIR_Attr ] & AM_MASK ; /* Get attribute */
}
break ;
}
}
} else
# endif
{ /* On the FAT/FAT32 volume */
dp - > obj . attr = a = dp - > dir [ DIR_Attr ] & AM_MASK ; /* Get attribute */
# if FF_USE_LFN /* LFN configuration */
if ( c = = DDEM | | c = = ' . ' | | ( int ) ( ( a & ~ AM_ARC ) = = AM_VOL ) ! = vol ) { /* An entry without valid data */
ord = 0xFF ;
} else {
if ( a = = AM_LFN ) { /* An LFN entry is found */
if ( c & LLEF ) { /* Is it start of an LFN sequence? */
sum = dp - > dir [ LDIR_Chksum ] ;
c & = ( BYTE ) ~ LLEF ; ord = c ;
dp - > blk_ofs = dp - > dptr ;
}
/* Check LFN validity and capture it */
ord = ( c = = ord & & sum = = dp - > dir [ LDIR_Chksum ] & & pick_lfn ( fs - > lfnbuf , dp - > dir ) ) ? ord - 1 : 0xFF ;
} else { /* An SFN entry is found */
if ( ord | | sum ! = sum_sfn ( dp - > dir ) ) { /* Is there a valid LFN? */
dp - > blk_ofs = 0xFFFFFFFF ; /* It has no LFN. */
}
break ;
}
}
# else /* Non LFN configuration */
if ( c ! = DDEM & & c ! = ' . ' & & a ! = AM_LFN & & ( int ) ( ( a & ~ AM_ARC ) = = AM_VOL ) = = vol ) { /* Is it a valid entry? */
break ;
}
# endif
}
res = dir_next ( dp , 0 ) ; /* Next entry */
if ( res ! = FR_OK ) break ;
}
if ( res ! = FR_OK ) dp - > sect = 0 ; /* Terminate the read operation on error or EOT */
return res ;
}
# endif /* FF_FS_MINIMIZE <= 1 || FF_USE_LABEL || FF_FS_RPATH >= 2 */
/*-----------------------------------------------------------------------*/
/* Directory handling - Find an object in the directory */
/*-----------------------------------------------------------------------*/
static
FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */
DIR * dp /* Pointer to the directory object with the file name */
)
{
FRESULT res ;
FATFS * fs = dp - > obj . fs ;
BYTE c ;
# if FF_USE_LFN
BYTE a , ord , sum ;
# endif
res = dir_sdi ( dp , 0 ) ; /* Rewind directory object */
if ( res ! = FR_OK ) return res ;
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) { /* On the exFAT volume */
BYTE nc ;
UINT di , ni ;
WORD hash = xname_sum ( fs - > lfnbuf ) ; /* Hash value of the name to find */
while ( ( res = dir_read ( dp , 0 ) ) = = FR_OK ) { /* Read an item */
# if FF_MAX_LFN < 255
if ( fs - > dirbuf [ XDIR_NumName ] > FF_MAX_LFN ) continue ; /* Skip comparison if inaccessible object name */
# endif
if ( ld_word ( fs - > dirbuf + XDIR_NameHash ) ! = hash ) continue ; /* Skip comparison if hash mismatched */
for ( nc = fs - > dirbuf [ XDIR_NumName ] , di = SZDIRE * 2 , ni = 0 ; nc ; nc - - , di + = 2 , ni + + ) { /* Compare the name */
if ( ( di % SZDIRE ) = = 0 ) di + = 2 ;
if ( ff_wtoupper ( ld_word ( fs - > dirbuf + di ) ) ! = ff_wtoupper ( fs - > lfnbuf [ ni ] ) ) break ;
}
if ( nc = = 0 & & ! fs - > lfnbuf [ ni ] ) break ; /* Name matched? */
}
return res ;
}
# endif
/* On the FAT/FAT32 volume */
# if FF_USE_LFN
ord = sum = 0xFF ; dp - > blk_ofs = 0xFFFFFFFF ; /* Reset LFN sequence */
# endif
do {
res = move_window ( fs , dp - > sect ) ;
if ( res ! = FR_OK ) break ;
c = dp - > dir [ DIR_Name ] ;
if ( c = = 0 ) { res = FR_NO_FILE ; break ; } /* Reached to end of table */
# if FF_USE_LFN /* LFN configuration */
dp - > obj . attr = a = dp - > dir [ DIR_Attr ] & AM_MASK ;
if ( c = = DDEM | | ( ( a & AM_VOL ) & & a ! = AM_LFN ) ) { /* An entry without valid data */
ord = 0xFF ; dp - > blk_ofs = 0xFFFFFFFF ; /* Reset LFN sequence */
} else {
if ( a = = AM_LFN ) { /* An LFN entry is found */
if ( ! ( dp - > fn [ NSFLAG ] & NS_NOLFN ) ) {
if ( c & LLEF ) { /* Is it start of LFN sequence? */
sum = dp - > dir [ LDIR_Chksum ] ;
c & = ( BYTE ) ~ LLEF ; ord = c ; /* LFN start order */
dp - > blk_ofs = dp - > dptr ; /* Start offset of LFN */
}
/* Check validity of the LFN entry and compare it with given name */
ord = ( c = = ord & & sum = = dp - > dir [ LDIR_Chksum ] & & cmp_lfn ( fs - > lfnbuf , dp - > dir ) ) ? ord - 1 : 0xFF ;
}
} else { /* An SFN entry is found */
if ( ! ord & & sum = = sum_sfn ( dp - > dir ) ) break ; /* LFN matched? */
if ( ! ( dp - > fn [ NSFLAG ] & NS_LOSS ) & & ! mem_cmp ( dp - > dir , dp - > fn , 11 ) ) break ; /* SFN matched? */
ord = 0xFF ; dp - > blk_ofs = 0xFFFFFFFF ; /* Reset LFN sequence */
}
}
# else /* Non LFN configuration */
dp - > obj . attr = dp - > dir [ DIR_Attr ] & AM_MASK ;
if ( ! ( dp - > dir [ DIR_Attr ] & AM_VOL ) & & ! mem_cmp ( dp - > dir , dp - > fn , 11 ) ) break ; /* Is it a valid entry? */
# endif
res = dir_next ( dp , 0 ) ; /* Next entry */
} while ( res = = FR_OK ) ;
return res ;
}
# if !FF_FS_READONLY
/*-----------------------------------------------------------------------*/
/* Register an object to the directory */
/*-----------------------------------------------------------------------*/
static
FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */
DIR * dp /* Target directory with object name to be created */
)
{
FRESULT res ;
FATFS * fs = dp - > obj . fs ;
# if FF_USE_LFN /* LFN configuration */
UINT n , nlen , nent ;
BYTE sn [ 12 ] , sum ;
if ( dp - > fn [ NSFLAG ] & ( NS_DOT | NS_NONAME ) ) return FR_INVALID_NAME ; /* Check name validity */
for ( nlen = 0 ; fs - > lfnbuf [ nlen ] ; nlen + + ) ; /* Get lfn length */
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) { /* On the exFAT volume */
nent = ( nlen + 14 ) / 15 + 2 ; /* Number of entries to allocate (85+C0+C1s) */
res = dir_alloc ( dp , nent ) ; /* Allocate entries */
if ( res ! = FR_OK ) return res ;
dp - > blk_ofs = dp - > dptr - SZDIRE * ( nent - 1 ) ; /* Set the allocated entry block offset */
if ( dp - > obj . stat & 4 ) { /* Has the directory been stretched? */
dp - > obj . stat & = ~ 4 ;
res = fill_first_frag ( & dp - > obj ) ; /* Fill the first fragment on the FAT if needed */
if ( res ! = FR_OK ) return res ;
res = fill_last_frag ( & dp - > obj , dp - > clust , 0xFFFFFFFF ) ; /* Fill the last fragment on the FAT if needed */
if ( res ! = FR_OK ) return res ;
if ( dp - > obj . sclust ! = 0 ) { /* Is it a sub directory? */
DIR dj ;
res = load_obj_xdir ( & dj , & dp - > obj ) ; /* Load the object status */
if ( res ! = FR_OK ) return res ;
dp - > obj . objsize + = ( DWORD ) fs - > csize * SS ( fs ) ; /* Increase the directory size by cluster size */
st_qword ( fs - > dirbuf + XDIR_FileSize , dp - > obj . objsize ) ; /* Update the allocation status */
st_qword ( fs - > dirbuf + XDIR_ValidFileSize , dp - > obj . objsize ) ;
fs - > dirbuf [ XDIR_GenFlags ] = dp - > obj . stat | 1 ;
res = store_xdir ( & dj ) ; /* Store the object status */
if ( res ! = FR_OK ) return res ;
}
}
create_xdir ( fs - > dirbuf , fs - > lfnbuf ) ; /* Create on-memory directory block to be written later */
return FR_OK ;
}
# endif
/* On the FAT/FAT32 volume */
mem_cpy ( sn , dp - > fn , 12 ) ;
if ( sn [ NSFLAG ] & NS_LOSS ) { /* When LFN is out of 8.3 format, generate a numbered name */
dp - > fn [ NSFLAG ] = NS_NOLFN ; /* Find only SFN */
for ( n = 1 ; n < 100 ; n + + ) {
gen_numname ( dp - > fn , sn , fs - > lfnbuf , n ) ; /* Generate a numbered name */
res = dir_find ( dp ) ; /* Check if the name collides with existing SFN */
if ( res ! = FR_OK ) break ;
}
if ( n = = 100 ) return FR_DENIED ; /* Abort if too many collisions */
if ( res ! = FR_NO_FILE ) return res ; /* Abort if the result is other than 'not collided' */
dp - > fn [ NSFLAG ] = sn [ NSFLAG ] ;
}
/* Create an SFN with/without LFNs. */
nent = ( sn [ NSFLAG ] & NS_LFN ) ? ( nlen + 12 ) / 13 + 1 : 1 ; /* Number of entries to allocate */
res = dir_alloc ( dp , nent ) ; /* Allocate entries */
if ( res = = FR_OK & & - - nent ) { /* Set LFN entry if needed */
res = dir_sdi ( dp , dp - > dptr - nent * SZDIRE ) ;
if ( res = = FR_OK ) {
sum = sum_sfn ( dp - > fn ) ; /* Checksum value of the SFN tied to the LFN */
do { /* Store LFN entries in bottom first */
res = move_window ( fs , dp - > sect ) ;
if ( res ! = FR_OK ) break ;
put_lfn ( fs - > lfnbuf , dp - > dir , ( BYTE ) nent , sum ) ;
fs - > wflag = 1 ;
res = dir_next ( dp , 0 ) ; /* Next entry */
} while ( res = = FR_OK & & - - nent ) ;
}
}
# else /* Non LFN configuration */
res = dir_alloc ( dp , 1 ) ; /* Allocate an entry for SFN */
# endif
/* Set SFN entry */
if ( res = = FR_OK ) {
res = move_window ( fs , dp - > sect ) ;
if ( res = = FR_OK ) {
mem_set ( dp - > dir , 0 , SZDIRE ) ; /* Clean the entry */
mem_cpy ( dp - > dir + DIR_Name , dp - > fn , 11 ) ; /* Put SFN */
# if FF_USE_LFN
dp - > dir [ DIR_NTres ] = dp - > fn [ NSFLAG ] & ( NS_BODY | NS_EXT ) ; /* Put NT flag */
# endif
fs - > wflag = 1 ;
}
}
return res ;
}
# endif /* !FF_FS_READONLY */
# if !FF_FS_READONLY && FF_FS_MINIMIZE == 0
/*-----------------------------------------------------------------------*/
/* Remove an object from the directory */
/*-----------------------------------------------------------------------*/
static
FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */
DIR * dp /* Directory object pointing the entry to be removed */
)
{
FRESULT res ;
FATFS * fs = dp - > obj . fs ;
# if FF_USE_LFN /* LFN configuration */
DWORD last = dp - > dptr ;
res = ( dp - > blk_ofs = = 0xFFFFFFFF ) ? FR_OK : dir_sdi ( dp , dp - > blk_ofs ) ; /* Goto top of the entry block if LFN is exist */
if ( res = = FR_OK ) {
do {
res = move_window ( fs , dp - > sect ) ;
if ( res ! = FR_OK ) break ;
/* Mark an entry 'deleted' */
if ( FF_FS_EXFAT & & fs - > fs_type = = FS_EXFAT ) { /* On the exFAT volume */
dp - > dir [ XDIR_Type ] & = 0x7F ;
} else { /* On the FAT/FAT32 volume */
dp - > dir [ DIR_Name ] = DDEM ;
}
fs - > wflag = 1 ;
if ( dp - > dptr > = last ) break ; /* If reached last entry then all entries of the object has been deleted. */
res = dir_next ( dp , 0 ) ; /* Next entry */
} while ( res = = FR_OK ) ;
if ( res = = FR_NO_FILE ) res = FR_INT_ERR ;
}
# else /* Non LFN configuration */
res = move_window ( fs , dp - > sect ) ;
if ( res = = FR_OK ) {
dp - > dir [ DIR_Name ] = DDEM ;
fs - > wflag = 1 ;
}
# endif
return res ;
}
# endif /* !FF_FS_READONLY && FF_FS_MINIMIZE == 0 */
# if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2
/*-----------------------------------------------------------------------*/
/* Get file information from directory entry */
/*-----------------------------------------------------------------------*/
static
void get_fileinfo ( /* No return code */
DIR * dp , /* Pointer to the directory object */
FILINFO * fno /* Pointer to the file information to be filled */
)
{
UINT i , j ;
TCHAR c ;
DWORD tm ;
# if FF_USE_LFN
WCHAR w , lfv ;
FATFS * fs = dp - > obj . fs ;
# endif
fno - > fname [ 0 ] = 0 ; /* Invaidate file info */
if ( dp - > sect = = 0 ) return ; /* Exit if read pointer has reached end of directory */
# if FF_USE_LFN /* LFN configuration */
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) { /* On the exFAT volume */
get_xdir_info ( fs - > dirbuf , fno ) ;
return ;
} else
# endif
{ /* On the FAT/FAT32 volume */
if ( dp - > blk_ofs ! = 0xFFFFFFFF ) { /* Get LFN if available */
i = j = 0 ;
while ( ( w = fs - > lfnbuf [ j + + ] ) ! = 0 ) { /* Get an LFN character */
# if !FF_LFN_UNICODE /* ANSI/OEM API */
w = ff_uni2oem ( w , CODEPAGE ) ; /* Unicode -> OEM */
if ( w = = 0 ) { i = 0 ; break ; } /* No LFN if it could not be converted */
if ( w > = 0x100 ) { /* Put 1st byte if it is a DBC */
fno - > fname [ i + + ] = ( char ) ( w > > 8 ) ;
}
# endif
if ( i > = FF_MAX_LFN ) { i = 0 ; break ; } /* No LFN if buffer overflow */
fno - > fname [ i + + ] = ( TCHAR ) w ;
}
fno - > fname [ i ] = 0 ; /* Terminate the LFN */
}
}
i = j = 0 ;
lfv = fno - > fname [ i ] ; /* LFN is exist if non-zero */
while ( i < 11 ) { /* Copy name body and extension */
c = ( TCHAR ) dp - > dir [ i + + ] ;
if ( c = = ' ' ) continue ; /* Skip padding spaces */
if ( c = = RDDEM ) c = ( TCHAR ) DDEM ; /* Restore replaced DDEM character */
if ( i = = 9 ) { /* Insert a . if extension is exist */
if ( ! lfv ) fno - > fname [ j ] = ' . ' ;
fno - > altname [ j + + ] = ' . ' ;
}
# if FF_LFN_UNICODE /* Unicode API */
if ( dbc_1st ( ( BYTE ) c ) & & i ! = 8 & & i ! = 11 & & dbc_2nd ( dp - > dir [ i ] ) ) {
c = c < < 8 | dp - > dir [ i + + ] ;
}
c = ff_oem2uni ( c , CODEPAGE ) ; /* OEM -> Unicode */
if ( ! c ) c = ' ? ' ;
# endif
fno - > altname [ j ] = c ;
if ( ! lfv ) {
if ( IsUpper ( c ) & & ( dp - > dir [ DIR_NTres ] & ( ( i > = 9 ) ? NS_EXT : NS_BODY ) ) ) {
c + = 0x20 ; /* To lower */
}
fno - > fname [ j ] = c ;
}
j + + ;
}
if ( ! lfv ) {
fno - > fname [ j ] = 0 ;
if ( ! dp - > dir [ DIR_NTres ] ) j = 0 ; /* Altname is no longer needed if neither LFN nor case info is exist. */
}
fno - > altname [ j ] = 0 ; /* Terminate the SFN */
# else /* Non-LFN configuration */
i = j = 0 ;
while ( i < 11 ) { /* Copy name body and extension */
c = ( TCHAR ) dp - > dir [ i + + ] ;
if ( c = = ' ' ) continue ; /* Skip padding spaces */
if ( c = = RDDEM ) c = ( TCHAR ) DDEM ; /* Restore replaced DDEM character */
if ( i = = 9 ) fno - > fname [ j + + ] = ' . ' ; /* Insert a . if extension is exist */
fno - > fname [ j + + ] = c ;
}
fno - > fname [ j ] = 0 ;
# endif
fno - > fattrib = dp - > dir [ DIR_Attr ] ; /* Attribute */
fno - > fsize = ld_dword ( dp - > dir + DIR_FileSize ) ; /* Size */
tm = ld_dword ( dp - > dir + DIR_ModTime ) ; /* Timestamp */
fno - > ftime = ( WORD ) tm ; fno - > fdate = ( WORD ) ( tm > > 16 ) ;
}
# endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */
# if FF_USE_FIND && FF_FS_MINIMIZE <= 1
/*-----------------------------------------------------------------------*/
/* Pattern matching */
/*-----------------------------------------------------------------------*/
static
WCHAR get_achar ( /* Get a character and advances ptr 1 or 2 */
const TCHAR * * ptr /* Pointer to pointer to the SBCS/DBCS/Unicode string */
)
{
WCHAR chr ;
# if FF_LFN_UNICODE && FF_USE_LFN /* Unicode API */
chr = ff_wtoupper ( * ( * ptr ) + + ) ; /* Get a Unicode char and to upper */
# else /* ANSI/OEM API */
chr = ( BYTE ) * ( * ptr ) + + ; /* Get a byte */
if ( IsLower ( chr ) ) chr - = 0x20 ; /* To upper ASCII char */
# if FF_CODE_PAGE == 0
if ( ExCvt & & chr > = 0x80 ) chr = ExCvt [ chr - 0x80 ] ; /* To upper SBCS extended char */
# elif FF_CODE_PAGE < 900
if ( chr > = 0x80 ) chr = ExCvt [ chr - 0x80 ] ; /* To upper SBCS extended char */
# endif
# if FF_CODE_PAGE == 0 || FF_CODE_PAGE >= 900
if ( dbc_1st ( ( BYTE ) chr ) & & dbc_2nd ( ( BYTE ) * * ptr ) ) { /* Get DBC 2nd byte if needed */
chr = chr < < 8 | ( BYTE ) * ( * ptr ) + + ;
}
# endif
# endif
return chr ;
}
static
int pattern_matching ( /* 0:not matched, 1:matched */
const TCHAR * pat , /* Matching pattern */
const TCHAR * nam , /* String to be tested */
int skip , /* Number of pre-skip chars (number of ?s) */
int inf /* Infinite search (* specified) */
)
{
const TCHAR * pp , * np ;
WCHAR pc , nc ;
int nm , nx ;
while ( skip - - ) { /* Pre-skip name chars */
if ( ! get_achar ( & nam ) ) return 0 ; /* Branch mismatched if less name chars */
}
if ( ! * pat & & inf ) return 1 ; /* (short circuit) */
do {
pp = pat ; np = nam ; /* Top of pattern and name to match */
for ( ; ; ) {
if ( * pp = = ' ? ' | | * pp = = ' * ' ) { /* Wildcard? */
nm = nx = 0 ;
do { /* Analyze the wildcard chars */
if ( * pp + + = = ' ? ' ) nm + + ; else nx = 1 ;
} while ( * pp = = ' ? ' | | * pp = = ' * ' ) ;
if ( pattern_matching ( pp , np , nm , nx ) ) return 1 ; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */
nc = * np ; break ; /* Branch mismatched */
}
pc = get_achar ( & pp ) ; /* Get a pattern char */
nc = get_achar ( & np ) ; /* Get a name char */
if ( pc ! = nc ) break ; /* Branch mismatched? */
if ( pc = = 0 ) return 1 ; /* Branch matched? (matched at end of both strings) */
}
get_achar ( & nam ) ; /* nam++ */
} while ( inf & & nc ) ; /* Retry until end of name if infinite search is specified */
return 0 ;
}
# endif /* FF_USE_FIND && FF_FS_MINIMIZE <= 1 */
/*-----------------------------------------------------------------------*/
/* Pick a top segment and create the object name in directory form */
/*-----------------------------------------------------------------------*/
static
FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */
DIR * dp , /* Pointer to the directory object */
const TCHAR * * path /* Pointer to pointer to the segment in the path string */
)
{
# if FF_USE_LFN /* LFN configuration */
BYTE b , cf ;
WCHAR w , * lfn ;
UINT i , ni , si , di ;
const TCHAR * p ;
/* Create LFN in Unicode */
p = * path ; lfn = dp - > obj . fs - > lfnbuf ; si = di = 0 ;
for ( ; ; ) {
w = p [ si + + ] ; /* Get a character */
if ( w < ' ' ) break ; /* Break if end of the path name */
if ( w = = ' / ' | | w = = ' \\ ' ) { /* Break if a separator is found */
while ( p [ si ] = = ' / ' | | p [ si ] = = ' \\ ' ) si + + ; /* Skip duplicated separator if exist */
break ;
}
if ( di > = FF_MAX_LFN ) return FR_INVALID_NAME ; /* Reject too long name */
# if !FF_LFN_UNICODE /* ANSI/OEM API */
w & = 0xFF ;
if ( dbc_1st ( ( BYTE ) w ) ) { /* Check if it is a DBC 1st byte */
b = ( BYTE ) p [ si + + ] ; /* Get 2nd byte */
w = ( w < < 8 ) + b ; /* Create a DBC */
if ( ! dbc_2nd ( b ) ) return FR_INVALID_NAME ; /* Reject invalid sequence */
}
w = ff_oem2uni ( w , CODEPAGE ) ; /* Convert ANSI/OEM to Unicode */
if ( ! w ) return FR_INVALID_NAME ; /* Reject invalid code */
# endif
if ( w < 0x80 & & chk_chr ( " \" *:<> \ ?| \x7F " , w ) ) return FR_INVALID_NAME ; /* Reject illegal characters for LFN */
lfn [ di + + ] = w ; /* Store the Unicode character */
}
* path = & p [ si ] ; /* Return pointer to the next segment */
cf = ( w < ' ' ) ? NS_LAST : 0 ; /* Set last segment flag if end of the path */
# if FF_FS_RPATH != 0
if ( ( di = = 1 & & lfn [ di - 1 ] = = ' . ' ) | |
( di = = 2 & & lfn [ di - 1 ] = = ' . ' & & lfn [ di - 2 ] = = ' . ' ) ) { /* Is this segment a dot name? */
lfn [ di ] = 0 ;
for ( i = 0 ; i < 11 ; i + + ) /* Create dot name for SFN entry */
dp - > fn [ i ] = ( i < di ) ? ' . ' : ' ' ;
dp - > fn [ i ] = cf | NS_DOT ; /* This is a dot entry */
return FR_OK ;
}
# endif
while ( di ) { /* Snip off trailing spaces and dots if exist */
w = lfn [ di - 1 ] ;
if ( w ! = ' ' & & w ! = ' . ' ) break ;
di - - ;
}
lfn [ di ] = 0 ; /* LFN is created */
if ( di = = 0 ) return FR_INVALID_NAME ; /* Reject nul name */
/* Create SFN in directory form */
mem_set ( dp - > fn , ' ' , 11 ) ;
for ( si = 0 ; lfn [ si ] = = ' ' | | lfn [ si ] = = ' . ' ; si + + ) ; /* Strip leading spaces and dots */
if ( si > 0 ) cf | = NS_LOSS | NS_LFN ;
while ( di > 0 & & lfn [ di - 1 ] ! = ' . ' ) di - - ; /* Find extension (di<=si: no extension) */
i = b = 0 ; ni = 8 ;
for ( ; ; ) {
w = lfn [ si + + ] ; /* Get an LFN character */
if ( ! w ) break ; /* Break on end of the LFN */
if ( w = = ' ' | | ( w = = ' . ' & & si ! = di ) ) { /* Remove spaces and dots */
cf | = NS_LOSS | NS_LFN ;
continue ;
}
if ( i > = ni | | si = = di ) { /* Entered extension or end of SFN */
if ( ni = = 11 ) { /* Extension fileld overflow? */
cf | = NS_LOSS | NS_LFN ;
break ;
}
if ( si ! = di ) cf | = NS_LOSS | NS_LFN ; /* Out of 8.3 format */
if ( si > di ) break ; /* No extension */
si = di ; i = 8 ; ni = 11 ; /* Enter extension fileld */
b < < = 2 ; continue ;
}
if ( w > = 0x80 ) { /* Is this a non-ASCII character? */
cf | = NS_LFN ; /* Force to create LFN entry */
# if FF_CODE_PAGE == 0
if ( ExCvt ) { /* In SBCS */
w = ff_uni2oem ( w , CODEPAGE ) ; /* Unicode -> OEM code */
if ( w & 0x80 ) w = ExCvt [ w & 0x7F ] ; /* Convert extended character to upper (SBCS) */
} else { /* In DBCS */
w = ff_uni2oem ( ff_wtoupper ( w ) , CODEPAGE ) ; /* Upper converted Unicode -> OEM code */
}
# elif FF_CODE_PAGE < 900 /* SBCS cfg */
w = ff_uni2oem ( w , CODEPAGE ) ; /* Unicode -> OEM code */
if ( w & 0x80 ) w = ExCvt [ w & 0x7F ] ; /* Convert extended character to upper (SBCS) */
# else /* DBCS cfg */
w = ff_uni2oem ( ff_wtoupper ( w ) , CODEPAGE ) ; /* Upper converted Unicode -> OEM code */
# endif
}
if ( w > = 0x100 ) { /* Is this a DBC? */
if ( i > = ni - 1 ) { /* Field overflow? */
cf | = NS_LOSS | NS_LFN ;
i = ni ; continue ; /* Next field */
}
dp - > fn [ i + + ] = ( BYTE ) ( w > > 8 ) ; /* Put 1st byte */
} else { /* SBC */
if ( ! w | | chk_chr ( " +,;=[] " , w ) ) { /* Replace illegal characters for SFN */
w = ' _ ' ; cf | = NS_LOSS | NS_LFN ; /* Lossy conversion */
} else {
if ( IsUpper ( w ) ) { /* ASCII large capital */
b | = 2 ;
} else {
if ( IsLower ( w ) ) { /* ASCII small capital */
b | = 1 ; w - = 0x20 ;
}
}
}
}
dp - > fn [ i + + ] = ( BYTE ) w ;
}
if ( dp - > fn [ 0 ] = = DDEM ) dp - > fn [ 0 ] = RDDEM ; /* If the first character collides with DDEM, replace it with RDDEM */
if ( ni = = 8 ) b < < = 2 ;
if ( ( b & 0x0C ) = = 0x0C | | ( b & 0x03 ) = = 0x03 ) cf | = NS_LFN ; /* Create LFN entry when there are composite capitals */
if ( ! ( cf & NS_LFN ) ) { /* When LFN is in 8.3 format without extended character, NT flags are created */
if ( ( b & 0x03 ) = = 0x01 ) cf | = NS_EXT ; /* NT flag (Extension has only small capital) */
if ( ( b & 0x0C ) = = 0x04 ) cf | = NS_BODY ; /* NT flag (Filename has only small capital) */
}
dp - > fn [ NSFLAG ] = cf ; /* SFN is created */
return FR_OK ;
# else /* FF_USE_LFN : Non-LFN configuration */
BYTE c , d , * sfn ;
UINT ni , si , i ;
const char * p ;
/* Create file name in directory form */
p = * path ; sfn = dp - > fn ;
mem_set ( sfn , ' ' , 11 ) ;
si = i = 0 ; ni = 8 ;
# if FF_FS_RPATH != 0
if ( p [ si ] = = ' . ' ) { /* Is this a dot entry? */
for ( ; ; ) {
c = ( BYTE ) p [ si + + ] ;
if ( c ! = ' . ' | | si > = 3 ) break ;
sfn [ i + + ] = c ;
}
if ( c ! = ' / ' & & c ! = ' \\ ' & & c > ' ' ) return FR_INVALID_NAME ;
* path = p + si ; /* Return pointer to the next segment */
sfn [ NSFLAG ] = ( c < = ' ' ) ? NS_LAST | NS_DOT : NS_DOT ; /* Set last segment flag if end of the path */
return FR_OK ;
}
# endif
for ( ; ; ) {
c = ( BYTE ) p [ si + + ] ;
if ( c < = ' ' ) break ; /* Break if end of the path name */
if ( c = = ' / ' | | c = = ' \\ ' ) { /* Break if a separator is found */
while ( p [ si ] = = ' / ' | | p [ si ] = = ' \\ ' ) si + + ; /* Skip duplicated separator if exist */
break ;
}
if ( c = = ' . ' | | i > = ni ) { /* End of body or over size? */
if ( ni = = 11 | | c ! = ' . ' ) return FR_INVALID_NAME ; /* Over size or invalid dot */
i = 8 ; ni = 11 ; /* Goto extension */
continue ;
}
# if FF_CODE_PAGE == 0
if ( ExCvt & & c > = 0x80 ) { /* Is SBC extended character? */
c = ExCvt [ c - 0x80 ] ; /* To upper SBC extended character */
}
# elif FF_CODE_PAGE < 900
if ( c > = 0x80 ) { /* Is SBC extended character? */
c = ExCvt [ c - 0x80 ] ; /* To upper SBC extended character */
}
# endif
if ( dbc_1st ( c ) ) { /* Check if it is a DBC 1st byte */
d = ( BYTE ) p [ si + + ] ; /* Get 2nd byte */
if ( ! dbc_2nd ( d ) | | i > = ni - 1 ) return FR_INVALID_NAME ; /* Reject invalid DBC */
sfn [ i + + ] = c ;
sfn [ i + + ] = d ;
} else { /* SBC */
if ( chk_chr ( " \" *+,:;<=> \ ?[]| \x7F " , c ) ) return FR_INVALID_NAME ; /* Reject illegal chrs for SFN */
if ( IsLower ( c ) ) c - = 0x20 ; /* To upper */
sfn [ i + + ] = c ;
}
}
* path = p + si ; /* Return pointer to the next segment */
if ( i = = 0 ) return FR_INVALID_NAME ; /* Reject nul string */
if ( sfn [ 0 ] = = DDEM ) sfn [ 0 ] = RDDEM ; /* If the first character collides with DDEM, replace it with RDDEM */
sfn [ NSFLAG ] = ( c < = ' ' ) ? NS_LAST : 0 ; /* Set last segment flag if end of the path */
return FR_OK ;
# endif /* FF_USE_LFN */
}
/*-----------------------------------------------------------------------*/
/* Follow a file path */
/*-----------------------------------------------------------------------*/
static
FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */
DIR * dp , /* Directory object to return last directory and found object */
const TCHAR * path /* Full-path string to find a file or directory */
)
{
FRESULT res ;
BYTE ns ;
FATFS * fs = dp - > obj . fs ;
# if FF_FS_RPATH != 0
if ( * path ! = ' / ' & & * path ! = ' \\ ' ) { /* Without heading separator */
dp - > obj . sclust = fs - > cdir ; /* Start from current directory */
} else
# endif
{ /* With heading separator */
while ( * path = = ' / ' | | * path = = ' \\ ' ) path + + ; /* Strip heading separator */
dp - > obj . sclust = 0 ; /* Start from root directory */
}
# if FF_FS_EXFAT
dp - > obj . n_frag = 0 ; /* Invalidate last fragment counter of the object */
# if FF_FS_RPATH != 0
if ( fs - > fs_type = = FS_EXFAT & & dp - > obj . sclust ) { /* exFAT: Retrieve the sub-directory's status */
DIR dj ;
dp - > obj . c_scl = fs - > cdc_scl ;
dp - > obj . c_size = fs - > cdc_size ;
dp - > obj . c_ofs = fs - > cdc_ofs ;
res = load_obj_xdir ( & dj , & dp - > obj ) ;
if ( res ! = FR_OK ) return res ;
dp - > obj . objsize = ld_dword ( fs - > dirbuf + XDIR_FileSize ) ;
dp - > obj . stat = fs - > dirbuf [ XDIR_GenFlags ] & 2 ;
}
# endif
# endif
if ( ( UINT ) * path < ' ' ) { /* Null path name is the origin directory itself */
dp - > fn [ NSFLAG ] = NS_NONAME ;
res = dir_sdi ( dp , 0 ) ;
} else { /* Follow path */
for ( ; ; ) {
res = create_name ( dp , & path ) ; /* Get a segment name of the path */
if ( res ! = FR_OK ) break ;
res = dir_find ( dp ) ; /* Find an object with the segment name */
ns = dp - > fn [ NSFLAG ] ;
if ( res ! = FR_OK ) { /* Failed to find the object */
if ( res = = FR_NO_FILE ) { /* Object is not found */
if ( FF_FS_RPATH & & ( ns & NS_DOT ) ) { /* If dot entry is not exist, stay there */
if ( ! ( ns & NS_LAST ) ) continue ; /* Continue to follow if not last segment */
dp - > fn [ NSFLAG ] = NS_NONAME ;
res = FR_OK ;
} else { /* Could not find the object */
if ( ! ( ns & NS_LAST ) ) res = FR_NO_PATH ; /* Adjust error code if not last segment */
}
}
break ;
}
if ( ns & NS_LAST ) break ; /* Last segment matched. Function completed. */
/* Get into the sub-directory */
if ( ! ( dp - > obj . attr & AM_DIR ) ) { /* It is not a sub-directory and cannot follow */
res = FR_NO_PATH ; break ;
}
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) { /* Save containing directory information for next dir */
dp - > obj . c_scl = dp - > obj . sclust ;
dp - > obj . c_size = ( ( DWORD ) dp - > obj . objsize & 0xFFFFFF00 ) | dp - > obj . stat ;
dp - > obj . c_ofs = dp - > blk_ofs ;
dp - > obj . sclust = ld_dword ( fs - > dirbuf + XDIR_FstClus ) ; /* Open next directory */
dp - > obj . stat = fs - > dirbuf [ XDIR_GenFlags ] & 2 ;
dp - > obj . objsize = ld_qword ( fs - > dirbuf + XDIR_FileSize ) ;
} else
# endif
{
dp - > obj . sclust = ld_clust ( fs , fs - > win + dp - > dptr % SS ( fs ) ) ; /* Open next directory */
}
}
}
return res ;
}
/*-----------------------------------------------------------------------*/
/* Get logical drive number from path name */
/*-----------------------------------------------------------------------*/
static
int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */
const TCHAR * * path /* Pointer to pointer to the path name */
)
{
const TCHAR * tp , * tt ;
UINT i ;
int vol = - 1 ;
# if FF_STR_VOLUME_ID /* Find string drive id */
static const char * const volid [ ] = { FF_VOLUME_STRS } ;
const char * sp ;
char c ;
TCHAR tc ;
# endif
if ( * path ) { /* If the pointer is not a null */
for ( tt = * path ; ( UINT ) * tt > = ( FF_USE_LFN ? ' ' : ' ! ' ) & & * tt ! = ' : ' ; tt + + ) ; /* Find a colon in the path */
if ( * tt = = ' : ' ) { /* If a colon is exist in the path name */
tp = * path ;
i = * tp + + ;
if ( IsDigit ( i ) & & tp = = tt ) { /* Is there a numeric drive id + colon? */
if ( ( i - = ' 0 ' ) < FF_VOLUMES ) { /* If drive id is found, get the value and strip it */
vol = ( int ) i ;
* path = + + tt ;
}
}
# if FF_STR_VOLUME_ID
else { /* No numeric drive number, find string drive id */
i = 0 ; tt + + ;
do {
sp = volid [ i ] ; tp = * path ;
do { /* Compare a string drive id with path name */
c = * sp + + ; tc = * tp + + ;
if ( IsLower ( tc ) ) tc - = 0x20 ;
} while ( c & & ( TCHAR ) c = = tc ) ;
} while ( ( c | | tp ! = tt ) & & + + i < FF_VOLUMES ) ; /* Repeat for each id until pattern match */
if ( i < FF_VOLUMES ) { /* If a drive id is found, get the value and strip it */
vol = ( int ) i ;
* path = tt ;
}
}
# endif
} else { /* No volume id and use default drive */
# if FF_FS_RPATH != 0 && FF_VOLUMES >= 2
vol = CurrVol ; /* Current drive */
# else
vol = 0 ; /* Drive 0 */
# endif
}
}
return vol ;
}
/*-----------------------------------------------------------------------*/
/* Load a sector and check if it is an FAT VBR */
/*-----------------------------------------------------------------------*/
static
BYTE check_fs ( /* 0:FAT, 1:exFAT, 2:Valid BS but not FAT, 3:Not a BS, 4:Disk error */
FATFS * fs , /* Filesystem object */
DWORD sect /* Sector# (lba) to load and check if it is an FAT-VBR or not */
)
{
fs - > wflag = 0 ; fs - > winsect = 0xFFFFFFFF ; /* Invaidate window */
if ( move_window ( fs , sect ) ! = FR_OK ) return 4 ; /* Load boot record */
if ( ld_word ( fs - > win + BS_55AA ) ! = 0xAA55 ) return 3 ; /* Check boot record signature (always placed here even if the sector size is >512) */
# if FF_FS_EXFAT
if ( ! mem_cmp ( fs - > win + BS_JmpBoot , " \xEB \x76 \x90 " " EXFAT " , 11 ) ) return 1 ; /* Check if exFAT VBR */
# endif
if ( fs - > win [ BS_JmpBoot ] = = 0xE9 | | ( fs - > win [ BS_JmpBoot ] = = 0xEB & & fs - > win [ BS_JmpBoot + 2 ] = = 0x90 ) ) { /* Valid JumpBoot code? */
if ( ! mem_cmp ( fs - > win + BS_FilSysType , " FAT " , 3 ) ) return 0 ; /* Is it an FAT VBR? */
if ( ! mem_cmp ( fs - > win + BS_FilSysType32 , " FAT32 " , 5 ) ) return 0 ; /* Is it an FAT32 VBR? */
}
return 2 ; /* Valid BS but not FAT */
}
/*-----------------------------------------------------------------------*/
/* Find logical drive and check if the volume is mounted */
/*-----------------------------------------------------------------------*/
static
FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */
const TCHAR * * path , /* Pointer to pointer to the path name (drive number) */
FATFS * * rfs , /* Pointer to pointer to the found filesystem object */
BYTE mode /* !=0: Check write protection for write access */
)
{
BYTE fmt , * pt ;
int vol ;
DSTATUS stat ;
DWORD bsect , fasize , tsect , sysect , nclst , szbfat , br [ 4 ] ;
WORD nrsv ;
FATFS * fs ;
UINT i ;
/* Get logical drive number */
* rfs = 0 ;
vol = get_ldnumber ( path ) ;
if ( vol < 0 ) return FR_INVALID_DRIVE ;
/* Check if the filesystem object is valid or not */
fs = FatFs [ vol ] ; /* Get pointer to the filesystem object */
if ( ! fs ) return FR_NOT_ENABLED ; /* Is the filesystem object available? */
# if FF_FS_REENTRANT
if ( ! lock_fs ( fs ) ) return FR_TIMEOUT ; /* Lock the volume */
# endif
* rfs = fs ; /* Return pointer to the filesystem object */
mode & = ( BYTE ) ~ FA_READ ; /* Desired access mode, write access or not */
if ( fs - > fs_type ! = 0 ) { /* If the volume has been mounted */
stat = disk_status ( fs - > pdrv ) ;
if ( ! ( stat & STA_NOINIT ) ) { /* and the physical drive is kept initialized */
if ( ! FF_FS_READONLY & & mode & & ( stat & STA_PROTECT ) ) { /* Check write protection if needed */
return FR_WRITE_PROTECTED ;
}
return FR_OK ; /* The filesystem object is valid */
}
}
/* The filesystem object is not valid. */
/* Following code attempts to mount the volume. (analyze BPB and initialize the filesystem object) */
fs - > fs_type = 0 ; /* Clear the filesystem object */
fs - > pdrv = LD2PD ( vol ) ; /* Bind the logical drive and a physical drive */
stat = disk_initialize ( fs - > pdrv ) ; /* Initialize the physical drive */
if ( stat & STA_NOINIT ) { /* Check if the initialization succeeded */
return FR_NOT_READY ; /* Failed to initialize due to no medium or hard error */
}
if ( ! FF_FS_READONLY & & mode & & ( stat & STA_PROTECT ) ) { /* Check disk write protection if needed */
return FR_WRITE_PROTECTED ;
}
# if FF_MAX_SS != FF_MIN_SS /* Get sector size (multiple sector size cfg only) */
if ( disk_ioctl ( fs - > pdrv , GET_SECTOR_SIZE , & SS ( fs ) ) ! = RES_OK ) return FR_DISK_ERR ;
if ( SS ( fs ) > FF_MAX_SS | | SS ( fs ) < FF_MIN_SS | | ( SS ( fs ) & ( SS ( fs ) - 1 ) ) ) return FR_DISK_ERR ;
# endif
/* Find an FAT partition on the drive. Supports only generic partitioning rules, FDISK and SFD. */
bsect = 0 ;
fmt = check_fs ( fs , bsect ) ; /* Load sector 0 and check if it is an FAT-VBR as SFD */
if ( fmt = = 2 | | ( fmt < 2 & & LD2PT ( vol ) ! = 0 ) ) { /* Not an FAT-VBR or forced partition number */
for ( i = 0 ; i < 4 ; i + + ) { /* Get partition offset */
pt = fs - > win + ( MBR_Table + i * SZ_PTE ) ;
br [ i ] = pt [ PTE_System ] ? ld_dword ( pt + PTE_StLba ) : 0 ;
}
i = LD2PT ( vol ) ; /* Partition number: 0:auto, 1-4:forced */
if ( i ! = 0 ) i - - ;
do { /* Find an FAT volume */
bsect = br [ i ] ;
fmt = bsect ? check_fs ( fs , bsect ) : 3 ; /* Check the partition */
} while ( LD2PT ( vol ) = = 0 & & fmt > = 2 & & + + i < 4 ) ;
}
if ( fmt = = 4 ) return FR_DISK_ERR ; /* An error occured in the disk I/O layer */
if ( fmt > = 2 ) return FR_NO_FILESYSTEM ; /* No FAT volume is found */
/* An FAT volume is found (bsect). Following code initializes the filesystem object */
# if FF_FS_EXFAT
if ( fmt = = 1 ) {
QWORD maxlba ;
for ( i = BPB_ZeroedEx ; i < BPB_ZeroedEx + 53 & & fs - > win [ i ] = = 0 ; i + + ) ; /* Check zero filler */
if ( i < BPB_ZeroedEx + 53 ) return FR_NO_FILESYSTEM ;
if ( ld_word ( fs - > win + BPB_FSVerEx ) ! = 0x100 ) return FR_NO_FILESYSTEM ; /* Check exFAT version (must be version 1.0) */
if ( 1 < < fs - > win [ BPB_BytsPerSecEx ] ! = SS ( fs ) ) { /* (BPB_BytsPerSecEx must be equal to the physical sector size) */
return FR_NO_FILESYSTEM ;
}
maxlba = ld_qword ( fs - > win + BPB_TotSecEx ) + bsect ; /* Last LBA + 1 of the volume */
if ( maxlba > = 0x100000000 ) return FR_NO_FILESYSTEM ; /* (It cannot be handled in 32-bit LBA) */
fs - > fsize = ld_dword ( fs - > win + BPB_FatSzEx ) ; /* Number of sectors per FAT */
fs - > n_fats = fs - > win [ BPB_NumFATsEx ] ; /* Number of FATs */
if ( fs - > n_fats ! = 1 ) return FR_NO_FILESYSTEM ; /* (Supports only 1 FAT) */
fs - > csize = 1 < < fs - > win [ BPB_SecPerClusEx ] ; /* Cluster size */
if ( fs - > csize = = 0 ) return FR_NO_FILESYSTEM ; /* (Must be 1..32768) */
nclst = ld_dword ( fs - > win + BPB_NumClusEx ) ; /* Number of clusters */
if ( nclst > MAX_EXFAT ) return FR_NO_FILESYSTEM ; /* (Too many clusters) */
fs - > n_fatent = nclst + 2 ;
/* Boundaries and Limits */
fs - > volbase = bsect ;
fs - > database = bsect + ld_dword ( fs - > win + BPB_DataOfsEx ) ;
fs - > fatbase = bsect + ld_dword ( fs - > win + BPB_FatOfsEx ) ;
if ( maxlba < ( QWORD ) fs - > database + nclst * fs - > csize ) return FR_NO_FILESYSTEM ; /* (Volume size must not be smaller than the size requiered) */
fs - > dirbase = ld_dword ( fs - > win + BPB_RootClusEx ) ;
/* Check if bitmap location is in assumption (at the first cluster) */
if ( move_window ( fs , clst2sect ( fs , fs - > dirbase ) ) ! = FR_OK ) return FR_DISK_ERR ;
for ( i = 0 ; i < SS ( fs ) ; i + = SZDIRE ) {
if ( fs - > win [ i ] = = 0x81 & & ld_dword ( fs - > win + i + 20 ) = = 2 ) break ; /* 81 entry with cluster #2? */
}
if ( i = = SS ( fs ) ) return FR_NO_FILESYSTEM ;
# if !FF_FS_READONLY
fs - > last_clst = fs - > free_clst = 0xFFFFFFFF ; /* Initialize cluster allocation information */
# endif
fmt = FS_EXFAT ; /* FAT sub-type */
} else
# endif /* FF_FS_EXFAT */
{
if ( ld_word ( fs - > win + BPB_BytsPerSec ) ! = SS ( fs ) ) return FR_NO_FILESYSTEM ; /* (BPB_BytsPerSec must be equal to the physical sector size) */
fasize = ld_word ( fs - > win + BPB_FATSz16 ) ; /* Number of sectors per FAT */
if ( fasize = = 0 ) fasize = ld_dword ( fs - > win + BPB_FATSz32 ) ;
fs - > fsize = fasize ;
fs - > n_fats = fs - > win [ BPB_NumFATs ] ; /* Number of FATs */
if ( fs - > n_fats ! = 1 & & fs - > n_fats ! = 2 ) return FR_NO_FILESYSTEM ; /* (Must be 1 or 2) */
fasize * = fs - > n_fats ; /* Number of sectors for FAT area */
fs - > csize = fs - > win [ BPB_SecPerClus ] ; /* Cluster size */
if ( fs - > csize = = 0 | | ( fs - > csize & ( fs - > csize - 1 ) ) ) return FR_NO_FILESYSTEM ; /* (Must be power of 2) */
fs - > n_rootdir = ld_word ( fs - > win + BPB_RootEntCnt ) ; /* Number of root directory entries */
if ( fs - > n_rootdir % ( SS ( fs ) / SZDIRE ) ) return FR_NO_FILESYSTEM ; /* (Must be sector aligned) */
tsect = ld_word ( fs - > win + BPB_TotSec16 ) ; /* Number of sectors on the volume */
if ( tsect = = 0 ) tsect = ld_dword ( fs - > win + BPB_TotSec32 ) ;
nrsv = ld_word ( fs - > win + BPB_RsvdSecCnt ) ; /* Number of reserved sectors */
if ( nrsv = = 0 ) return FR_NO_FILESYSTEM ; /* (Must not be 0) */
/* Determine the FAT sub type */
sysect = nrsv + fasize + fs - > n_rootdir / ( SS ( fs ) / SZDIRE ) ; /* RSV + FAT + DIR */
if ( tsect < sysect ) return FR_NO_FILESYSTEM ; /* (Invalid volume size) */
nclst = ( tsect - sysect ) / fs - > csize ; /* Number of clusters */
if ( nclst = = 0 ) return FR_NO_FILESYSTEM ; /* (Invalid volume size) */
fmt = 0 ;
if ( nclst < = MAX_FAT32 ) fmt = FS_FAT32 ;
if ( nclst < = MAX_FAT16 ) fmt = FS_FAT16 ;
if ( nclst < = MAX_FAT12 ) fmt = FS_FAT12 ;
if ( fmt = = 0 ) return FR_NO_FILESYSTEM ;
/* Boundaries and Limits */
fs - > n_fatent = nclst + 2 ; /* Number of FAT entries */
fs - > volbase = bsect ; /* Volume start sector */
fs - > fatbase = bsect + nrsv ; /* FAT start sector */
fs - > database = bsect + sysect ; /* Data start sector */
if ( fmt = = FS_FAT32 ) {
if ( ld_word ( fs - > win + BPB_FSVer32 ) ! = 0 ) return FR_NO_FILESYSTEM ; /* (Must be FAT32 revision 0.0) */
if ( fs - > n_rootdir ! = 0 ) return FR_NO_FILESYSTEM ; /* (BPB_RootEntCnt must be 0) */
fs - > dirbase = ld_dword ( fs - > win + BPB_RootClus32 ) ; /* Root directory start cluster */
szbfat = fs - > n_fatent * 4 ; /* (Needed FAT size) */
} else {
if ( fs - > n_rootdir = = 0 ) return FR_NO_FILESYSTEM ; /* (BPB_RootEntCnt must not be 0) */
fs - > dirbase = fs - > fatbase + fasize ; /* Root directory start sector */
szbfat = ( fmt = = FS_FAT16 ) ? /* (Needed FAT size) */
fs - > n_fatent * 2 : fs - > n_fatent * 3 / 2 + ( fs - > n_fatent & 1 ) ;
}
if ( fs - > fsize < ( szbfat + ( SS ( fs ) - 1 ) ) / SS ( fs ) ) return FR_NO_FILESYSTEM ; /* (BPB_FATSz must not be less than the size needed) */
# if !FF_FS_READONLY
/* Get FSInfo if available */
fs - > last_clst = fs - > free_clst = 0xFFFFFFFF ; /* Initialize cluster allocation information */
fs - > fsi_flag = 0x80 ;
# if (FF_FS_NOFSINFO & 3) != 3
if ( fmt = = FS_FAT32 /* Allow to update FSInfo only if BPB_FSInfo32 == 1 */
& & ld_word ( fs - > win + BPB_FSInfo32 ) = = 1
& & move_window ( fs , bsect + 1 ) = = FR_OK )
{
fs - > fsi_flag = 0 ;
if ( ld_word ( fs - > win + BS_55AA ) = = 0xAA55 /* Load FSInfo data if available */
& & ld_dword ( fs - > win + FSI_LeadSig ) = = 0x41615252
& & ld_dword ( fs - > win + FSI_StrucSig ) = = 0x61417272 )
{
# if (FF_FS_NOFSINFO & 1) == 0
fs - > free_clst = ld_dword ( fs - > win + FSI_Free_Count ) ;
# endif
# if (FF_FS_NOFSINFO & 2) == 0
fs - > last_clst = ld_dword ( fs - > win + FSI_Nxt_Free ) ;
# endif
}
}
# endif /* (FF_FS_NOFSINFO & 3) != 3 */
# endif /* !FF_FS_READONLY */
}
fs - > fs_type = fmt ; /* FAT sub-type */
fs - > id = + + Fsid ; /* Volume mount ID */
# if FF_USE_LFN == 1
fs - > lfnbuf = LfnBuf ; /* Static LFN working buffer */
# if FF_FS_EXFAT
fs - > dirbuf = DirBuf ; /* Static directory block scratchpad buuffer */
# endif
# endif
# if FF_FS_RPATH != 0
fs - > cdir = 0 ; /* Initialize current directory */
# endif
# if FF_FS_LOCK != 0 /* Clear file lock semaphores */
clear_lock ( fs ) ;
# endif
return FR_OK ;
}
/*-----------------------------------------------------------------------*/
/* Check if the file/directory object is valid or not */
/*-----------------------------------------------------------------------*/
static
FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */
FFOBJID * obj , /* Pointer to the FFOBJID, the 1st member in the FIL/DIR object, to check validity */
FATFS * * rfs /* Pointer to pointer to the owner filesystem object to return */
)
{
FRESULT res = FR_INVALID_OBJECT ;
if ( obj & & obj - > fs & & obj - > fs - > fs_type & & obj - > id = = obj - > fs - > id ) { /* Test if the object is valid */
# if FF_FS_REENTRANT
if ( lock_fs ( obj - > fs ) ) { /* Obtain the filesystem object */
if ( ! ( disk_status ( obj - > fs - > pdrv ) & STA_NOINIT ) ) { /* Test if the phsical drive is kept initialized */
res = FR_OK ;
} else {
unlock_fs ( obj - > fs , FR_OK ) ;
}
} else {
res = FR_TIMEOUT ;
}
# else
if ( ! ( disk_status ( obj - > fs - > pdrv ) & STA_NOINIT ) ) { /* Test if the phsical drive is kept initialized */
res = FR_OK ;
}
# endif
}
* rfs = ( res = = FR_OK ) ? obj - > fs : 0 ; /* Corresponding filesystem object */
return res ;
}
/*---------------------------------------------------------------------------
Public Functions ( FatFs API )
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*-----------------------------------------------------------------------*/
/* Mount/Unmount a Logical Drive */
/*-----------------------------------------------------------------------*/
FRESULT f_mount (
FATFS * fs , /* Pointer to the filesystem object (NULL:unmount)*/
const TCHAR * path , /* Logical drive number to be mounted/unmounted */
BYTE opt /* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */
)
{
FATFS * cfs ;
int vol ;
FRESULT res ;
const TCHAR * rp = path ;
/* Get logical drive number */
vol = get_ldnumber ( & rp ) ;
if ( vol < 0 ) return FR_INVALID_DRIVE ;
cfs = FatFs [ vol ] ; /* Pointer to fs object */
if ( cfs ) {
# if FF_FS_LOCK != 0
clear_lock ( cfs ) ;
# endif
# if FF_FS_REENTRANT /* Discard sync object of the current volume */
if ( ! ff_del_syncobj ( cfs - > sobj ) ) return FR_INT_ERR ;
# endif
cfs - > fs_type = 0 ; /* Clear old fs object */
}
if ( fs ) {
fs - > fs_type = 0 ; /* Clear new fs object */
# if FF_FS_REENTRANT /* Create sync object for the new volume */
if ( ! ff_cre_syncobj ( ( BYTE ) vol , & fs - > sobj ) ) return FR_INT_ERR ;
# endif
}
FatFs [ vol ] = fs ; /* Register new fs object */
if ( opt = = 0 ) return FR_OK ; /* Do not mount now, it will be mounted later */
res = find_volume ( & path , & fs , 0 ) ; /* Force mounted the volume */
LEAVE_FF ( fs , res ) ;
}
/*-----------------------------------------------------------------------*/
/* Open or Create a File */
/*-----------------------------------------------------------------------*/
FRESULT f_open (
FIL * fp , /* Pointer to the blank file object */
const TCHAR * path , /* Pointer to the file name */
BYTE mode /* Access mode and file open mode flags */
)
{
FRESULT res ;
DIR dj ;
FATFS * fs ;
# if !FF_FS_READONLY
DWORD dw , cl , bcs , clst , sc ;
FSIZE_t ofs ;
# endif
DEF_NAMBUF
if ( ! fp ) return FR_INVALID_OBJECT ;
/* Get logical drive */
mode & = FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND | FA_SEEKEND ;
res = find_volume ( & path , & fs , mode ) ;
if ( res = = FR_OK ) {
dj . obj . fs = fs ;
INIT_NAMBUF ( fs ) ;
res = follow_path ( & dj , path ) ; /* Follow the file path */
# if !FF_FS_READONLY /* Read/Write configuration */
if ( res = = FR_OK ) {
if ( dj . fn [ NSFLAG ] & NS_NONAME ) { /* Origin directory itself? */
res = FR_INVALID_NAME ;
}
# if FF_FS_LOCK != 0
else {
res = chk_lock ( & dj , ( mode & ~ FA_READ ) ? 1 : 0 ) ; /* Check if the file can be used */
}
# endif
}
/* Create or Open a file */
if ( mode & ( FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW ) ) {
if ( res ! = FR_OK ) { /* No file, create new */
if ( res = = FR_NO_FILE ) { /* There is no file to open, create a new entry */
# if FF_FS_LOCK != 0
res = enq_lock ( ) ? dir_register ( & dj ) : FR_TOO_MANY_OPEN_FILES ;
# else
res = dir_register ( & dj ) ;
# endif
}
mode | = FA_CREATE_ALWAYS ; /* File is created */
}
else { /* Any object with the same name is already existing */
if ( dj . obj . attr & ( AM_RDO | AM_DIR ) ) { /* Cannot overwrite it (R/O or DIR) */
res = FR_DENIED ;
} else {
if ( mode & FA_CREATE_NEW ) res = FR_EXIST ; /* Cannot create as new file */
}
}
if ( res = = FR_OK & & ( mode & FA_CREATE_ALWAYS ) ) { /* Truncate the file if overwrite mode */
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) {
/* Get current allocation info */
fp - > obj . fs = fs ;
fp - > obj . sclust = cl = ld_dword ( fs - > dirbuf + XDIR_FstClus ) ;
fp - > obj . objsize = ld_qword ( fs - > dirbuf + XDIR_FileSize ) ;
fp - > obj . stat = fs - > dirbuf [ XDIR_GenFlags ] & 2 ;
fp - > obj . n_frag = 0 ;
/* Set directory entry block initial state */
mem_set ( fs - > dirbuf + 2 , 0 , 30 ) ; /* Clear 85 entry except for NumSec */
mem_set ( fs - > dirbuf + 38 , 0 , 26 ) ; /* Clear C0 entry except for NumName and NameHash */
fs - > dirbuf [ XDIR_Attr ] = AM_ARC ;
st_dword ( fs - > dirbuf + XDIR_CrtTime , GET_FATTIME ( ) ) ;
fs - > dirbuf [ XDIR_GenFlags ] = 1 ;
res = store_xdir ( & dj ) ;
if ( res = = FR_OK & & cl ! = 0 ) { /* Remove the cluster chain if exist */
res = remove_chain ( & fp - > obj , cl , 0 ) ;
fs - > last_clst = cl - 1 ; /* Reuse the cluster hole */
}
} else
# endif
{
/* Set directory entry initial state */
cl = ld_clust ( fs , dj . dir ) ; /* Get current cluster chain */
st_dword ( dj . dir + DIR_CrtTime , GET_FATTIME ( ) ) ; /* Set created time */
dj . dir [ DIR_Attr ] = AM_ARC ; /* Reset attribute */
st_clust ( fs , dj . dir , 0 ) ; /* Reset file allocation info */
st_dword ( dj . dir + DIR_FileSize , 0 ) ;
fs - > wflag = 1 ;
if ( cl ! = 0 ) { /* Remove the cluster chain if exist */
dw = fs - > winsect ;
res = remove_chain ( & dj . obj , cl , 0 ) ;
if ( res = = FR_OK ) {
res = move_window ( fs , dw ) ;
fs - > last_clst = cl - 1 ; /* Reuse the cluster hole */
}
}
}
}
}
else { /* Open an existing file */
if ( res = = FR_OK ) { /* Is the object exsiting? */
if ( dj . obj . attr & AM_DIR ) { /* File open against a directory */
res = FR_NO_FILE ;
} else {
if ( ( mode & FA_WRITE ) & & ( dj . obj . attr & AM_RDO ) ) { /* Write mode open against R/O file */
res = FR_DENIED ;
}
}
}
}
if ( res = = FR_OK ) {
if ( mode & FA_CREATE_ALWAYS ) mode | = FA_MODIFIED ; /* Set file change flag if created or overwritten */
fp - > dir_sect = fs - > winsect ; /* Pointer to the directory entry */
fp - > dir_ptr = dj . dir ;
# if FF_FS_LOCK != 0
fp - > obj . lockid = inc_lock ( & dj , ( mode & ~ FA_READ ) ? 1 : 0 ) ; /* Lock the file for this session */
if ( ! fp - > obj . lockid ) res = FR_INT_ERR ;
# endif
}
# else /* R/O configuration */
if ( res = = FR_OK ) {
if ( dj . fn [ NSFLAG ] & NS_NONAME ) { /* Is it origin directory itself? */
res = FR_INVALID_NAME ;
} else {
if ( dj . obj . attr & AM_DIR ) { /* Is it a directory? */
res = FR_NO_FILE ;
}
}
}
# endif
if ( res = = FR_OK ) {
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) {
fp - > obj . c_scl = dj . obj . sclust ; /* Get containing directory info */
fp - > obj . c_size = ( ( DWORD ) dj . obj . objsize & 0xFFFFFF00 ) | dj . obj . stat ;
fp - > obj . c_ofs = dj . blk_ofs ;
fp - > obj . sclust = ld_dword ( fs - > dirbuf + XDIR_FstClus ) ; /* Get object allocation info */
fp - > obj . objsize = ld_qword ( fs - > dirbuf + XDIR_FileSize ) ;
fp - > obj . stat = fs - > dirbuf [ XDIR_GenFlags ] & 2 ;
fp - > obj . n_frag = 0 ;
} else
# endif
{
fp - > obj . sclust = ld_clust ( fs , dj . dir ) ; /* Get object allocation info */
fp - > obj . objsize = ld_dword ( dj . dir + DIR_FileSize ) ;
}
# if FF_USE_FASTSEEK
fp - > cltbl = 0 ; /* Disable fast seek mode */
# endif
fp - > obj . fs = fs ; /* Validate the file object */
fp - > obj . id = fs - > id ;
fp - > flag = mode ; /* Set file access mode */
fp - > err = 0 ; /* Clear error flag */
fp - > sect = 0 ; /* Invalidate current data sector */
fp - > fptr = 0 ; /* Set file pointer top of the file */
# if !FF_FS_READONLY
# if !FF_FS_TINY
mem_set ( fp - > buf , 0 , FF_MAX_SS ) ; /* Clear sector buffer */
# endif
if ( ( mode & FA_SEEKEND ) & & fp - > obj . objsize > 0 ) { /* Seek to end of file if FA_OPEN_APPEND is specified */
fp - > fptr = fp - > obj . objsize ; /* Offset to seek */
bcs = ( DWORD ) fs - > csize * SS ( fs ) ; /* Cluster size in byte */
clst = fp - > obj . sclust ; /* Follow the cluster chain */
for ( ofs = fp - > obj . objsize ; res = = FR_OK & & ofs > bcs ; ofs - = bcs ) {
clst = get_fat ( & fp - > obj , clst ) ;
if ( clst < = 1 ) res = FR_INT_ERR ;
if ( clst = = 0xFFFFFFFF ) res = FR_DISK_ERR ;
}
fp - > clust = clst ;
if ( res = = FR_OK & & ofs % SS ( fs ) ) { /* Fill sector buffer if not on the sector boundary */
if ( ( sc = clst2sect ( fs , clst ) ) = = 0 ) {
res = FR_INT_ERR ;
} else {
fp - > sect = sc + ( DWORD ) ( ofs / SS ( fs ) ) ;
# if !FF_FS_TINY
if ( disk_read ( fs - > pdrv , fp - > buf , fp - > sect , 1 ) ! = RES_OK ) res = FR_DISK_ERR ;
# endif
}
}
}
# endif
}
FREE_NAMBUF ( ) ;
}
if ( res ! = FR_OK ) fp - > obj . fs = 0 ; /* Invalidate file object on error */
LEAVE_FF ( fs , res ) ;
}
/*-----------------------------------------------------------------------*/
/* Read File */
/*-----------------------------------------------------------------------*/
FRESULT f_read (
FIL * fp , /* Pointer to the file object */
void * buff , /* Pointer to data buffer */
UINT btr , /* Number of bytes to read */
UINT * br /* Pointer to number of bytes read */
)
{
FRESULT res ;
FATFS * fs ;
DWORD clst , sect ;
FSIZE_t remain ;
UINT rcnt , cc , csect ;
BYTE * rbuff = ( BYTE * ) buff ;
* br = 0 ; /* Clear read byte counter */
res = validate ( & fp - > obj , & fs ) ; /* Check validity of the file object */
if ( res ! = FR_OK | | ( res = ( FRESULT ) fp - > err ) ! = FR_OK ) LEAVE_FF ( fs , res ) ; /* Check validity */
if ( ! ( fp - > flag & FA_READ ) ) LEAVE_FF ( fs , FR_DENIED ) ; /* Check access mode */
remain = fp - > obj . objsize - fp - > fptr ;
if ( btr > remain ) btr = ( UINT ) remain ; /* Truncate btr by remaining bytes */
for ( ; btr ; /* Repeat until all data read */
btr - = rcnt , * br + = rcnt , rbuff + = rcnt , fp - > fptr + = rcnt ) {
if ( fp - > fptr % SS ( fs ) = = 0 ) { /* On the sector boundary? */
csect = ( UINT ) ( fp - > fptr / SS ( fs ) & ( fs - > csize - 1 ) ) ; /* Sector offset in the cluster */
if ( csect = = 0 ) { /* On the cluster boundary? */
if ( fp - > fptr = = 0 ) { /* On the top of the file? */
clst = fp - > obj . sclust ; /* Follow cluster chain from the origin */
} else { /* Middle or end of the file */
# if FF_USE_FASTSEEK
if ( fp - > cltbl ) {
clst = clmt_clust ( fp , fp - > fptr ) ; /* Get cluster# from the CLMT */
} else
# endif
{
clst = get_fat ( & fp - > obj , fp - > clust ) ; /* Follow cluster chain on the FAT */
}
}
if ( clst < 2 ) ABORT ( fs , FR_INT_ERR ) ;
if ( clst = = 0xFFFFFFFF ) ABORT ( fs , FR_DISK_ERR ) ;
fp - > clust = clst ; /* Update current cluster */
}
sect = clst2sect ( fs , fp - > clust ) ; /* Get current sector */
if ( sect = = 0 ) ABORT ( fs , FR_INT_ERR ) ;
sect + = csect ;
cc = btr / SS ( fs ) ; /* When remaining bytes >= sector size, */
if ( cc > 0 ) { /* Read maximum contiguous sectors directly */
if ( csect + cc > fs - > csize ) { /* Clip at cluster boundary */
cc = fs - > csize - csect ;
}
if ( disk_read ( fs - > pdrv , rbuff , sect , cc ) ! = RES_OK ) ABORT ( fs , FR_DISK_ERR ) ;
# if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */
# if FF_FS_TINY
if ( fs - > wflag & & fs - > winsect - sect < cc ) {
mem_cpy ( rbuff + ( ( fs - > winsect - sect ) * SS ( fs ) ) , fs - > win , SS ( fs ) ) ;
}
# else
if ( ( fp - > flag & FA_DIRTY ) & & fp - > sect - sect < cc ) {
mem_cpy ( rbuff + ( ( fp - > sect - sect ) * SS ( fs ) ) , fp - > buf , SS ( fs ) ) ;
}
# endif
# endif
rcnt = SS ( fs ) * cc ; /* Number of bytes transferred */
continue ;
}
# if !FF_FS_TINY
if ( fp - > sect ! = sect ) { /* Load data sector if not in cache */
# if !FF_FS_READONLY
if ( fp - > flag & FA_DIRTY ) { /* Write-back dirty sector cache */
if ( disk_write ( fs - > pdrv , fp - > buf , fp - > sect , 1 ) ! = RES_OK ) ABORT ( fs , FR_DISK_ERR ) ;
fp - > flag & = ( BYTE ) ~ FA_DIRTY ;
}
# endif
if ( disk_read ( fs - > pdrv , fp - > buf , sect , 1 ) ! = RES_OK ) ABORT ( fs , FR_DISK_ERR ) ; /* Fill sector cache */
}
# endif
fp - > sect = sect ;
}
rcnt = SS ( fs ) - ( UINT ) fp - > fptr % SS ( fs ) ; /* Number of bytes left in the sector */
if ( rcnt > btr ) rcnt = btr ; /* Clip it by btr if needed */
# if FF_FS_TINY
if ( move_window ( fs , fp - > sect ) ! = FR_OK ) ABORT ( fs , FR_DISK_ERR ) ; /* Move sector window */
mem_cpy ( rbuff , fs - > win + fp - > fptr % SS ( fs ) , rcnt ) ; /* Extract partial sector */
# else
mem_cpy ( rbuff , fp - > buf + fp - > fptr % SS ( fs ) , rcnt ) ; /* Extract partial sector */
# endif
}
LEAVE_FF ( fs , FR_OK ) ;
}
# if !FF_FS_READONLY
/*-----------------------------------------------------------------------*/
/* Write File */
/*-----------------------------------------------------------------------*/
FRESULT f_write (
FIL * fp , /* Pointer to the file object */
const void * buff , /* Pointer to the data to be written */
UINT btw , /* Number of bytes to write */
UINT * bw /* Pointer to number of bytes written */
)
{
FRESULT res ;
FATFS * fs ;
DWORD clst , sect ;
UINT wcnt , cc , csect ;
const BYTE * wbuff = ( const BYTE * ) buff ;
* bw = 0 ; /* Clear write byte counter */
res = validate ( & fp - > obj , & fs ) ; /* Check validity of the file object */
if ( res ! = FR_OK | | ( res = ( FRESULT ) fp - > err ) ! = FR_OK ) LEAVE_FF ( fs , res ) ; /* Check validity */
if ( ! ( fp - > flag & FA_WRITE ) ) LEAVE_FF ( fs , FR_DENIED ) ; /* Check access mode */
/* Check fptr wrap-around (file size cannot reach 4 GiB at FAT volume) */
if ( ( ! FF_FS_EXFAT | | fs - > fs_type ! = FS_EXFAT ) & & ( DWORD ) ( fp - > fptr + btw ) < ( DWORD ) fp - > fptr ) {
btw = ( UINT ) ( 0xFFFFFFFF - ( DWORD ) fp - > fptr ) ;
}
for ( ; btw ; /* Repeat until all data written */
btw - = wcnt , * bw + = wcnt , wbuff + = wcnt , fp - > fptr + = wcnt , fp - > obj . objsize = ( fp - > fptr > fp - > obj . objsize ) ? fp - > fptr : fp - > obj . objsize ) {
if ( fp - > fptr % SS ( fs ) = = 0 ) { /* On the sector boundary? */
csect = ( UINT ) ( fp - > fptr / SS ( fs ) ) & ( fs - > csize - 1 ) ; /* Sector offset in the cluster */
if ( csect = = 0 ) { /* On the cluster boundary? */
if ( fp - > fptr = = 0 ) { /* On the top of the file? */
clst = fp - > obj . sclust ; /* Follow from the origin */
if ( clst = = 0 ) { /* If no cluster is allocated, */
clst = create_chain ( & fp - > obj , 0 ) ; /* create a new cluster chain */
}
} else { /* On the middle or end of the file */
# if FF_USE_FASTSEEK
if ( fp - > cltbl ) {
clst = clmt_clust ( fp , fp - > fptr ) ; /* Get cluster# from the CLMT */
} else
# endif
{
clst = create_chain ( & fp - > obj , fp - > clust ) ; /* Follow or stretch cluster chain on the FAT */
}
}
if ( clst = = 0 ) break ; /* Could not allocate a new cluster (disk full) */
if ( clst = = 1 ) ABORT ( fs , FR_INT_ERR ) ;
if ( clst = = 0xFFFFFFFF ) ABORT ( fs , FR_DISK_ERR ) ;
fp - > clust = clst ; /* Update current cluster */
if ( fp - > obj . sclust = = 0 ) fp - > obj . sclust = clst ; /* Set start cluster if the first write */
}
# if FF_FS_TINY
if ( fs - > winsect = = fp - > sect & & sync_window ( fs ) ! = FR_OK ) ABORT ( fs , FR_DISK_ERR ) ; /* Write-back sector cache */
# else
if ( fp - > flag & FA_DIRTY ) { /* Write-back sector cache */
if ( disk_write ( fs - > pdrv , fp - > buf , fp - > sect , 1 ) ! = RES_OK ) ABORT ( fs , FR_DISK_ERR ) ;
fp - > flag & = ( BYTE ) ~ FA_DIRTY ;
}
# endif
sect = clst2sect ( fs , fp - > clust ) ; /* Get current sector */
if ( sect = = 0 ) ABORT ( fs , FR_INT_ERR ) ;
sect + = csect ;
cc = btw / SS ( fs ) ; /* When remaining bytes >= sector size, */
if ( cc > 0 ) { /* Write maximum contiguous sectors directly */
if ( csect + cc > fs - > csize ) { /* Clip at cluster boundary */
cc = fs - > csize - csect ;
}
if ( disk_write ( fs - > pdrv , wbuff , sect , cc ) ! = RES_OK ) ABORT ( fs , FR_DISK_ERR ) ;
# if FF_FS_MINIMIZE <= 2
# if FF_FS_TINY
if ( fs - > winsect - sect < cc ) { /* Refill sector cache if it gets invalidated by the direct write */
mem_cpy ( fs - > win , wbuff + ( ( fs - > winsect - sect ) * SS ( fs ) ) , SS ( fs ) ) ;
fs - > wflag = 0 ;
}
# else
if ( fp - > sect - sect < cc ) { /* Refill sector cache if it gets invalidated by the direct write */
mem_cpy ( fp - > buf , wbuff + ( ( fp - > sect - sect ) * SS ( fs ) ) , SS ( fs ) ) ;
fp - > flag & = ( BYTE ) ~ FA_DIRTY ;
}
# endif
# endif
wcnt = SS ( fs ) * cc ; /* Number of bytes transferred */
continue ;
}
# if FF_FS_TINY
if ( fp - > fptr > = fp - > obj . objsize ) { /* Avoid silly cache filling on the growing edge */
if ( sync_window ( fs ) ! = FR_OK ) ABORT ( fs , FR_DISK_ERR ) ;
fs - > winsect = sect ;
}
# else
if ( fp - > sect ! = sect & & /* Fill sector cache with file data */
fp - > fptr < fp - > obj . objsize & &
disk_read ( fs - > pdrv , fp - > buf , sect , 1 ) ! = RES_OK ) {
ABORT ( fs , FR_DISK_ERR ) ;
}
# endif
fp - > sect = sect ;
}
wcnt = SS ( fs ) - ( UINT ) fp - > fptr % SS ( fs ) ; /* Number of bytes left in the sector */
if ( wcnt > btw ) wcnt = btw ; /* Clip it by btw if needed */
# if FF_FS_TINY
if ( move_window ( fs , fp - > sect ) ! = FR_OK ) ABORT ( fs , FR_DISK_ERR ) ; /* Move sector window */
mem_cpy ( fs - > win + fp - > fptr % SS ( fs ) , wbuff , wcnt ) ; /* Fit data to the sector */
fs - > wflag = 1 ;
# else
mem_cpy ( fp - > buf + fp - > fptr % SS ( fs ) , wbuff , wcnt ) ; /* Fit data to the sector */
fp - > flag | = FA_DIRTY ;
# endif
}
fp - > flag | = FA_MODIFIED ; /* Set file change flag */
LEAVE_FF ( fs , FR_OK ) ;
}
/*-----------------------------------------------------------------------*/
/* Synchronize the File */
/*-----------------------------------------------------------------------*/
FRESULT f_sync (
FIL * fp /* Pointer to the file object */
)
{
FRESULT res ;
FATFS * fs ;
DWORD tm ;
BYTE * dir ;
res = validate ( & fp - > obj , & fs ) ; /* Check validity of the file object */
if ( res = = FR_OK ) {
if ( fp - > flag & FA_MODIFIED ) { /* Is there any change to the file? */
# if !FF_FS_TINY
if ( fp - > flag & FA_DIRTY ) { /* Write-back cached data if needed */
if ( disk_write ( fs - > pdrv , fp - > buf , fp - > sect , 1 ) ! = RES_OK ) LEAVE_FF ( fs , FR_DISK_ERR ) ;
fp - > flag & = ( BYTE ) ~ FA_DIRTY ;
}
# endif
/* Update the directory entry */
tm = GET_FATTIME ( ) ; /* Modified time */
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) {
res = fill_first_frag ( & fp - > obj ) ; /* Fill first fragment on the FAT if needed */
if ( res = = FR_OK ) {
res = fill_last_frag ( & fp - > obj , fp - > clust , 0xFFFFFFFF ) ; /* Fill last fragment on the FAT if needed */
}
if ( res = = FR_OK ) {
DIR dj ;
DEF_NAMBUF
INIT_NAMBUF ( fs ) ;
res = load_obj_xdir ( & dj , & fp - > obj ) ; /* Load directory entry block */
if ( res = = FR_OK ) {
fs - > dirbuf [ XDIR_Attr ] | = AM_ARC ; /* Set archive attribute to indicate that the file has been changed */
fs - > dirbuf [ XDIR_GenFlags ] = fp - > obj . stat | 1 ; /* Update file allocation information */
st_dword ( fs - > dirbuf + XDIR_FstClus , fp - > obj . sclust ) ;
st_qword ( fs - > dirbuf + XDIR_FileSize , fp - > obj . objsize ) ;
st_qword ( fs - > dirbuf + XDIR_ValidFileSize , fp - > obj . objsize ) ;
st_dword ( fs - > dirbuf + XDIR_ModTime , tm ) ; /* Update modified time */
fs - > dirbuf [ XDIR_ModTime10 ] = 0 ;
st_dword ( fs - > dirbuf + XDIR_AccTime , 0 ) ;
res = store_xdir ( & dj ) ; /* Restore it to the directory */
if ( res = = FR_OK ) {
res = sync_fs ( fs ) ;
fp - > flag & = ( BYTE ) ~ FA_MODIFIED ;
}
}
FREE_NAMBUF ( ) ;
}
} else
# endif
{
res = move_window ( fs , fp - > dir_sect ) ;
if ( res = = FR_OK ) {
dir = fp - > dir_ptr ;
dir [ DIR_Attr ] | = AM_ARC ; /* Set archive attribute to indicate that the file has been changed */
st_clust ( fp - > obj . fs , dir , fp - > obj . sclust ) ; /* Update file allocation information */
st_dword ( dir + DIR_FileSize , ( DWORD ) fp - > obj . objsize ) ; /* Update file size */
st_dword ( dir + DIR_ModTime , tm ) ; /* Update modified time */
st_word ( dir + DIR_LstAccDate , 0 ) ;
fs - > wflag = 1 ;
res = sync_fs ( fs ) ; /* Restore it to the directory */
fp - > flag & = ( BYTE ) ~ FA_MODIFIED ;
}
}
}
}
LEAVE_FF ( fs , res ) ;
}
# endif /* !FF_FS_READONLY */
/*-----------------------------------------------------------------------*/
/* Close File */
/*-----------------------------------------------------------------------*/
FRESULT f_close (
FIL * fp /* Pointer to the file object to be closed */
)
{
FRESULT res ;
FATFS * fs ;
# if !FF_FS_READONLY
res = f_sync ( fp ) ; /* Flush cached data */
if ( res = = FR_OK )
# endif
{
res = validate ( & fp - > obj , & fs ) ; /* Lock volume */
if ( res = = FR_OK ) {
# if FF_FS_LOCK != 0
res = dec_lock ( fp - > obj . lockid ) ; /* Decrement file open counter */
if ( res = = FR_OK ) fp - > obj . fs = 0 ; /* Invalidate file object */
# else
fp - > obj . fs = 0 ; /* Invalidate file object */
# endif
# if FF_FS_REENTRANT
unlock_fs ( fs , FR_OK ) ; /* Unlock volume */
# endif
}
}
return res ;
}
# if FF_FS_RPATH >= 1
/*-----------------------------------------------------------------------*/
/* Change Current Directory or Current Drive, Get Current Directory */
/*-----------------------------------------------------------------------*/
# if FF_VOLUMES >= 2
FRESULT f_chdrive (
const TCHAR * path /* Drive number */
)
{
int vol ;
/* Get logical drive number */
vol = get_ldnumber ( & path ) ;
if ( vol < 0 ) return FR_INVALID_DRIVE ;
CurrVol = ( BYTE ) vol ; /* Set it as current volume */
return FR_OK ;
}
# endif
FRESULT f_chdir (
const TCHAR * path /* Pointer to the directory path */
)
{
FRESULT res ;
DIR dj ;
FATFS * fs ;
DEF_NAMBUF
/* Get logical drive */
res = find_volume ( & path , & fs , 0 ) ;
if ( res = = FR_OK ) {
dj . obj . fs = fs ;
INIT_NAMBUF ( fs ) ;
res = follow_path ( & dj , path ) ; /* Follow the path */
if ( res = = FR_OK ) { /* Follow completed */
if ( dj . fn [ NSFLAG ] & NS_NONAME ) {
fs - > cdir = dj . obj . sclust ; /* It is the start directory itself */
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) {
fs - > cdc_scl = dj . obj . c_scl ;
fs - > cdc_size = dj . obj . c_size ;
fs - > cdc_ofs = dj . obj . c_ofs ;
}
# endif
} else {
if ( dj . obj . attr & AM_DIR ) { /* It is a sub-directory */
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) {
fs - > cdir = ld_dword ( fs - > dirbuf + XDIR_FstClus ) ; /* Sub-directory cluster */
fs - > cdc_scl = dj . obj . sclust ; /* Save containing directory information */
fs - > cdc_size = ( ( DWORD ) dj . obj . objsize & 0xFFFFFF00 ) | dj . obj . stat ;
fs - > cdc_ofs = dj . blk_ofs ;
} else
# endif
{
fs - > cdir = ld_clust ( fs , dj . dir ) ; /* Sub-directory cluster */
}
} else {
res = FR_NO_PATH ; /* Reached but a file */
}
}
}
FREE_NAMBUF ( ) ;
if ( res = = FR_NO_FILE ) res = FR_NO_PATH ;
}
LEAVE_FF ( fs , res ) ;
}
# if FF_FS_RPATH >= 2
FRESULT f_getcwd (
TCHAR * buff , /* Pointer to the directory path */
UINT len /* Size of path */
)
{
FRESULT res ;
DIR dj ;
FATFS * fs ;
UINT i , n ;
DWORD ccl ;
TCHAR * tp ;
FILINFO fno ;
DEF_NAMBUF
* buff = 0 ;
/* Get logical drive */
res = find_volume ( ( const TCHAR * * ) & buff , & fs , 0 ) ; /* Get current volume */
if ( res = = FR_OK ) {
dj . obj . fs = fs ;
INIT_NAMBUF ( fs ) ;
i = len ; /* Bottom of buffer (directory stack base) */
if ( ! FF_FS_EXFAT | | fs - > fs_type ! = FS_EXFAT ) { /* (Cannot do getcwd on exFAT and returns root path) */
dj . obj . sclust = fs - > cdir ; /* Start to follow upper directory from current directory */
while ( ( ccl = dj . obj . sclust ) ! = 0 ) { /* Repeat while current directory is a sub-directory */
res = dir_sdi ( & dj , 1 * SZDIRE ) ; /* Get parent directory */
if ( res ! = FR_OK ) break ;
res = move_window ( fs , dj . sect ) ;
if ( res ! = FR_OK ) break ;
dj . obj . sclust = ld_clust ( fs , dj . dir ) ; /* Goto parent directory */
res = dir_sdi ( & dj , 0 ) ;
if ( res ! = FR_OK ) break ;
do { /* Find the entry links to the child directory */
res = dir_read ( & dj , 0 ) ;
if ( res ! = FR_OK ) break ;
if ( ccl = = ld_clust ( fs , dj . dir ) ) break ; /* Found the entry */
res = dir_next ( & dj , 0 ) ;
} while ( res = = FR_OK ) ;
if ( res = = FR_NO_FILE ) res = FR_INT_ERR ; /* It cannot be 'not found'. */
if ( res ! = FR_OK ) break ;
get_fileinfo ( & dj , & fno ) ; /* Get the directory name and push it to the buffer */
for ( n = 0 ; fno . fname [ n ] ; n + + ) ;
if ( i < n + 3 ) {
res = FR_NOT_ENOUGH_CORE ; break ;
}
while ( n ) buff [ - - i ] = fno . fname [ - - n ] ;
buff [ - - i ] = ' / ' ;
}
}
tp = buff ;
if ( res = = FR_OK ) {
# if FF_VOLUMES >= 2
* tp + + = ' 0 ' + CurrVol ; /* Put drive number */
* tp + + = ' : ' ;
# endif
if ( i = = len ) { /* Root-directory */
* tp + + = ' / ' ;
} else { /* Sub-directroy */
do /* Add stacked path str */
* tp + + = buff [ i + + ] ;
while ( i < len ) ;
}
}
* tp = 0 ;
FREE_NAMBUF ( ) ;
}
LEAVE_FF ( fs , res ) ;
}
# endif /* FF_FS_RPATH >= 2 */
# endif /* FF_FS_RPATH >= 1 */
# if FF_FS_MINIMIZE <= 2
/*-----------------------------------------------------------------------*/
/* Seek File Read/Write Pointer */
/*-----------------------------------------------------------------------*/
FRESULT f_lseek (
FIL * fp , /* Pointer to the file object */
FSIZE_t ofs /* File pointer from top of file */
)
{
FRESULT res ;
FATFS * fs ;
DWORD clst , bcs , nsect ;
FSIZE_t ifptr ;
# if FF_USE_FASTSEEK
DWORD cl , pcl , ncl , tcl , dsc , tlen , ulen , * tbl ;
# endif
res = validate ( & fp - > obj , & fs ) ; /* Check validity of the file object */
if ( res = = FR_OK ) res = ( FRESULT ) fp - > err ;
# if FF_FS_EXFAT && !FF_FS_READONLY
if ( res = = FR_OK & & fs - > fs_type = = FS_EXFAT ) {
res = fill_last_frag ( & fp - > obj , fp - > clust , 0xFFFFFFFF ) ; /* Fill last fragment on the FAT if needed */
}
# endif
if ( res ! = FR_OK ) LEAVE_FF ( fs , res ) ;
# if FF_USE_FASTSEEK
if ( fp - > cltbl ) { /* Fast seek */
if ( ofs = = CREATE_LINKMAP ) { /* Create CLMT */
tbl = fp - > cltbl ;
tlen = * tbl + + ; ulen = 2 ; /* Given table size and required table size */
cl = fp - > obj . sclust ; /* Origin of the chain */
if ( cl ! = 0 ) {
do {
/* Get a fragment */
tcl = cl ; ncl = 0 ; ulen + = 2 ; /* Top, length and used items */
do {
pcl = cl ; ncl + + ;
cl = get_fat ( & fp - > obj , cl ) ;
if ( cl < = 1 ) ABORT ( fs , FR_INT_ERR ) ;
if ( cl = = 0xFFFFFFFF ) ABORT ( fs , FR_DISK_ERR ) ;
} while ( cl = = pcl + 1 ) ;
if ( ulen < = tlen ) { /* Store the length and top of the fragment */
* tbl + + = ncl ; * tbl + + = tcl ;
}
} while ( cl < fs - > n_fatent ) ; /* Repeat until end of chain */
}
* fp - > cltbl = ulen ; /* Number of items used */
if ( ulen < = tlen ) {
* tbl = 0 ; /* Terminate table */
} else {
res = FR_NOT_ENOUGH_CORE ; /* Given table size is smaller than required */
}
} else { /* Fast seek */
if ( ofs > fp - > obj . objsize ) ofs = fp - > obj . objsize ; /* Clip offset at the file size */
fp - > fptr = ofs ; /* Set file pointer */
if ( ofs > 0 ) {
fp - > clust = clmt_clust ( fp , ofs - 1 ) ;
dsc = clst2sect ( fs , fp - > clust ) ;
if ( dsc = = 0 ) ABORT ( fs , FR_INT_ERR ) ;
dsc + = ( DWORD ) ( ( ofs - 1 ) / SS ( fs ) ) & ( fs - > csize - 1 ) ;
if ( fp - > fptr % SS ( fs ) & & dsc ! = fp - > sect ) { /* Refill sector cache if needed */
# if !FF_FS_TINY
# if !FF_FS_READONLY
if ( fp - > flag & FA_DIRTY ) { /* Write-back dirty sector cache */
if ( disk_write ( fs - > pdrv , fp - > buf , fp - > sect , 1 ) ! = RES_OK ) ABORT ( fs , FR_DISK_ERR ) ;
fp - > flag & = ( BYTE ) ~ FA_DIRTY ;
}
# endif
if ( disk_read ( fs - > pdrv , fp - > buf , dsc , 1 ) ! = RES_OK ) ABORT ( fs , FR_DISK_ERR ) ; /* Load current sector */
# endif
fp - > sect = dsc ;
}
}
}
} else
# endif
/* Normal Seek */
{
# if FF_FS_EXFAT
if ( fs - > fs_type ! = FS_EXFAT & & ofs > = 0x100000000 ) ofs = 0xFFFFFFFF ; /* Clip at 4 GiB - 1 if at FATxx */
# endif
if ( ofs > fp - > obj . objsize & & ( FF_FS_READONLY | | ! ( fp - > flag & FA_WRITE ) ) ) { /* In read-only mode, clip offset with the file size */
ofs = fp - > obj . objsize ;
}
ifptr = fp - > fptr ;
fp - > fptr = nsect = 0 ;
if ( ofs > 0 ) {
bcs = ( DWORD ) fs - > csize * SS ( fs ) ; /* Cluster size (byte) */
if ( ifptr > 0 & &
( ofs - 1 ) / bcs > = ( ifptr - 1 ) / bcs ) { /* When seek to same or following cluster, */
fp - > fptr = ( ifptr - 1 ) & ~ ( FSIZE_t ) ( bcs - 1 ) ; /* start from the current cluster */
ofs - = fp - > fptr ;
clst = fp - > clust ;
} else { /* When seek to back cluster, */
clst = fp - > obj . sclust ; /* start from the first cluster */
# if !FF_FS_READONLY
if ( clst = = 0 ) { /* If no cluster chain, create a new chain */
clst = create_chain ( & fp - > obj , 0 ) ;
if ( clst = = 1 ) ABORT ( fs , FR_INT_ERR ) ;
if ( clst = = 0xFFFFFFFF ) ABORT ( fs , FR_DISK_ERR ) ;
fp - > obj . sclust = clst ;
}
# endif
fp - > clust = clst ;
}
if ( clst ! = 0 ) {
while ( ofs > bcs ) { /* Cluster following loop */
ofs - = bcs ; fp - > fptr + = bcs ;
# if !FF_FS_READONLY
if ( fp - > flag & FA_WRITE ) { /* Check if in write mode or not */
if ( FF_FS_EXFAT & & fp - > fptr > fp - > obj . objsize ) { /* No FAT chain object needs correct objsize to generate FAT value */
fp - > obj . objsize = fp - > fptr ;
fp - > flag | = FA_MODIFIED ;
}
clst = create_chain ( & fp - > obj , clst ) ; /* Follow chain with forceed stretch */
if ( clst = = 0 ) { /* Clip file size in case of disk full */
ofs = 0 ; break ;
}
} else
# endif
{
clst = get_fat ( & fp - > obj , clst ) ; /* Follow cluster chain if not in write mode */
}
if ( clst = = 0xFFFFFFFF ) ABORT ( fs , FR_DISK_ERR ) ;
if ( clst < = 1 | | clst > = fs - > n_fatent ) ABORT ( fs , FR_INT_ERR ) ;
fp - > clust = clst ;
}
fp - > fptr + = ofs ;
if ( ofs % SS ( fs ) ) {
nsect = clst2sect ( fs , clst ) ; /* Current sector */
if ( nsect = = 0 ) ABORT ( fs , FR_INT_ERR ) ;
nsect + = ( DWORD ) ( ofs / SS ( fs ) ) ;
}
}
}
if ( ! FF_FS_READONLY & & fp - > fptr > fp - > obj . objsize ) { /* Set file change flag if the file size is extended */
fp - > obj . objsize = fp - > fptr ;
fp - > flag | = FA_MODIFIED ;
}
if ( fp - > fptr % SS ( fs ) & & nsect ! = fp - > sect ) { /* Fill sector cache if needed */
# if !FF_FS_TINY
# if !FF_FS_READONLY
if ( fp - > flag & FA_DIRTY ) { /* Write-back dirty sector cache */
if ( disk_write ( fs - > pdrv , fp - > buf , fp - > sect , 1 ) ! = RES_OK ) ABORT ( fs , FR_DISK_ERR ) ;
fp - > flag & = ( BYTE ) ~ FA_DIRTY ;
}
# endif
if ( disk_read ( fs - > pdrv , fp - > buf , nsect , 1 ) ! = RES_OK ) ABORT ( fs , FR_DISK_ERR ) ; /* Fill sector cache */
# endif
fp - > sect = nsect ;
}
}
LEAVE_FF ( fs , res ) ;
}
# if FF_FS_MINIMIZE <= 1
/*-----------------------------------------------------------------------*/
/* Create a Directory Object */
/*-----------------------------------------------------------------------*/
FRESULT f_opendir (
DIR * dp , /* Pointer to directory object to create */
const TCHAR * path /* Pointer to the directory path */
)
{
FRESULT res ;
FATFS * fs ;
DEF_NAMBUF
if ( ! dp ) return FR_INVALID_OBJECT ;
/* Get logical drive */
res = find_volume ( & path , & fs , 0 ) ;
if ( res = = FR_OK ) {
dp - > obj . fs = fs ;
INIT_NAMBUF ( fs ) ;
res = follow_path ( dp , path ) ; /* Follow the path to the directory */
if ( res = = FR_OK ) { /* Follow completed */
if ( ! ( dp - > fn [ NSFLAG ] & NS_NONAME ) ) { /* It is not the origin directory itself */
if ( dp - > obj . attr & AM_DIR ) { /* This object is a sub-directory */
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) {
dp - > obj . c_scl = dp - > obj . sclust ; /* Get containing directory inforamation */
dp - > obj . c_size = ( ( DWORD ) dp - > obj . objsize & 0xFFFFFF00 ) | dp - > obj . stat ;
dp - > obj . c_ofs = dp - > blk_ofs ;
dp - > obj . sclust = ld_dword ( fs - > dirbuf + XDIR_FstClus ) ; /* Get object allocation info */
dp - > obj . objsize = ld_qword ( fs - > dirbuf + XDIR_FileSize ) ;
dp - > obj . stat = fs - > dirbuf [ XDIR_GenFlags ] & 2 ;
} else
# endif
{
dp - > obj . sclust = ld_clust ( fs , dp - > dir ) ; /* Get object allocation info */
}
} else { /* This object is a file */
res = FR_NO_PATH ;
}
}
if ( res = = FR_OK ) {
dp - > obj . id = fs - > id ;
res = dir_sdi ( dp , 0 ) ; /* Rewind directory */
# if FF_FS_LOCK != 0
if ( res = = FR_OK ) {
if ( dp - > obj . sclust ! = 0 ) {
dp - > obj . lockid = inc_lock ( dp , 0 ) ; /* Lock the sub directory */
if ( ! dp - > obj . lockid ) res = FR_TOO_MANY_OPEN_FILES ;
} else {
dp - > obj . lockid = 0 ; /* Root directory need not to be locked */
}
}
# endif
}
}
FREE_NAMBUF ( ) ;
if ( res = = FR_NO_FILE ) res = FR_NO_PATH ;
}
if ( res ! = FR_OK ) dp - > obj . fs = 0 ; /* Invalidate the directory object if function faild */
LEAVE_FF ( fs , res ) ;
}
/*-----------------------------------------------------------------------*/
/* Close Directory */
/*-----------------------------------------------------------------------*/
FRESULT f_closedir (
DIR * dp /* Pointer to the directory object to be closed */
)
{
FRESULT res ;
FATFS * fs ;
res = validate ( & dp - > obj , & fs ) ; /* Check validity of the file object */
if ( res = = FR_OK ) {
# if FF_FS_LOCK != 0
if ( dp - > obj . lockid ) res = dec_lock ( dp - > obj . lockid ) ; /* Decrement sub-directory open counter */
if ( res = = FR_OK ) dp - > obj . fs = 0 ; /* Invalidate directory object */
# else
dp - > obj . fs = 0 ; /* Invalidate directory object */
# endif
# if FF_FS_REENTRANT
unlock_fs ( fs , FR_OK ) ; /* Unlock volume */
# endif
}
return res ;
}
/*-----------------------------------------------------------------------*/
/* Read Directory Entries in Sequence */
/*-----------------------------------------------------------------------*/
FRESULT f_readdir (
DIR * dp , /* Pointer to the open directory object */
FILINFO * fno /* Pointer to file information to return */
)
{
FRESULT res ;
FATFS * fs ;
DEF_NAMBUF
res = validate ( & dp - > obj , & fs ) ; /* Check validity of the directory object */
if ( res = = FR_OK ) {
if ( ! fno ) {
res = dir_sdi ( dp , 0 ) ; /* Rewind the directory object */
} else {
INIT_NAMBUF ( fs ) ;
res = dir_read ( dp , 0 ) ; /* Read an item */
if ( res = = FR_NO_FILE ) res = FR_OK ; /* Ignore end of directory */
if ( res = = FR_OK ) { /* A valid entry is found */
get_fileinfo ( dp , fno ) ; /* Get the object information */
res = dir_next ( dp , 0 ) ; /* Increment index for next */
if ( res = = FR_NO_FILE ) res = FR_OK ; /* Ignore end of directory now */
}
FREE_NAMBUF ( ) ;
}
}
LEAVE_FF ( fs , res ) ;
}
# if FF_USE_FIND
/*-----------------------------------------------------------------------*/
/* Find Next File */
/*-----------------------------------------------------------------------*/
FRESULT f_findnext (
DIR * dp , /* Pointer to the open directory object */
FILINFO * fno /* Pointer to the file information structure */
)
{
FRESULT res ;
for ( ; ; ) {
res = f_readdir ( dp , fno ) ; /* Get a directory item */
if ( res ! = FR_OK | | ! fno | | ! fno - > fname [ 0 ] ) break ; /* Terminate if any error or end of directory */
if ( pattern_matching ( dp - > pat , fno - > fname , 0 , 0 ) ) break ; /* Test for the file name */
# if FF_USE_LFN && FF_USE_FIND == 2
if ( pattern_matching ( dp - > pat , fno - > altname , 0 , 0 ) ) break ; /* Test for alternative name if exist */
# endif
}
return res ;
}
/*-----------------------------------------------------------------------*/
/* Find First File */
/*-----------------------------------------------------------------------*/
FRESULT f_findfirst (
DIR * dp , /* Pointer to the blank directory object */
FILINFO * fno , /* Pointer to the file information structure */
const TCHAR * path , /* Pointer to the directory to open */
const TCHAR * pattern /* Pointer to the matching pattern */
)
{
FRESULT res ;
dp - > pat = pattern ; /* Save pointer to pattern string */
res = f_opendir ( dp , path ) ; /* Open the target directory */
if ( res = = FR_OK ) {
res = f_findnext ( dp , fno ) ; /* Find the first item */
}
return res ;
}
# endif /* FF_USE_FIND */
# if FF_FS_MINIMIZE == 0
/*-----------------------------------------------------------------------*/
/* Get File Status */
/*-----------------------------------------------------------------------*/
FRESULT f_stat (
const TCHAR * path , /* Pointer to the file path */
FILINFO * fno /* Pointer to file information to return */
)
{
FRESULT res ;
DIR dj ;
DEF_NAMBUF
/* Get logical drive */
res = find_volume ( & path , & dj . obj . fs , 0 ) ;
if ( res = = FR_OK ) {
INIT_NAMBUF ( dj . obj . fs ) ;
res = follow_path ( & dj , path ) ; /* Follow the file path */
if ( res = = FR_OK ) { /* Follow completed */
if ( dj . fn [ NSFLAG ] & NS_NONAME ) { /* It is origin directory */
res = FR_INVALID_NAME ;
} else { /* Found an object */
if ( fno ) get_fileinfo ( & dj , fno ) ;
}
}
FREE_NAMBUF ( ) ;
}
LEAVE_FF ( dj . obj . fs , res ) ;
}
# if !FF_FS_READONLY
/*-----------------------------------------------------------------------*/
/* Get Number of Free Clusters */
/*-----------------------------------------------------------------------*/
FRESULT f_getfree (
const TCHAR * path , /* Path name of the logical drive number */
DWORD * nclst , /* Pointer to a variable to return number of free clusters */
FATFS * * fatfs /* Pointer to return pointer to corresponding filesystem object */
)
{
FRESULT res ;
FATFS * fs ;
DWORD nfree , clst , sect , stat ;
UINT i ;
FFOBJID obj ;
/* Get logical drive */
res = find_volume ( & path , & fs , 0 ) ;
if ( res = = FR_OK ) {
* fatfs = fs ; /* Return ptr to the fs object */
/* If free_clst is valid, return it without full FAT scan */
if ( fs - > free_clst < = fs - > n_fatent - 2 ) {
* nclst = fs - > free_clst ;
} else {
/* Scan FAT to obtain number of free clusters */
nfree = 0 ;
if ( fs - > fs_type = = FS_FAT12 ) { /* FAT12: Scan bit field FAT entries */
clst = 2 ; obj . fs = fs ;
do {
stat = get_fat ( & obj , clst ) ;
if ( stat = = 0xFFFFFFFF ) { res = FR_DISK_ERR ; break ; }
if ( stat = = 1 ) { res = FR_INT_ERR ; break ; }
if ( stat = = 0 ) nfree + + ;
} while ( + + clst < fs - > n_fatent ) ;
} else {
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) { /* exFAT: Scan allocation bitmap */
BYTE bm ;
UINT b ;
clst = fs - > n_fatent - 2 ; /* Number of clusters */
sect = fs - > database ; /* Assuming bitmap starts at cluster 2 */
i = 0 ; /* Offset in the sector */
do { /* Counts numbuer of bits with zero in the bitmap */
if ( i = = 0 ) {
res = move_window ( fs , sect + + ) ;
if ( res ! = FR_OK ) break ;
}
for ( b = 8 , bm = fs - > win [ i ] ; b & & clst ; b - - , clst - - ) {
if ( ! ( bm & 1 ) ) nfree + + ;
bm > > = 1 ;
}
i = ( i + 1 ) % SS ( fs ) ;
} while ( clst ) ;
} else
# endif
{ /* FAT16/32: Scan WORD/DWORD FAT entries */
clst = fs - > n_fatent ; /* Number of entries */
sect = fs - > fatbase ; /* Top of the FAT */
i = 0 ; /* Offset in the sector */
do { /* Counts numbuer of entries with zero in the FAT */
if ( i = = 0 ) {
res = move_window ( fs , sect + + ) ;
if ( res ! = FR_OK ) break ;
}
if ( fs - > fs_type = = FS_FAT16 ) {
if ( ld_word ( fs - > win + i ) = = 0 ) nfree + + ;
i + = 2 ;
} else {
if ( ( ld_dword ( fs - > win + i ) & 0x0FFFFFFF ) = = 0 ) nfree + + ;
i + = 4 ;
}
i % = SS ( fs ) ;
} while ( - - clst ) ;
}
}
* nclst = nfree ; /* Return the free clusters */
fs - > free_clst = nfree ; /* Now free_clst is valid */
fs - > fsi_flag | = 1 ; /* FAT32: FSInfo is to be updated */
}
}
LEAVE_FF ( fs , res ) ;
}
/*-----------------------------------------------------------------------*/
/* Truncate File */
/*-----------------------------------------------------------------------*/
FRESULT f_truncate (
FIL * fp /* Pointer to the file object */
)
{
FRESULT res ;
FATFS * fs ;
DWORD ncl ;
res = validate ( & fp - > obj , & fs ) ; /* Check validity of the file object */
if ( res ! = FR_OK | | ( res = ( FRESULT ) fp - > err ) ! = FR_OK ) LEAVE_FF ( fs , res ) ;
if ( ! ( fp - > flag & FA_WRITE ) ) LEAVE_FF ( fs , FR_DENIED ) ; /* Check access mode */
if ( fp - > fptr < fp - > obj . objsize ) { /* Process when fptr is not on the eof */
if ( fp - > fptr = = 0 ) { /* When set file size to zero, remove entire cluster chain */
res = remove_chain ( & fp - > obj , fp - > obj . sclust , 0 ) ;
fp - > obj . sclust = 0 ;
} else { /* When truncate a part of the file, remove remaining clusters */
ncl = get_fat ( & fp - > obj , fp - > clust ) ;
res = FR_OK ;
if ( ncl = = 0xFFFFFFFF ) res = FR_DISK_ERR ;
if ( ncl = = 1 ) res = FR_INT_ERR ;
if ( res = = FR_OK & & ncl < fs - > n_fatent ) {
res = remove_chain ( & fp - > obj , ncl , fp - > clust ) ;
}
}
fp - > obj . objsize = fp - > fptr ; /* Set file size to current read/write point */
fp - > flag | = FA_MODIFIED ;
# if !FF_FS_TINY
if ( res = = FR_OK & & ( fp - > flag & FA_DIRTY ) ) {
if ( disk_write ( fs - > pdrv , fp - > buf , fp - > sect , 1 ) ! = RES_OK ) {
res = FR_DISK_ERR ;
} else {
fp - > flag & = ( BYTE ) ~ FA_DIRTY ;
}
}
# endif
if ( res ! = FR_OK ) ABORT ( fs , res ) ;
}
LEAVE_FF ( fs , res ) ;
}
/*-----------------------------------------------------------------------*/
/* Delete a File/Directory */
/*-----------------------------------------------------------------------*/
FRESULT f_unlink (
const TCHAR * path /* Pointer to the file or directory path */
)
{
FRESULT res ;
DIR dj , sdj ;
DWORD dclst = 0 ;
FATFS * fs ;
# if FF_FS_EXFAT
FFOBJID obj ;
# endif
DEF_NAMBUF
/* Get logical drive */
res = find_volume ( & path , & fs , FA_WRITE ) ;
if ( res = = FR_OK ) {
dj . obj . fs = fs ;
INIT_NAMBUF ( fs ) ;
res = follow_path ( & dj , path ) ; /* Follow the file path */
if ( FF_FS_RPATH & & res = = FR_OK & & ( dj . fn [ NSFLAG ] & NS_DOT ) ) {
res = FR_INVALID_NAME ; /* Cannot remove dot entry */
}
# if FF_FS_LOCK != 0
if ( res = = FR_OK ) res = chk_lock ( & dj , 2 ) ; /* Check if it is an open object */
# endif
if ( res = = FR_OK ) { /* The object is accessible */
if ( dj . fn [ NSFLAG ] & NS_NONAME ) {
res = FR_INVALID_NAME ; /* Cannot remove the origin directory */
} else {
if ( dj . obj . attr & AM_RDO ) {
res = FR_DENIED ; /* Cannot remove R/O object */
}
}
if ( res = = FR_OK ) {
# if FF_FS_EXFAT
obj . fs = fs ;
if ( fs - > fs_type = = FS_EXFAT ) {
obj . sclust = dclst = ld_dword ( fs - > dirbuf + XDIR_FstClus ) ;
obj . objsize = ld_qword ( fs - > dirbuf + XDIR_FileSize ) ;
obj . stat = fs - > dirbuf [ XDIR_GenFlags ] & 2 ;
} else
# endif
{
dclst = ld_clust ( fs , dj . dir ) ;
}
if ( dj . obj . attr & AM_DIR ) { /* Is it a sub-directory? */
# if FF_FS_RPATH != 0
if ( dclst = = fs - > cdir ) { /* Is it the current directory? */
res = FR_DENIED ;
} else
# endif
{
sdj . obj . fs = fs ; /* Open the sub-directory */
sdj . obj . sclust = dclst ;
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) {
sdj . obj . objsize = obj . objsize ;
sdj . obj . stat = obj . stat ;
}
# endif
res = dir_sdi ( & sdj , 0 ) ;
if ( res = = FR_OK ) {
res = dir_read ( & sdj , 0 ) ; /* Read an item */
if ( res = = FR_OK ) res = FR_DENIED ; /* Not empty? */
if ( res = = FR_NO_FILE ) res = FR_OK ; /* Empty? */
}
}
}
}
if ( res = = FR_OK ) {
res = dir_remove ( & dj ) ; /* Remove the directory entry */
if ( res = = FR_OK & & dclst ) { /* Remove the cluster chain if exist */
# if FF_FS_EXFAT
res = remove_chain ( & obj , dclst , 0 ) ;
# else
res = remove_chain ( & dj . obj , dclst , 0 ) ;
# endif
}
if ( res = = FR_OK ) res = sync_fs ( fs ) ;
}
}
FREE_NAMBUF ( ) ;
}
LEAVE_FF ( fs , res ) ;
}
/*-----------------------------------------------------------------------*/
/* Create a Directory */
/*-----------------------------------------------------------------------*/
FRESULT f_mkdir (
const TCHAR * path /* Pointer to the directory path */
)
{
FRESULT res ;
DIR dj ;
FATFS * fs ;
BYTE * dir ;
DWORD dcl , pcl , tm ;
DEF_NAMBUF
/* Get logical drive */
res = find_volume ( & path , & fs , FA_WRITE ) ;
if ( res = = FR_OK ) {
dj . obj . fs = fs ;
INIT_NAMBUF ( fs ) ;
res = follow_path ( & dj , path ) ; /* Follow the file path */
if ( res = = FR_OK ) res = FR_EXIST ; /* Any object with same name is already existing */
if ( FF_FS_RPATH & & res = = FR_NO_FILE & & ( dj . fn [ NSFLAG ] & NS_DOT ) ) {
res = FR_INVALID_NAME ;
}
if ( res = = FR_NO_FILE ) { /* Can create a new directory */
dcl = create_chain ( & dj . obj , 0 ) ; /* Allocate a cluster for the new directory table */
dj . obj . objsize = ( DWORD ) fs - > csize * SS ( fs ) ;
res = FR_OK ;
if ( dcl = = 0 ) res = FR_DENIED ; /* No space to allocate a new cluster */
if ( dcl = = 1 ) res = FR_INT_ERR ;
if ( dcl = = 0xFFFFFFFF ) res = FR_DISK_ERR ;
if ( res = = FR_OK ) res = sync_window ( fs ) ; /* Flush FAT */
tm = GET_FATTIME ( ) ;
if ( res = = FR_OK ) { /* Initialize the new directory table */
res = dir_clear ( fs , dcl ) ; /* Clean up the new table */
if ( res = = FR_OK & & ( ! FF_FS_EXFAT | | fs - > fs_type ! = FS_EXFAT ) ) { /* Create dot entries (FAT only) */
dir = fs - > win ;
mem_set ( dir + DIR_Name , ' ' , 11 ) ; /* Create "." entry */
dir [ DIR_Name ] = ' . ' ;
dir [ DIR_Attr ] = AM_DIR ;
st_dword ( dir + DIR_ModTime , tm ) ;
st_clust ( fs , dir , dcl ) ;
mem_cpy ( dir + SZDIRE , dir , SZDIRE ) ; /* Create ".." entry */
dir [ SZDIRE + 1 ] = ' . ' ; pcl = dj . obj . sclust ;
st_clust ( fs , dir + SZDIRE , pcl ) ;
fs - > wflag = 1 ;
}
}
if ( res = = FR_OK ) {
res = dir_register ( & dj ) ; /* Register the object to the directoy */
}
if ( res = = FR_OK ) {
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) { /* Initialize directory entry block */
st_dword ( fs - > dirbuf + XDIR_ModTime , tm ) ; /* Created time */
st_dword ( fs - > dirbuf + XDIR_FstClus , dcl ) ; /* Table start cluster */
st_dword ( fs - > dirbuf + XDIR_FileSize , ( DWORD ) dj . obj . objsize ) ; /* File size needs to be valid */
st_dword ( fs - > dirbuf + XDIR_ValidFileSize , ( DWORD ) dj . obj . objsize ) ;
fs - > dirbuf [ XDIR_GenFlags ] = 3 ; /* Initialize the object flag */
fs - > dirbuf [ XDIR_Attr ] = AM_DIR ; /* Attribute */
res = store_xdir ( & dj ) ;
} else
# endif
{
dir = dj . dir ;
st_dword ( dir + DIR_ModTime , tm ) ; /* Created time */
st_clust ( fs , dir , dcl ) ; /* Table start cluster */
dir [ DIR_Attr ] = AM_DIR ; /* Attribute */
fs - > wflag = 1 ;
}
if ( res = = FR_OK ) {
res = sync_fs ( fs ) ;
}
} else {
remove_chain ( & dj . obj , dcl , 0 ) ; /* Could not register, remove cluster chain */
}
}
FREE_NAMBUF ( ) ;
}
LEAVE_FF ( fs , res ) ;
}
/*-----------------------------------------------------------------------*/
/* Rename a File/Directory */
/*-----------------------------------------------------------------------*/
FRESULT f_rename (
const TCHAR * path_old , /* Pointer to the object name to be renamed */
const TCHAR * path_new /* Pointer to the new name */
)
{
FRESULT res ;
DIR djo , djn ;
FATFS * fs ;
BYTE buf [ FF_FS_EXFAT ? SZDIRE * 2 : SZDIRE ] , * dir ;
DWORD dw ;
DEF_NAMBUF
get_ldnumber ( & path_new ) ; /* Snip the drive number of new name off */
res = find_volume ( & path_old , & fs , FA_WRITE ) ; /* Get logical drive of the old object */
if ( res = = FR_OK ) {
djo . obj . fs = fs ;
INIT_NAMBUF ( fs ) ;
res = follow_path ( & djo , path_old ) ; /* Check old object */
if ( res = = FR_OK & & ( djo . fn [ NSFLAG ] & ( NS_DOT | NS_NONAME ) ) ) res = FR_INVALID_NAME ; /* Check validity of name */
# if FF_FS_LOCK != 0
if ( res = = FR_OK ) {
res = chk_lock ( & djo , 2 ) ;
}
# endif
if ( res = = FR_OK ) { /* Object to be renamed is found */
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) { /* At exFAT volume */
BYTE nf , nn ;
WORD nh ;
mem_cpy ( buf , fs - > dirbuf , SZDIRE * 2 ) ; /* Save 85+C0 entry of old object */
mem_cpy ( & djn , & djo , sizeof djo ) ;
res = follow_path ( & djn , path_new ) ; /* Make sure if new object name is not in use */
if ( res = = FR_OK ) { /* Is new name already in use by any other object? */
res = ( djn . obj . sclust = = djo . obj . sclust & & djn . dptr = = djo . dptr ) ? FR_NO_FILE : FR_EXIST ;
}
if ( res = = FR_NO_FILE ) { /* It is a valid path and no name collision */
res = dir_register ( & djn ) ; /* Register the new entry */
if ( res = = FR_OK ) {
nf = fs - > dirbuf [ XDIR_NumSec ] ; nn = fs - > dirbuf [ XDIR_NumName ] ;
nh = ld_word ( fs - > dirbuf + XDIR_NameHash ) ;
mem_cpy ( fs - > dirbuf , buf , SZDIRE * 2 ) ; /* Restore 85+C0 entry */
fs - > dirbuf [ XDIR_NumSec ] = nf ; fs - > dirbuf [ XDIR_NumName ] = nn ;
st_word ( fs - > dirbuf + XDIR_NameHash , nh ) ;
if ( ! ( fs - > dirbuf [ XDIR_Attr ] & AM_DIR ) ) fs - > dirbuf [ XDIR_Attr ] | = AM_ARC ; /* Set archive attribute if it is a file */
/* Start of critical section where an interruption can cause a cross-link */
res = store_xdir ( & djn ) ;
}
}
} else
# endif
{ /* At FAT/FAT32 volume */
mem_cpy ( buf , djo . dir , SZDIRE ) ; /* Save directory entry of the object */
mem_cpy ( & djn , & djo , sizeof ( DIR ) ) ; /* Duplicate the directory object */
res = follow_path ( & djn , path_new ) ; /* Make sure if new object name is not in use */
if ( res = = FR_OK ) { /* Is new name already in use by any other object? */
res = ( djn . obj . sclust = = djo . obj . sclust & & djn . dptr = = djo . dptr ) ? FR_NO_FILE : FR_EXIST ;
}
if ( res = = FR_NO_FILE ) { /* It is a valid path and no name collision */
res = dir_register ( & djn ) ; /* Register the new entry */
if ( res = = FR_OK ) {
dir = djn . dir ; /* Copy directory entry of the object except name */
mem_cpy ( dir + 13 , buf + 13 , SZDIRE - 13 ) ;
dir [ DIR_Attr ] = buf [ DIR_Attr ] ;
if ( ! ( dir [ DIR_Attr ] & AM_DIR ) ) dir [ DIR_Attr ] | = AM_ARC ; /* Set archive attribute if it is a file */
fs - > wflag = 1 ;
if ( ( dir [ DIR_Attr ] & AM_DIR ) & & djo . obj . sclust ! = djn . obj . sclust ) { /* Update .. entry in the sub-directory if needed */
dw = clst2sect ( fs , ld_clust ( fs , dir ) ) ;
if ( dw = = 0 ) {
res = FR_INT_ERR ;
} else {
/* Start of critical section where an interruption can cause a cross-link */
res = move_window ( fs , dw ) ;
dir = fs - > win + SZDIRE * 1 ; /* Ptr to .. entry */
if ( res = = FR_OK & & dir [ 1 ] = = ' . ' ) {
st_clust ( fs , dir , djn . obj . sclust ) ;
fs - > wflag = 1 ;
}
}
}
}
}
}
if ( res = = FR_OK ) {
res = dir_remove ( & djo ) ; /* Remove old entry */
if ( res = = FR_OK ) {
res = sync_fs ( fs ) ;
}
}
/* End of the critical section */
}
FREE_NAMBUF ( ) ;
}
LEAVE_FF ( fs , res ) ;
}
# endif /* !FF_FS_READONLY */
# endif /* FF_FS_MINIMIZE == 0 */
# endif /* FF_FS_MINIMIZE <= 1 */
# endif /* FF_FS_MINIMIZE <= 2 */
# if FF_USE_CHMOD && !FF_FS_READONLY
/*-----------------------------------------------------------------------*/
/* Change Attribute */
/*-----------------------------------------------------------------------*/
FRESULT f_chmod (
const TCHAR * path , /* Pointer to the file path */
BYTE attr , /* Attribute bits */
BYTE mask /* Attribute mask to change */
)
{
FRESULT res ;
DIR dj ;
FATFS * fs ;
DEF_NAMBUF
res = find_volume ( & path , & fs , FA_WRITE ) ; /* Get logical drive */
if ( res = = FR_OK ) {
dj . obj . fs = fs ;
INIT_NAMBUF ( fs ) ;
res = follow_path ( & dj , path ) ; /* Follow the file path */
if ( res = = FR_OK & & ( dj . fn [ NSFLAG ] & ( NS_DOT | NS_NONAME ) ) ) res = FR_INVALID_NAME ; /* Check object validity */
if ( res = = FR_OK ) {
mask & = AM_RDO | AM_HID | AM_SYS | AM_ARC ; /* Valid attribute mask */
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) {
fs - > dirbuf [ XDIR_Attr ] = ( attr & mask ) | ( fs - > dirbuf [ XDIR_Attr ] & ( BYTE ) ~ mask ) ; /* Apply attribute change */
res = store_xdir ( & dj ) ;
} else
# endif
{
dj . dir [ DIR_Attr ] = ( attr & mask ) | ( dj . dir [ DIR_Attr ] & ( BYTE ) ~ mask ) ; /* Apply attribute change */
fs - > wflag = 1 ;
}
if ( res = = FR_OK ) {
res = sync_fs ( fs ) ;
}
}
FREE_NAMBUF ( ) ;
}
LEAVE_FF ( fs , res ) ;
}
/*-----------------------------------------------------------------------*/
/* Change Timestamp */
/*-----------------------------------------------------------------------*/
FRESULT f_utime (
const TCHAR * path , /* Pointer to the file/directory name */
const FILINFO * fno /* Pointer to the timestamp to be set */
)
{
FRESULT res ;
DIR dj ;
FATFS * fs ;
DEF_NAMBUF
res = find_volume ( & path , & fs , FA_WRITE ) ; /* Get logical drive */
if ( res = = FR_OK ) {
dj . obj . fs = fs ;
INIT_NAMBUF ( fs ) ;
res = follow_path ( & dj , path ) ; /* Follow the file path */
if ( res = = FR_OK & & ( dj . fn [ NSFLAG ] & ( NS_DOT | NS_NONAME ) ) ) res = FR_INVALID_NAME ; /* Check object validity */
if ( res = = FR_OK ) {
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) {
st_dword ( fs - > dirbuf + XDIR_ModTime , ( DWORD ) fno - > fdate < < 16 | fno - > ftime ) ;
res = store_xdir ( & dj ) ;
} else
# endif
{
st_dword ( dj . dir + DIR_ModTime , ( DWORD ) fno - > fdate < < 16 | fno - > ftime ) ;
fs - > wflag = 1 ;
}
if ( res = = FR_OK ) {
res = sync_fs ( fs ) ;
}
}
FREE_NAMBUF ( ) ;
}
LEAVE_FF ( fs , res ) ;
}
# endif /* FF_USE_CHMOD && !FF_FS_READONLY */
# if FF_USE_LABEL
/*-----------------------------------------------------------------------*/
/* Get Volume Label */
/*-----------------------------------------------------------------------*/
FRESULT f_getlabel (
const TCHAR * path , /* Path name of the logical drive number */
TCHAR * label , /* Pointer to a buffer to store the volume label */
DWORD * vsn /* Pointer to a variable to store the volume serial number */
)
{
FRESULT res ;
DIR dj ;
FATFS * fs ;
UINT si , di ;
# if (FF_LFN_UNICODE && FF_USE_LFN) || FF_FS_EXFAT
WCHAR w ;
# endif
/* Get logical drive */
res = find_volume ( & path , & fs , 0 ) ;
/* Get volume label */
if ( res = = FR_OK & & label ) {
dj . obj . fs = fs ; dj . obj . sclust = 0 ; /* Open root directory */
res = dir_sdi ( & dj , 0 ) ;
if ( res = = FR_OK ) {
res = dir_read ( & dj , 1 ) ; /* Find a volume label entry */
if ( res = = FR_OK ) {
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) {
for ( si = di = 0 ; si < dj . dir [ XDIR_NumLabel ] ; si + + ) { /* Extract volume label from 83 entry */
w = ld_word ( dj . dir + XDIR_Label + si * 2 ) ;
# if !FF_LFN_UNICODE /* ANSI/OEM API */
w = ff_uni2oem ( w , CODEPAGE ) ; /* Unicode -> OEM */
if ( w = = 0 ) w = ' ? ' ; /* Replace wrong char with '?' */
if ( w > = 0x100 ) label [ di + + ] = ( char ) ( w > > 8 ) ;
# endif
label [ di + + ] = ( TCHAR ) w ;
}
label [ di ] = 0 ;
} else
# endif
{
si = di = 0 ; /* Extract volume label from AM_VOL entry with code comversion */
do {
# if FF_LFN_UNICODE && FF_USE_LFN /* Unicode API */
w = ( si < 11 ) ? dj . dir [ si + + ] : ' ' ;
if ( dbc_1st ( ( BYTE ) w ) & & si < 11 & & dbc_2nd ( dj . dir [ si ] ) ) {
w = w < < 8 | dj . dir [ si + + ] ;
}
label [ di + + ] = ff_oem2uni ( w , CODEPAGE ) ; /* OEM -> Unicode */
# else /* ANSI/OEM API */
label [ di + + ] = dj . dir [ si + + ] ;
# endif
} while ( di < 11 ) ;
do { /* Truncate trailing spaces */
label [ di ] = 0 ;
if ( di = = 0 ) break ;
} while ( label [ - - di ] = = ' ' ) ;
}
}
}
if ( res = = FR_NO_FILE ) { /* No label entry and return nul string */
label [ 0 ] = 0 ;
res = FR_OK ;
}
}
/* Get volume serial number */
if ( res = = FR_OK & & vsn ) {
res = move_window ( fs , fs - > volbase ) ;
if ( res = = FR_OK ) {
switch ( fs - > fs_type ) {
case FS_EXFAT :
di = BPB_VolIDEx ; break ;
case FS_FAT32 :
di = BS_VolID32 ; break ;
default :
di = BS_VolID ;
}
* vsn = ld_dword ( fs - > win + di ) ;
}
}
LEAVE_FF ( fs , res ) ;
}
# if !FF_FS_READONLY
/*-----------------------------------------------------------------------*/
/* Set Volume Label */
/*-----------------------------------------------------------------------*/
FRESULT f_setlabel (
const TCHAR * label /* Pointer to the volume label to set */
)
{
FRESULT res ;
DIR dj ;
FATFS * fs ;
BYTE dirvn [ 22 ] ;
UINT i , j , slen ;
WCHAR w ;
static const char badchr [ ] = " \" *+,.:;<=> \ ?[]| \x7F " ;
/* Get logical drive */
res = find_volume ( & label , & fs , FA_WRITE ) ;
if ( res ! = FR_OK ) LEAVE_FF ( fs , res ) ;
dj . obj . fs = fs ;
/* Get length of given volume label */
for ( slen = 0 ; ( UINT ) label [ slen ] > = ' ' ; slen + + ) ; /* Get name length */
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) { /* On the exFAT volume */
for ( i = j = 0 ; i < slen ; ) { /* Create volume label in directory form */
w = label [ i + + ] ;
# if !FF_LFN_UNICODE /* ANSI/OEM API */
if ( dbc_1st ( ( BYTE ) w ) ) {
w = ( i < slen & & dbc_2nd ( ( BYTE ) label [ i ] ) ) ? w < < 8 | ( BYTE ) label [ i + + ] : 0 ;
}
w = ff_oem2uni ( w , CODEPAGE ) ;
# endif
if ( w = = 0 | | chk_chr ( badchr , w ) | | j = = 22 ) { /* Check validity check validity of the volume label */
LEAVE_FF ( fs , FR_INVALID_NAME ) ;
}
st_word ( dirvn + j , w ) ; j + = 2 ;
}
slen = j ;
} else
# endif
{ /* On the FAT/FAT32 volume */
for ( ; slen & & label [ slen - 1 ] = = ' ' ; slen - - ) ; /* Remove trailing spaces */
if ( slen ! = 0 ) { /* Is there a volume label to be set? */
dirvn [ 0 ] = 0 ; i = j = 0 ; /* Create volume label in directory form */
do {
# if FF_LFN_UNICODE && FF_USE_LFN /* Unicode API */
w = ff_uni2oem ( ff_wtoupper ( label [ i + + ] ) , CODEPAGE ) ;
# else /* ANSI/OEM API */
w = ( BYTE ) label [ i + + ] ;
if ( dbc_1st ( ( BYTE ) w ) ) {
w = ( j < 10 & & i < slen & & dbc_2nd ( ( BYTE ) label [ i ] ) ) ? w < < 8 | ( BYTE ) label [ i + + ] : 0 ;
}
# if FF_USE_LFN
w = ff_uni2oem ( ff_wtoupper ( ff_oem2uni ( w , CODEPAGE ) ) , CODEPAGE ) ;
# else
if ( IsLower ( w ) ) w - = 0x20 ; /* To upper ASCII characters */
# if FF_CODE_PAGE == 0
if ( ExCvt & & w > = 0x80 ) w = ExCvt [ w - 0x80 ] ; /* To upper extended characters (SBCS cfg) */
# elif FF_CODE_PAGE < 900
if ( w > = 0x80 ) w = ExCvt [ w - 0x80 ] ; /* To upper extended characters (SBCS cfg) */
# endif
# endif
# endif
if ( w = = 0 | | chk_chr ( badchr , w ) | | j > = ( UINT ) ( ( w > = 0x100 ) ? 10 : 11 ) ) { /* Reject invalid characters for volume label */
LEAVE_FF ( fs , FR_INVALID_NAME ) ;
}
if ( w > = 0x100 ) dirvn [ j + + ] = ( BYTE ) ( w > > 8 ) ;
dirvn [ j + + ] = ( BYTE ) w ;
} while ( i < slen ) ;
while ( j < 11 ) dirvn [ j + + ] = ' ' ; /* Fill remaining name field */
if ( dirvn [ 0 ] = = DDEM ) LEAVE_FF ( fs , FR_INVALID_NAME ) ; /* Reject illegal name (heading DDEM) */
}
}
/* Set volume label */
dj . obj . sclust = 0 ; /* Open root directory */
res = dir_sdi ( & dj , 0 ) ;
if ( res = = FR_OK ) {
res = dir_read ( & dj , 1 ) ; /* Get volume label entry */
if ( res = = FR_OK ) {
if ( FF_FS_EXFAT & & fs - > fs_type = = FS_EXFAT ) {
dj . dir [ XDIR_NumLabel ] = ( BYTE ) ( slen / 2 ) ; /* Change the volume label */
mem_cpy ( dj . dir + XDIR_Label , dirvn , slen ) ;
} else {
if ( slen ! = 0 ) {
mem_cpy ( dj . dir , dirvn , 11 ) ; /* Change the volume label */
} else {
dj . dir [ DIR_Name ] = DDEM ; /* Remove the volume label */
}
}
fs - > wflag = 1 ;
res = sync_fs ( fs ) ;
} else { /* No volume label entry or an error */
if ( res = = FR_NO_FILE ) {
res = FR_OK ;
if ( slen ! = 0 ) { /* Create a volume label entry */
res = dir_alloc ( & dj , 1 ) ; /* Allocate an entry */
if ( res = = FR_OK ) {
mem_set ( dj . dir , 0 , SZDIRE ) ; /* Clear the entry */
if ( FF_FS_EXFAT & & fs - > fs_type = = FS_EXFAT ) {
dj . dir [ XDIR_Type ] = 0x83 ; /* Create 83 entry */
dj . dir [ XDIR_NumLabel ] = ( BYTE ) ( slen / 2 ) ;
mem_cpy ( dj . dir + XDIR_Label , dirvn , slen ) ;
} else {
dj . dir [ DIR_Attr ] = AM_VOL ; /* Create volume label entry */
mem_cpy ( dj . dir , dirvn , 11 ) ;
}
fs - > wflag = 1 ;
res = sync_fs ( fs ) ;
}
}
}
}
}
LEAVE_FF ( fs , res ) ;
}
# endif /* !FF_FS_READONLY */
# endif /* FF_USE_LABEL */
# if FF_USE_EXPAND && !FF_FS_READONLY
/*-----------------------------------------------------------------------*/
/* Allocate a Contiguous Blocks to the File */
/*-----------------------------------------------------------------------*/
FRESULT f_expand (
FIL * fp , /* Pointer to the file object */
FSIZE_t fsz , /* File size to be expanded to */
BYTE opt /* Operation mode 0:Find and prepare or 1:Find and allocate */
)
{
FRESULT res ;
FATFS * fs ;
DWORD n , clst , stcl , scl , ncl , tcl , lclst ;
res = validate ( & fp - > obj , & fs ) ; /* Check validity of the file object */
if ( res ! = FR_OK | | ( res = ( FRESULT ) fp - > err ) ! = FR_OK ) LEAVE_FF ( fs , res ) ;
if ( fsz = = 0 | | fp - > obj . objsize ! = 0 | | ! ( fp - > flag & FA_WRITE ) ) LEAVE_FF ( fs , FR_DENIED ) ;
# if FF_FS_EXFAT
if ( fs - > fs_type ! = FS_EXFAT & & fsz > = 0x100000000 ) LEAVE_FF ( fs , FR_DENIED ) ; /* Check if in size limit */
# endif
n = ( DWORD ) fs - > csize * SS ( fs ) ; /* Cluster size */
tcl = ( DWORD ) ( fsz / n ) + ( ( fsz & ( n - 1 ) ) ? 1 : 0 ) ; /* Number of clusters required */
stcl = fs - > last_clst ; lclst = 0 ;
if ( stcl < 2 | | stcl > = fs - > n_fatent ) stcl = 2 ;
# if FF_FS_EXFAT
if ( fs - > fs_type = = FS_EXFAT ) {
scl = find_bitmap ( fs , stcl , tcl ) ; /* Find a contiguous cluster block */
if ( scl = = 0 ) res = FR_DENIED ; /* No contiguous cluster block was found */
if ( scl = = 0xFFFFFFFF ) res = FR_DISK_ERR ;
if ( res = = FR_OK ) { /* A contiguous free area is found */
if ( opt ) { /* Allocate it now */
res = change_bitmap ( fs , scl , tcl , 1 ) ; /* Mark the cluster block 'in use' */
lclst = scl + tcl - 1 ;
} else { /* Set it as suggested point for next allocation */
lclst = scl - 1 ;
}
}
} else
# endif
{
scl = clst = stcl ; ncl = 0 ;
for ( ; ; ) { /* Find a contiguous cluster block */
n = get_fat ( & fp - > obj , clst ) ;
if ( + + clst > = fs - > n_fatent ) clst = 2 ;
if ( n = = 1 ) { res = FR_INT_ERR ; break ; }
if ( n = = 0xFFFFFFFF ) { res = FR_DISK_ERR ; break ; }
if ( n = = 0 ) { /* Is it a free cluster? */
if ( + + ncl = = tcl ) break ; /* Break if a contiguous cluster block is found */
} else {
scl = clst ; ncl = 0 ; /* Not a free cluster */
}
if ( clst = = stcl ) { res = FR_DENIED ; break ; } /* No contiguous cluster? */
}
if ( res = = FR_OK ) { /* A contiguous free area is found */
if ( opt ) { /* Allocate it now */
for ( clst = scl , n = tcl ; n ; clst + + , n - - ) { /* Create a cluster chain on the FAT */
res = put_fat ( fs , clst , ( n = = 1 ) ? 0xFFFFFFFF : clst + 1 ) ;
if ( res ! = FR_OK ) break ;
lclst = clst ;
}
} else { /* Set it as suggested point for next allocation */
lclst = scl - 1 ;
}
}
}
if ( res = = FR_OK ) {
fs - > last_clst = lclst ; /* Set suggested start cluster to start next */
if ( opt ) { /* Is it allocated now? */
fp - > obj . sclust = scl ; /* Update object allocation information */
fp - > obj . objsize = fsz ;
if ( FF_FS_EXFAT ) fp - > obj . stat = 2 ; /* Set status 'contiguous chain' */
fp - > flag | = FA_MODIFIED ;
if ( fs - > free_clst < = fs - > n_fatent - 2 ) { /* Update FSINFO */
fs - > free_clst - = tcl ;
fs - > fsi_flag | = 1 ;
}
}
}
LEAVE_FF ( fs , res ) ;
}
# endif /* FF_USE_EXPAND && !FF_FS_READONLY */
# if FF_USE_FORWARD
/*-----------------------------------------------------------------------*/
/* Forward Data to the Stream Directly */
/*-----------------------------------------------------------------------*/
FRESULT f_forward (
FIL * fp , /* Pointer to the file object */
UINT ( * func ) ( const BYTE * , UINT ) , /* Pointer to the streaming function */
UINT btf , /* Number of bytes to forward */
UINT * bf /* Pointer to number of bytes forwarded */
)
{
FRESULT res ;
FATFS * fs ;
DWORD clst , sect ;
FSIZE_t remain ;
UINT rcnt , csect ;
BYTE * dbuf ;
* bf = 0 ; /* Clear transfer byte counter */
res = validate ( & fp - > obj , & fs ) ; /* Check validity of the file object */
if ( res ! = FR_OK | | ( res = ( FRESULT ) fp - > err ) ! = FR_OK ) LEAVE_FF ( fs , res ) ;
if ( ! ( fp - > flag & FA_READ ) ) LEAVE_FF ( fs , FR_DENIED ) ; /* Check access mode */
remain = fp - > obj . objsize - fp - > fptr ;
if ( btf > remain ) btf = ( UINT ) remain ; /* Truncate btf by remaining bytes */
for ( ; btf & & ( * func ) ( 0 , 0 ) ; /* Repeat until all data transferred or stream goes busy */
fp - > fptr + = rcnt , * bf + = rcnt , btf - = rcnt ) {
csect = ( UINT ) ( fp - > fptr / SS ( fs ) & ( fs - > csize - 1 ) ) ; /* Sector offset in the cluster */
if ( fp - > fptr % SS ( fs ) = = 0 ) { /* On the sector boundary? */
if ( csect = = 0 ) { /* On the cluster boundary? */
clst = ( fp - > fptr = = 0 ) ? /* On the top of the file? */
fp - > obj . sclust : get_fat ( & fp - > obj , fp - > clust ) ;
if ( clst < = 1 ) ABORT ( fs , FR_INT_ERR ) ;
if ( clst = = 0xFFFFFFFF ) ABORT ( fs , FR_DISK_ERR ) ;
fp - > clust = clst ; /* Update current cluster */
}
}
sect = clst2sect ( fs , fp - > clust ) ; /* Get current data sector */
if ( sect = = 0 ) ABORT ( fs , FR_INT_ERR ) ;
sect + = csect ;
# if FF_FS_TINY
if ( move_window ( fs , sect ) ! = FR_OK ) ABORT ( fs , FR_DISK_ERR ) ; /* Move sector window to the file data */
dbuf = fs - > win ;
# else
if ( fp - > sect ! = sect ) { /* Fill sector cache with file data */
# if !FF_FS_READONLY
if ( fp - > flag & FA_DIRTY ) { /* Write-back dirty sector cache */
if ( disk_write ( fs - > pdrv , fp - > buf , fp - > sect , 1 ) ! = RES_OK ) ABORT ( fs , FR_DISK_ERR ) ;
fp - > flag & = ( BYTE ) ~ FA_DIRTY ;
}
# endif
if ( disk_read ( fs - > pdrv , fp - > buf , sect , 1 ) ! = RES_OK ) ABORT ( fs , FR_DISK_ERR ) ;
}
dbuf = fp - > buf ;
# endif
fp - > sect = sect ;
rcnt = SS ( fs ) - ( UINT ) fp - > fptr % SS ( fs ) ; /* Number of bytes left in the sector */
if ( rcnt > btf ) rcnt = btf ; /* Clip it by btr if needed */
rcnt = ( * func ) ( dbuf + ( ( UINT ) fp - > fptr % SS ( fs ) ) , rcnt ) ; /* Forward the file data */
if ( rcnt = = 0 ) ABORT ( fs , FR_INT_ERR ) ;
}
LEAVE_FF ( fs , FR_OK ) ;
}
# endif /* FF_USE_FORWARD */
# if FF_USE_MKFS && !FF_FS_READONLY
/*-----------------------------------------------------------------------*/
/* Create an FAT/exFAT volume */
/*-----------------------------------------------------------------------*/
FRESULT f_mkfs (
const TCHAR * path , /* Logical drive number */
BYTE opt , /* Format option */
DWORD au , /* Size of allocation unit (cluster) [byte] */
void * work , /* Pointer to working buffer */
UINT len /* Size of working buffer */
)
{
const UINT n_fats = 1 ; /* Number of FATs for FAT/FAT32 volume (1 or 2) */
const UINT n_rootdir = 512 ; /* Number of root directory entries for FAT volume */
static const WORD cst [ ] = { 1 , 4 , 16 , 64 , 256 , 512 , 0 } ; /* Cluster size boundary for FAT volume (4Ks unit) */
static const WORD cst32 [ ] = { 1 , 2 , 4 , 8 , 16 , 32 , 0 } ; /* Cluster size boundary for FAT32 volume (128Ks unit) */
BYTE fmt , sys , * buf , * pte , pdrv , part ;
WORD ss ;
DWORD szb_buf , sz_buf , sz_blk , n_clst , pau , sect , nsect , n ;
DWORD b_vol , b_fat , b_data ; /* Base LBA for volume, fat, data */
DWORD sz_vol , sz_rsv , sz_fat , sz_dir ; /* Size for volume, fat, dir, data */
UINT i ;
int vol ;
DSTATUS stat ;
# if FF_USE_TRIM || FF_FS_EXFAT
DWORD tbl [ 3 ] ;
# endif
/* Check mounted drive and clear work area */
vol = get_ldnumber ( & path ) ; /* Get target logical drive */
if ( vol < 0 ) return FR_INVALID_DRIVE ;
if ( FatFs [ vol ] ) FatFs [ vol ] - > fs_type = 0 ; /* Clear the volume */
pdrv = LD2PD ( vol ) ; /* Physical drive */
part = LD2PT ( vol ) ; /* Partition (0:create as new, 1-4:get from partition table) */
/* Check physical drive status */
stat = disk_initialize ( pdrv ) ;
if ( stat & STA_NOINIT ) return FR_NOT_READY ;
if ( stat & STA_PROTECT ) return FR_WRITE_PROTECTED ;
if ( disk_ioctl ( pdrv , GET_BLOCK_SIZE , & sz_blk ) ! = RES_OK | | ! sz_blk | | sz_blk > 32768 | | ( sz_blk & ( sz_blk - 1 ) ) ) sz_blk = 1 ; /* Erase block to align data area */
# if FF_MAX_SS != FF_MIN_SS /* Get sector size of the medium if variable sector size cfg. */
if ( disk_ioctl ( pdrv , GET_SECTOR_SIZE , & ss ) ! = RES_OK ) return FR_DISK_ERR ;
if ( ss > FF_MAX_SS | | ss < FF_MIN_SS | | ( ss & ( ss - 1 ) ) ) return FR_DISK_ERR ;
# else
ss = FF_MAX_SS ;
# endif
if ( ( au ! = 0 & & au < ss ) | | au > 0x1000000 | | ( au & ( au - 1 ) ) ) return FR_INVALID_PARAMETER ; /* Check if au is valid */
au / = ss ; /* Cluster size in unit of sector */
/* Get working buffer */
buf = ( BYTE * ) work ; /* Working buffer */
sz_buf = len / ss ; /* Size of working buffer (sector) */
szb_buf = sz_buf * ss ; /* Size of working buffer (byte) */
if ( szb_buf = = 0 ) return FR_MKFS_ABORTED ;
/* Determine where the volume to be located (b_vol, sz_vol) */
if ( FF_MULTI_PARTITION & & part ! = 0 ) {
/* Get partition information from partition table in the MBR */
if ( disk_read ( pdrv , buf , 0 , 1 ) ! = RES_OK ) return FR_DISK_ERR ; /* Load MBR */
if ( ld_word ( buf + BS_55AA ) ! = 0xAA55 ) return FR_MKFS_ABORTED ; /* Check if MBR is valid */
pte = buf + ( MBR_Table + ( part - 1 ) * SZ_PTE ) ;
if ( ! pte [ PTE_System ] ) return FR_MKFS_ABORTED ; /* No partition? */
b_vol = ld_dword ( pte + PTE_StLba ) ; /* Get volume start sector */
sz_vol = ld_dword ( pte + PTE_SizLba ) ; /* Get volume size */
} else {
/* Create a single-partition in this function */
if ( disk_ioctl ( pdrv , GET_SECTOR_COUNT , & sz_vol ) ! = RES_OK ) return FR_DISK_ERR ;
b_vol = ( opt & FM_SFD ) ? 0 : 63 ; /* Volume start sector */
if ( sz_vol < b_vol ) return FR_MKFS_ABORTED ;
sz_vol - = b_vol ; /* Volume size */
}
if ( sz_vol < 128 ) return FR_MKFS_ABORTED ; /* Check if volume size is >=128s */
/* Pre-determine the FAT type */
do {
if ( FF_FS_EXFAT & & ( opt & FM_EXFAT ) ) { /* exFAT possible? */
if ( ( opt & FM_ANY ) = = FM_EXFAT | | sz_vol > = 0x4000000 | | au > 128 ) { /* exFAT only, vol >= 64Ms or au > 128s ? */
fmt = FS_EXFAT ; break ;
}
}
if ( au > 128 ) return FR_INVALID_PARAMETER ; /* Too large au for FAT/FAT32 */
if ( opt & FM_FAT32 ) { /* FAT32 possible? */
if ( ( opt & FM_ANY ) = = FM_FAT32 | | ! ( opt & FM_FAT ) ) { /* FAT32 only or no-FAT? */
fmt = FS_FAT32 ; break ;
}
}
if ( ! ( opt & FM_FAT ) ) return FR_INVALID_PARAMETER ; /* no-FAT? */
fmt = FS_FAT16 ;
} while ( 0 ) ;
# if FF_FS_EXFAT
if ( fmt = = FS_EXFAT ) { /* Create an exFAT volume */
DWORD szb_bit , szb_case , sum , nb , cl ;
WCHAR ch , si ;
UINT j , st ;
BYTE b ;
if ( sz_vol < 0x1000 ) return FR_MKFS_ABORTED ; /* Too small volume? */
# if FF_USE_TRIM
tbl [ 0 ] = b_vol ; tbl [ 1 ] = b_vol + sz_vol - 1 ; /* Inform the device the volume area may be erased */
disk_ioctl ( pdrv , CTRL_TRIM , tbl ) ;
# endif
/* Determine FAT location, data location and number of clusters */
if ( au = = 0 ) { /* au auto-selection */
au = 8 ;
if ( sz_vol > = 0x80000 ) au = 64 ; /* >= 512Ks */
if ( sz_vol > = 0x4000000 ) au = 256 ; /* >= 64Ms */
}
b_fat = b_vol + 32 ; /* FAT start at offset 32 */
sz_fat = ( ( sz_vol / au + 2 ) * 4 + ss - 1 ) / ss ; /* Number of FAT sectors */
b_data = ( b_fat + sz_fat + sz_blk - 1 ) & ~ ( sz_blk - 1 ) ; /* Align data area to the erase block boundary */
if ( b_data > = sz_vol / 2 ) return FR_MKFS_ABORTED ; /* Too small volume? */
n_clst = ( sz_vol - ( b_data - b_vol ) ) / au ; /* Number of clusters */
if ( n_clst < 16 ) return FR_MKFS_ABORTED ; /* Too few clusters? */
if ( n_clst > MAX_EXFAT ) return FR_MKFS_ABORTED ; /* Too many clusters? */
szb_bit = ( n_clst + 7 ) / 8 ; /* Size of allocation bitmap */
tbl [ 0 ] = ( szb_bit + au * ss - 1 ) / ( au * ss ) ; /* Number of allocation bitmap clusters */
/* Create a compressed up-case table */
sect = b_data + au * tbl [ 0 ] ; /* Table start sector */
sum = 0 ; /* Table checksum to be stored in the 82 entry */
st = si = i = j = szb_case = 0 ;
do {
switch ( st ) {
case 0 :
ch = ff_wtoupper ( si ) ; /* Get an up-case char */
if ( ch ! = si ) {
si + + ; break ; /* Store the up-case char if exist */
}
for ( j = 1 ; ( WCHAR ) ( si + j ) & & ( WCHAR ) ( si + j ) = = ff_wtoupper ( ( WCHAR ) ( si + j ) ) ; j + + ) ; /* Get run length of no-case block */
if ( j > = 128 ) {
ch = 0xFFFF ; st = 2 ; break ; /* Compress the no-case block if run is >= 128 */
}
st = 1 ; /* Do not compress short run */
/* go to next case */
case 1 :
ch = si + + ; /* Fill the short run */
if ( - - j = = 0 ) st = 0 ;
break ;
default :
ch = ( WCHAR ) j ; si + = j ; /* Number of chars to skip */
st = 0 ;
}
sum = xsum32 ( buf [ i + 0 ] = ( BYTE ) ch , sum ) ; /* Put it into the write buffer */
sum = xsum32 ( buf [ i + 1 ] = ( BYTE ) ( ch > > 8 ) , sum ) ;
i + = 2 ; szb_case + = 2 ;
if ( si = = 0 | | i = = szb_buf ) { /* Write buffered data when buffer full or end of process */
n = ( i + ss - 1 ) / ss ;
if ( disk_write ( pdrv , buf , sect , n ) ! = RES_OK ) return FR_DISK_ERR ;
sect + = n ; i = 0 ;
}
} while ( si ) ;
tbl [ 1 ] = ( szb_case + au * ss - 1 ) / ( au * ss ) ; /* Number of up-case table clusters */
tbl [ 2 ] = 1 ; /* Number of root dir clusters */
/* Initialize the allocation bitmap */
sect = b_data ; nsect = ( szb_bit + ss - 1 ) / ss ; /* Start of bitmap and number of sectors */
nb = tbl [ 0 ] + tbl [ 1 ] + tbl [ 2 ] ; /* Number of clusters in-use by system */
do {
mem_set ( buf , 0 , szb_buf ) ;
for ( i = 0 ; nb > = 8 & & i < szb_buf ; buf [ i + + ] = 0xFF , nb - = 8 ) ;
for ( b = 1 ; nb ! = 0 & & i < szb_buf ; buf [ i ] | = b , b < < = 1 , nb - - ) ;
n = ( nsect > sz_buf ) ? sz_buf : nsect ; /* Write the buffered data */
if ( disk_write ( pdrv , buf , sect , n ) ! = RES_OK ) return FR_DISK_ERR ;
sect + = n ; nsect - = n ;
} while ( nsect ) ;
/* Initialize the FAT */
sect = b_fat ; nsect = sz_fat ; /* Start of FAT and number of FAT sectors */
j = nb = cl = 0 ;
do {
mem_set ( buf , 0 , szb_buf ) ; i = 0 ; /* Clear work area and reset write index */
if ( cl = = 0 ) { /* Set entry 0 and 1 */
st_dword ( buf + i , 0xFFFFFFF8 ) ; i + = 4 ; cl + + ;
st_dword ( buf + i , 0xFFFFFFFF ) ; i + = 4 ; cl + + ;
}
do { /* Create chains of bitmap, up-case and root dir */
while ( nb ! = 0 & & i < szb_buf ) { /* Create a chain */
st_dword ( buf + i , ( nb > 1 ) ? cl + 1 : 0xFFFFFFFF ) ;
i + = 4 ; cl + + ; nb - - ;
}
if ( nb = = 0 & & j < 3 ) nb = tbl [ j + + ] ; /* Next chain */
} while ( nb ! = 0 & & i < szb_buf ) ;
n = ( nsect > sz_buf ) ? sz_buf : nsect ; /* Write the buffered data */
if ( disk_write ( pdrv , buf , sect , n ) ! = RES_OK ) return FR_DISK_ERR ;
sect + = n ; nsect - = n ;
} while ( nsect ) ;
/* Initialize the root directory */
mem_set ( buf , 0 , szb_buf ) ;
buf [ SZDIRE * 0 + 0 ] = 0x83 ; /* 83 entry (volume label) */
buf [ SZDIRE * 1 + 0 ] = 0x81 ; /* 81 entry (allocation bitmap) */
st_dword ( buf + SZDIRE * 1 + 20 , 2 ) ;
st_dword ( buf + SZDIRE * 1 + 24 , szb_bit ) ;
buf [ SZDIRE * 2 + 0 ] = 0x82 ; /* 82 entry (up-case table) */
st_dword ( buf + SZDIRE * 2 + 4 , sum ) ;
st_dword ( buf + SZDIRE * 2 + 20 , 2 + tbl [ 0 ] ) ;
st_dword ( buf + SZDIRE * 2 + 24 , szb_case ) ;
sect = b_data + au * ( tbl [ 0 ] + tbl [ 1 ] ) ; nsect = au ; /* Start of the root directory and number of sectors */
do { /* Fill root directory sectors */
n = ( nsect > sz_buf ) ? sz_buf : nsect ;
if ( disk_write ( pdrv , buf , sect , n ) ! = RES_OK ) return FR_DISK_ERR ;
mem_set ( buf , 0 , ss ) ;
sect + = n ; nsect - = n ;
} while ( nsect ) ;
/* Create two set of the exFAT VBR blocks */
sect = b_vol ;
for ( n = 0 ; n < 2 ; n + + ) {
/* Main record (+0) */
mem_set ( buf , 0 , ss ) ;
mem_cpy ( buf + BS_JmpBoot , " \xEB \x76 \x90 " " EXFAT " , 11 ) ; /* Boot jump code (x86), OEM name */
st_dword ( buf + BPB_VolOfsEx , b_vol ) ; /* Volume offset in the physical drive [sector] */
st_dword ( buf + BPB_TotSecEx , sz_vol ) ; /* Volume size [sector] */
st_dword ( buf + BPB_FatOfsEx , b_fat - b_vol ) ; /* FAT offset [sector] */
st_dword ( buf + BPB_FatSzEx , sz_fat ) ; /* FAT size [sector] */
st_dword ( buf + BPB_DataOfsEx , b_data - b_vol ) ; /* Data offset [sector] */
st_dword ( buf + BPB_NumClusEx , n_clst ) ; /* Number of clusters */
st_dword ( buf + BPB_RootClusEx , 2 + tbl [ 0 ] + tbl [ 1 ] ) ; /* Root dir cluster # */
st_dword ( buf + BPB_VolIDEx , GET_FATTIME ( ) ) ; /* VSN */
st_word ( buf + BPB_FSVerEx , 0x100 ) ; /* Filesystem version (1.00) */
for ( buf [ BPB_BytsPerSecEx ] = 0 , i = ss ; i > > = 1 ; buf [ BPB_BytsPerSecEx ] + + ) ; /* Log2 of sector size [byte] */
for ( buf [ BPB_SecPerClusEx ] = 0 , i = au ; i > > = 1 ; buf [ BPB_SecPerClusEx ] + + ) ; /* Log2 of cluster size [sector] */
buf [ BPB_NumFATsEx ] = 1 ; /* Number of FATs */
buf [ BPB_DrvNumEx ] = 0x80 ; /* Drive number (for int13) */
st_word ( buf + BS_BootCodeEx , 0xFEEB ) ; /* Boot code (x86) */
st_word ( buf + BS_55AA , 0xAA55 ) ; /* Signature (placed here regardless of sector size) */
for ( i = sum = 0 ; i < ss ; i + + ) { /* VBR checksum */
if ( i ! = BPB_VolFlagEx & & i ! = BPB_VolFlagEx + 1 & & i ! = BPB_PercInUseEx ) sum = xsum32 ( buf [ i ] , sum ) ;
}
if ( disk_write ( pdrv , buf , sect + + , 1 ) ! = RES_OK ) return FR_DISK_ERR ;
/* Extended bootstrap record (+1..+8) */
mem_set ( buf , 0 , ss ) ;
st_word ( buf + ss - 2 , 0xAA55 ) ; /* Signature (placed at end of sector) */
for ( j = 1 ; j < 9 ; j + + ) {
for ( i = 0 ; i < ss ; sum = xsum32 ( buf [ i + + ] , sum ) ) ; /* VBR checksum */
if ( disk_write ( pdrv , buf , sect + + , 1 ) ! = RES_OK ) return FR_DISK_ERR ;
}
/* OEM/Reserved record (+9..+10) */
mem_set ( buf , 0 , ss ) ;
for ( ; j < 11 ; j + + ) {
for ( i = 0 ; i < ss ; sum = xsum32 ( buf [ i + + ] , sum ) ) ; /* VBR checksum */
if ( disk_write ( pdrv , buf , sect + + , 1 ) ! = RES_OK ) return FR_DISK_ERR ;
}
/* Sum record (+11) */
for ( i = 0 ; i < ss ; i + = 4 ) st_dword ( buf + i , sum ) ; /* Fill with checksum value */
if ( disk_write ( pdrv , buf , sect + + , 1 ) ! = RES_OK ) return FR_DISK_ERR ;
}
} else
# endif /* FF_FS_EXFAT */
{ /* Create an FAT/FAT32 volume */
do {
pau = au ;
/* Pre-determine number of clusters and FAT sub-type */
if ( fmt = = FS_FAT32 ) { /* FAT32 volume */
if ( pau = = 0 ) { /* au auto-selection */
n = sz_vol / 0x20000 ; /* Volume size in unit of 128KS */
for ( i = 0 , pau = 1 ; cst32 [ i ] & & cst32 [ i ] < = n ; i + + , pau < < = 1 ) ; /* Get from table */
}
n_clst = sz_vol / pau ; /* Number of clusters */
sz_fat = ( n_clst * 4 + 8 + ss - 1 ) / ss ; /* FAT size [sector] */
sz_rsv = 32 ; /* Number of reserved sectors */
sz_dir = 0 ; /* No static directory */
if ( n_clst < = MAX_FAT16 | | n_clst > MAX_FAT32 ) return FR_MKFS_ABORTED ;
} else { /* FAT volume */
if ( pau = = 0 ) { /* au auto-selection */
n = sz_vol / 0x1000 ; /* Volume size in unit of 4KS */
for ( i = 0 , pau = 1 ; cst [ i ] & & cst [ i ] < = n ; i + + , pau < < = 1 ) ; /* Get from table */
}
n_clst = sz_vol / pau ;
if ( n_clst > MAX_FAT12 ) {
n = n_clst * 2 + 4 ; /* FAT size [byte] */
} else {
fmt = FS_FAT12 ;
n = ( n_clst * 3 + 1 ) / 2 + 3 ; /* FAT size [byte] */
}
sz_fat = ( n + ss - 1 ) / ss ; /* FAT size [sector] */
sz_rsv = 1 ; /* Number of reserved sectors */
sz_dir = ( DWORD ) n_rootdir * SZDIRE / ss ; /* Rootdir size [sector] */
}
b_fat = b_vol + sz_rsv ; /* FAT base */
b_data = b_fat + sz_fat * n_fats + sz_dir ; /* Data base */
/* Align data base to erase block boundary (for flash memory media) */
n = ( ( b_data + sz_blk - 1 ) & ~ ( sz_blk - 1 ) ) - b_data ; /* Next nearest erase block from current data base */
if ( fmt = = FS_FAT32 ) { /* FAT32: Move FAT base */
sz_rsv + = n ; b_fat + = n ;
} else { /* FAT: Expand FAT size */
sz_fat + = n / n_fats ;
}
/* Determine number of clusters and final check of validity of the FAT sub-type */
if ( sz_vol < b_data + pau * 16 - b_vol ) return FR_MKFS_ABORTED ; /* Too small volume */
n_clst = ( sz_vol - sz_rsv - sz_fat * n_fats - sz_dir ) / pau ;
if ( fmt = = FS_FAT32 ) {
if ( n_clst < = MAX_FAT16 ) { /* Too few clusters for FAT32 */
if ( au = = 0 & & ( au = pau / 2 ) ! = 0 ) continue ; /* Adjust cluster size and retry */
return FR_MKFS_ABORTED ;
}
}
if ( fmt = = FS_FAT16 ) {
if ( n_clst > MAX_FAT16 ) { /* Too many clusters for FAT16 */
if ( au = = 0 & & ( pau * 2 ) < = 64 ) {
au = pau * 2 ; continue ; /* Adjust cluster size and retry */
}
if ( ( opt & FM_FAT32 ) ) {
fmt = FS_FAT32 ; continue ; /* Switch type to FAT32 and retry */
}
if ( au = = 0 & & ( au = pau * 2 ) < = 128 ) continue ; /* Adjust cluster size and retry */
return FR_MKFS_ABORTED ;
}
if ( n_clst < = MAX_FAT12 ) { /* Too few clusters for FAT16 */
if ( au = = 0 & & ( au = pau * 2 ) < = 128 ) continue ; /* Adjust cluster size and retry */
return FR_MKFS_ABORTED ;
}
}
if ( fmt = = FS_FAT12 & & n_clst > MAX_FAT12 ) return FR_MKFS_ABORTED ; /* Too many clusters for FAT12 */
/* Ok, it is the valid cluster configuration */
break ;
} while ( 1 ) ;
# if FF_USE_TRIM
tbl [ 0 ] = b_vol ; tbl [ 1 ] = b_vol + sz_vol - 1 ; /* Inform the device the volume area can be erased */
disk_ioctl ( pdrv , CTRL_TRIM , tbl ) ;
# endif
/* Create FAT VBR */
mem_set ( buf , 0 , ss ) ;
mem_cpy ( buf + BS_JmpBoot , " \xEB \xFE \x90 " " MSDOS5.0 " , 11 ) ; /* Boot jump code (x86), OEM name */
st_word ( buf + BPB_BytsPerSec , ss ) ; /* Sector size [byte] */
buf [ BPB_SecPerClus ] = ( BYTE ) pau ; /* Cluster size [sector] */
st_word ( buf + BPB_RsvdSecCnt , ( WORD ) sz_rsv ) ; /* Size of reserved area */
buf [ BPB_NumFATs ] = ( BYTE ) n_fats ; /* Number of FATs */
st_word ( buf + BPB_RootEntCnt , ( WORD ) ( ( fmt = = FS_FAT32 ) ? 0 : n_rootdir ) ) ; /* Number of root directory entries */
if ( sz_vol < 0x10000 ) {
st_word ( buf + BPB_TotSec16 , ( WORD ) sz_vol ) ; /* Volume size in 16-bit LBA */
} else {
st_dword ( buf + BPB_TotSec32 , sz_vol ) ; /* Volume size in 32-bit LBA */
}
buf [ BPB_Media ] = 0xF8 ; /* Media descriptor byte */
st_word ( buf + BPB_SecPerTrk , 63 ) ; /* Number of sectors per track (for int13) */
st_word ( buf + BPB_NumHeads , 255 ) ; /* Number of heads (for int13) */
st_dword ( buf + BPB_HiddSec , b_vol ) ; /* Volume offset in the physical drive [sector] */
if ( fmt = = FS_FAT32 ) {
st_dword ( buf + BS_VolID32 , GET_FATTIME ( ) ) ; /* VSN */
st_dword ( buf + BPB_FATSz32 , sz_fat ) ; /* FAT size [sector] */
st_dword ( buf + BPB_RootClus32 , 2 ) ; /* Root directory cluster # (2) */
st_word ( buf + BPB_FSInfo32 , 1 ) ; /* Offset of FSINFO sector (VBR + 1) */
st_word ( buf + BPB_BkBootSec32 , 6 ) ; /* Offset of backup VBR (VBR + 6) */
buf [ BS_DrvNum32 ] = 0x80 ; /* Drive number (for int13) */
buf [ BS_BootSig32 ] = 0x29 ; /* Extended boot signature */
mem_cpy ( buf + BS_VolLab32 , " NO NAME " " FAT32 " , 19 ) ; /* Volume label, FAT signature */
} else {
st_dword ( buf + BS_VolID , GET_FATTIME ( ) ) ; /* VSN */
st_word ( buf + BPB_FATSz16 , ( WORD ) sz_fat ) ; /* FAT size [sector] */
buf [ BS_DrvNum ] = 0x80 ; /* Drive number (for int13) */
buf [ BS_BootSig ] = 0x29 ; /* Extended boot signature */
mem_cpy ( buf + BS_VolLab , " NO NAME " " FAT " , 19 ) ; /* Volume label, FAT signature */
}
st_word ( buf + BS_55AA , 0xAA55 ) ; /* Signature (offset is fixed here regardless of sector size) */
if ( disk_write ( pdrv , buf , b_vol , 1 ) ! = RES_OK ) return FR_DISK_ERR ; /* Write it to the VBR sector */
/* Create FSINFO record if needed */
if ( fmt = = FS_FAT32 ) {
disk_write ( pdrv , buf , b_vol + 6 , 1 ) ; /* Write backup VBR (VBR + 6) */
mem_set ( buf , 0 , ss ) ;
st_dword ( buf + FSI_LeadSig , 0x41615252 ) ;
st_dword ( buf + FSI_StrucSig , 0x61417272 ) ;
st_dword ( buf + FSI_Free_Count , n_clst - 1 ) ; /* Number of free clusters */
st_dword ( buf + FSI_Nxt_Free , 2 ) ; /* Last allocated cluster# */
st_word ( buf + BS_55AA , 0xAA55 ) ;
disk_write ( pdrv , buf , b_vol + 7 , 1 ) ; /* Write backup FSINFO (VBR + 7) */
disk_write ( pdrv , buf , b_vol + 1 , 1 ) ; /* Write original FSINFO (VBR + 1) */
}
/* Initialize FAT area */
mem_set ( buf , 0 , ( UINT ) szb_buf ) ;
sect = b_fat ; /* FAT start sector */
for ( i = 0 ; i < n_fats ; i + + ) { /* Initialize FATs each */
if ( fmt = = FS_FAT32 ) {
st_dword ( buf + 0 , 0xFFFFFFF8 ) ; /* Entry 0 */
st_dword ( buf + 4 , 0xFFFFFFFF ) ; /* Entry 1 */
st_dword ( buf + 8 , 0x0FFFFFFF ) ; /* Entry 2 (root directory) */
} else {
st_dword ( buf + 0 , ( fmt = = FS_FAT12 ) ? 0xFFFFF8 : 0xFFFFFFF8 ) ; /* Entry 0 and 1 */
}
nsect = sz_fat ; /* Number of FAT sectors */
do { /* Fill FAT sectors */
n = ( nsect > sz_buf ) ? sz_buf : nsect ;
if ( disk_write ( pdrv , buf , sect , ( UINT ) n ) ! = RES_OK ) return FR_DISK_ERR ;
mem_set ( buf , 0 , ss ) ;
sect + = n ; nsect - = n ;
} while ( nsect ) ;
}
/* Initialize root directory (fill with zero) */
nsect = ( fmt = = FS_FAT32 ) ? pau : sz_dir ; /* Number of root directory sectors */
do {
n = ( nsect > sz_buf ) ? sz_buf : nsect ;
if ( disk_write ( pdrv , buf , sect , ( UINT ) n ) ! = RES_OK ) return FR_DISK_ERR ;
sect + = n ; nsect - = n ;
} while ( nsect ) ;
}
/* Determine system ID in the partition table */
if ( FF_FS_EXFAT & & fmt = = FS_EXFAT ) {
sys = 0x07 ; /* HPFS/NTFS/exFAT */
} else {
if ( fmt = = FS_FAT32 ) {
sys = 0x0C ; /* FAT32X */
} else {
if ( sz_vol > = 0x10000 ) {
sys = 0x06 ; /* FAT12/16 (large) */
} else {
sys = ( fmt = = FS_FAT16 ) ? 0x04 : 0x01 ; /* FAT16 : FAT12 */
}
}
}
/* Update partition information */
if ( FF_MULTI_PARTITION & & part ! = 0 ) { /* Created in the existing partition */
/* Update system ID in the partition table */
if ( disk_read ( pdrv , buf , 0 , 1 ) ! = RES_OK ) return FR_DISK_ERR ; /* Read the MBR */
buf [ MBR_Table + ( part - 1 ) * SZ_PTE + PTE_System ] = sys ; /* Set system ID */
if ( disk_write ( pdrv , buf , 0 , 1 ) ! = RES_OK ) return FR_DISK_ERR ; /* Write it back to the MBR */
} else { /* Created as a new single partition */
if ( ! ( opt & FM_SFD ) ) { /* Create partition table if in FDISK format */
mem_set ( buf , 0 , ss ) ;
st_word ( buf + BS_55AA , 0xAA55 ) ; /* MBR signature */
pte = buf + MBR_Table ; /* Create partition table for single partition in the drive */
pte [ PTE_Boot ] = 0 ; /* Boot indicator */
pte [ PTE_StHead ] = 1 ; /* Start head */
pte [ PTE_StSec ] = 1 ; /* Start sector */
pte [ PTE_StCyl ] = 0 ; /* Start cylinder */
pte [ PTE_System ] = sys ; /* System type */
n = ( b_vol + sz_vol ) / ( 63 * 255 ) ; /* (End CHS may be invalid) */
pte [ PTE_EdHead ] = 254 ; /* End head */
pte [ PTE_EdSec ] = ( BYTE ) ( n > > 2 | 63 ) ; /* End sector */
pte [ PTE_EdCyl ] = ( BYTE ) n ; /* End cylinder */
st_dword ( pte + PTE_StLba , b_vol ) ; /* Start offset in LBA */
st_dword ( pte + PTE_SizLba , sz_vol ) ; /* Size in sectors */
if ( disk_write ( pdrv , buf , 0 , 1 ) ! = RES_OK ) return FR_DISK_ERR ; /* Write it to the MBR */
}
}
if ( disk_ioctl ( pdrv , CTRL_SYNC , 0 ) ! = RES_OK ) return FR_DISK_ERR ;
return FR_OK ;
}
# if FF_MULTI_PARTITION
/*-----------------------------------------------------------------------*/
/* Create Partition Table on the Physical Drive */
/*-----------------------------------------------------------------------*/
FRESULT f_fdisk (
BYTE pdrv , /* Physical drive number */
const DWORD * szt , /* Pointer to the size table for each partitions */
void * work /* Pointer to the working buffer */
)
{
UINT i , n , sz_cyl , tot_cyl , b_cyl , e_cyl , p_cyl ;
BYTE s_hd , e_hd , * p , * buf = ( BYTE * ) work ;
DSTATUS stat ;
DWORD sz_disk , sz_part , s_part ;
stat = disk_initialize ( pdrv ) ;
if ( stat & STA_NOINIT ) return FR_NOT_READY ;
if ( stat & STA_PROTECT ) return FR_WRITE_PROTECTED ;
if ( disk_ioctl ( pdrv , GET_SECTOR_COUNT , & sz_disk ) ) return FR_DISK_ERR ;
/* Determine the CHS without any consideration of the drive geometry */
for ( n = 16 ; n < 256 & & sz_disk / n / 63 > 1024 ; n * = 2 ) ;
if ( n = = 256 ) n - - ;
e_hd = n - 1 ;
sz_cyl = 63 * n ;
tot_cyl = sz_disk / sz_cyl ;
/* Create partition table */
mem_set ( buf , 0 , FF_MAX_SS ) ;
p = buf + MBR_Table ; b_cyl = 0 ;
for ( i = 0 ; i < 4 ; i + + , p + = SZ_PTE ) {
p_cyl = ( szt [ i ] < = 100U ) ? ( DWORD ) tot_cyl * szt [ i ] / 100 : szt [ i ] / sz_cyl ; /* Number of cylinders */
if ( p_cyl = = 0 ) continue ;
s_part = ( DWORD ) sz_cyl * b_cyl ;
sz_part = ( DWORD ) sz_cyl * p_cyl ;
if ( i = = 0 ) { /* Exclude first track of cylinder 0 */
s_hd = 1 ;
s_part + = 63 ; sz_part - = 63 ;
} else {
s_hd = 0 ;
}
e_cyl = b_cyl + p_cyl - 1 ; /* End cylinder */
if ( e_cyl > = tot_cyl ) return FR_INVALID_PARAMETER ;
/* Set partition table */
p [ 1 ] = s_hd ; /* Start head */
p [ 2 ] = ( BYTE ) ( ( b_cyl > > 2 ) + 1 ) ; /* Start sector */
p [ 3 ] = ( BYTE ) b_cyl ; /* Start cylinder */
p [ 4 ] = 0x07 ; /* System type (temporary setting) */
p [ 5 ] = e_hd ; /* End head */
p [ 6 ] = ( BYTE ) ( ( e_cyl > > 2 ) + 63 ) ; /* End sector */
p [ 7 ] = ( BYTE ) e_cyl ; /* End cylinder */
st_dword ( p + 8 , s_part ) ; /* Start sector in LBA */
st_dword ( p + 12 , sz_part ) ; /* Number of sectors */
/* Next partition */
b_cyl + = p_cyl ;
}
st_word ( p , 0xAA55 ) ;
/* Write it to the MBR */
return ( disk_write ( pdrv , buf , 0 , 1 ) ! = RES_OK | | disk_ioctl ( pdrv , CTRL_SYNC , 0 ) ! = RES_OK ) ? FR_DISK_ERR : FR_OK ;
}
# endif /* FF_MULTI_PARTITION */
# endif /* FF_USE_MKFS && !FF_FS_READONLY */
# if FF_USE_STRFUNC
/*-----------------------------------------------------------------------*/
/* Get a String from the File */
/*-----------------------------------------------------------------------*/
TCHAR * f_gets (
TCHAR * buff , /* Pointer to the string buffer to read */
int len , /* Size of string buffer (characters) */
FIL * fp /* Pointer to the file object */
)
{
int n = 0 ;
TCHAR c , * p = buff ;
BYTE s [ 2 ] ;
UINT rc ;
while ( n < len - 1 ) { /* Read characters until buffer gets filled */
# if FF_LFN_UNICODE && FF_USE_LFN /* Unicode API */
# if FF_STRF_ENCODE == 3 /* Read a character in UTF-8 */
f_read ( fp , s , 1 , & rc ) ;
if ( rc ! = 1 ) break ;
c = s [ 0 ] ;
if ( c > = 0x80 ) {
if ( c < 0xC0 ) continue ; /* Skip stray trailer */
if ( c < 0xE0 ) { /* Two-byte sequence (0x80-0x7FF) */
f_read ( fp , s , 1 , & rc ) ;
if ( rc ! = 1 ) break ;
c = ( c & 0x1F ) < < 6 | ( s [ 0 ] & 0x3F ) ;
if ( c < 0x80 ) c = ' ? ' ; /* Reject invalid code range */
} else {
if ( c < 0xF0 ) { /* Three-byte sequence (0x800-0xFFFF) */
f_read ( fp , s , 2 , & rc ) ;
if ( rc ! = 2 ) break ;
c = c < < 12 | ( s [ 0 ] & 0x3F ) < < 6 | ( s [ 1 ] & 0x3F ) ;
if ( c < 0x800 ) c = ' ? ' ; /* Reject invalid code range */
} else { /* Reject four-byte sequence */
c = ' ? ' ;
}
}
}
# elif FF_STRF_ENCODE == 2 /* Read a character in UTF-16BE */
f_read ( fp , s , 2 , & rc ) ;
if ( rc ! = 2 ) break ;
c = s [ 1 ] + ( s [ 0 ] < < 8 ) ;
# elif FF_STRF_ENCODE == 1 /* Read a character in UTF-16LE */
f_read ( fp , s , 2 , & rc ) ;
if ( rc ! = 2 ) break ;
c = s [ 0 ] + ( s [ 1 ] < < 8 ) ;
# else /* Read a character in ANSI/OEM */
f_read ( fp , s , 1 , & rc ) ;
if ( rc ! = 1 ) break ;
c = s [ 0 ] ;
if ( dbc_1st ( ( BYTE ) c ) ) {
f_read ( fp , s , 1 , & rc ) ;
if ( rc ! = 1 ) break ;
c = ( c < < 8 ) + s [ 0 ] ;
}
c = ff_oem2uni ( c , CODEPAGE ) ; /* OEM -> Unicode */
if ( ! c ) c = ' ? ' ;
# endif
# else /* ANSI/OEM API: Read a character without conversion */
f_read ( fp , s , 1 , & rc ) ;
if ( rc ! = 1 ) break ;
c = s [ 0 ] ;
# endif
if ( FF_USE_STRFUNC = = 2 & & c = = ' \r ' ) continue ; /* Strip '\r' */
* p + + = c ;
n + + ;
if ( c = = ' \n ' ) break ; /* Break on EOL */
}
* p = 0 ;
return n ? buff : 0 ; /* When no data read (eof or error), return with error. */
}
# if !FF_FS_READONLY
# include <stdarg.h>
/*-----------------------------------------------------------------------*/
/* Put a Character to the File */
/*-----------------------------------------------------------------------*/
typedef struct {
FIL * fp ; /* Ptr to the writing file */
int idx , nchr ; /* Write index of buf[] (-1:error), number of chars written */
BYTE buf [ 64 ] ; /* Write buffer */
} putbuff ;
static
void putc_bfd ( /* Buffered write with code conversion */
putbuff * pb ,
TCHAR c
)
{
UINT bw ;
int i ;
if ( FF_USE_STRFUNC = = 2 & & c = = ' \n ' ) { /* LF -> CRLF conversion */
putc_bfd ( pb , ' \r ' ) ;
}
i = pb - > idx ; /* Write index of pb->buf[] */
if ( i < 0 ) return ;
# if FF_LFN_UNICODE && FF_USE_LFN /* Unicode API */
# if FF_STRF_ENCODE == 3 /* Write a character in UTF-8 */
if ( c < 0x80 ) { /* 7-bit */
pb - > buf [ i + + ] = ( BYTE ) c ;
} else {
if ( c < 0x800 ) { /* 11-bit */
pb - > buf [ i + + ] = ( BYTE ) ( 0xC0 | c > > 6 ) ;
} else { /* 16-bit */
pb - > buf [ i + + ] = ( BYTE ) ( 0xE0 | c > > 12 ) ;
pb - > buf [ i + + ] = ( BYTE ) ( 0x80 | ( c > > 6 & 0x3F ) ) ;
}
pb - > buf [ i + + ] = ( BYTE ) ( 0x80 | ( c & 0x3F ) ) ;
}
# elif FF_STRF_ENCODE == 2 /* Write a character in UTF-16BE */
pb - > buf [ i + + ] = ( BYTE ) ( c > > 8 ) ;
pb - > buf [ i + + ] = ( BYTE ) c ;
# elif FF_STRF_ENCODE == 1 /* Write a character in UTF-16LE */
pb - > buf [ i + + ] = ( BYTE ) c ;
pb - > buf [ i + + ] = ( BYTE ) ( c > > 8 ) ;
# else /* Write a character in ANSI/OEM */
c = ff_uni2oem ( c , CODEPAGE ) ; /* Unicode -> OEM */
if ( ! c ) c = ' ? ' ;
if ( c > = 0x100 )
pb - > buf [ i + + ] = ( BYTE ) ( c > > 8 ) ;
pb - > buf [ i + + ] = ( BYTE ) c ;
# endif
# else /* ANSI/OEM API: Write a character without conversion */
pb - > buf [ i + + ] = ( BYTE ) c ;
# endif
if ( i > = ( int ) ( sizeof pb - > buf ) - 3 ) { /* Write buffered characters to the file */
f_write ( pb - > fp , pb - > buf , ( UINT ) i , & bw ) ;
i = ( bw = = ( UINT ) i ) ? 0 : - 1 ;
}
pb - > idx = i ;
pb - > nchr + + ;
}
static
int putc_flush ( /* Flush left characters in the buffer */
putbuff * pb
)
{
UINT nw ;
if ( pb - > idx > = 0 /* Flush buffered characters to the file */
& & f_write ( pb - > fp , pb - > buf , ( UINT ) pb - > idx , & nw ) = = FR_OK
& & ( UINT ) pb - > idx = = nw ) return pb - > nchr ;
return EOF ;
}
static
void putc_init ( /* Initialize write buffer */
putbuff * pb ,
FIL * fp
)
{
pb - > fp = fp ;
pb - > nchr = pb - > idx = 0 ;
}
int f_putc (
TCHAR c , /* A character to be output */
FIL * fp /* Pointer to the file object */
)
{
putbuff pb ;
putc_init ( & pb , fp ) ;
putc_bfd ( & pb , c ) ; /* Put the character */
return putc_flush ( & pb ) ;
}
/*-----------------------------------------------------------------------*/
/* Put a String to the File */
/*-----------------------------------------------------------------------*/
int f_puts (
const TCHAR * str , /* Pointer to the string to be output */
FIL * fp /* Pointer to the file object */
)
{
putbuff pb ;
putc_init ( & pb , fp ) ;
while ( * str ) putc_bfd ( & pb , * str + + ) ; /* Put the string */
return putc_flush ( & pb ) ;
}
/*-----------------------------------------------------------------------*/
/* Put a Formatted String to the File */
/*-----------------------------------------------------------------------*/
int f_printf (
FIL * fp , /* Pointer to the file object */
const TCHAR * fmt , /* Pointer to the format string */
. . . /* Optional arguments... */
)
{
va_list arp ;
putbuff pb ;
BYTE f , r ;
UINT i , j , w ;
DWORD v ;
TCHAR c , d , str [ 32 ] , * p ;
putc_init ( & pb , fp ) ;
va_start ( arp , fmt ) ;
for ( ; ; ) {
c = * fmt + + ;
if ( c = = 0 ) break ; /* End of string */
if ( c ! = ' % ' ) { /* Non escape character */
putc_bfd ( & pb , c ) ;
continue ;
}
w = f = 0 ;
c = * fmt + + ;
if ( c = = ' 0 ' ) { /* Flag: '0' padding */
f = 1 ; c = * fmt + + ;
} else {
if ( c = = ' - ' ) { /* Flag: left justified */
f = 2 ; c = * fmt + + ;
}
}
while ( IsDigit ( c ) ) { /* Precision */
w = w * 10 + c - ' 0 ' ;
c = * fmt + + ;
}
if ( c = = ' l ' | | c = = ' L ' ) { /* Prefix: Size is long int */
f | = 4 ; c = * fmt + + ;
}
if ( ! c ) break ;
d = c ;
if ( IsLower ( d ) ) d - = 0x20 ;
switch ( d ) { /* Type is... */
case ' S ' : /* String */
p = va_arg ( arp , TCHAR * ) ;
for ( j = 0 ; p [ j ] ; j + + ) ;
if ( ! ( f & 2 ) ) { /* Right pad */
while ( j + + < w ) putc_bfd ( & pb , ' ' ) ;
}
while ( * p ) putc_bfd ( & pb , * p + + ) ; /* String body */
while ( j + + < w ) putc_bfd ( & pb , ' ' ) ; /* Left pad */
continue ;
case ' C ' : /* Character */
putc_bfd ( & pb , ( TCHAR ) va_arg ( arp , int ) ) ; continue ;
case ' B ' : /* Binary */
r = 2 ; break ;
case ' O ' : /* Octal */
r = 8 ; break ;
case ' D ' : /* Signed decimal */
case ' U ' : /* Unsigned decimal */
r = 10 ; break ;
case ' X ' : /* Hexdecimal */
r = 16 ; break ;
default : /* Unknown type (pass-through) */
putc_bfd ( & pb , c ) ; continue ;
}
/* Get an argument and put it in numeral */
v = ( f & 4 ) ? ( DWORD ) va_arg ( arp , long ) : ( ( d = = ' D ' ) ? ( DWORD ) ( long ) va_arg ( arp , int ) : ( DWORD ) va_arg ( arp , unsigned int ) ) ;
if ( d = = ' D ' & & ( v & 0x80000000 ) ) {
v = 0 - v ;
f | = 8 ;
}
i = 0 ;
do {
d = ( TCHAR ) ( v % r ) ; v / = r ;
if ( d > 9 ) d + = ( c = = ' x ' ) ? 0x27 : 0x07 ;
str [ i + + ] = d + ' 0 ' ;
} while ( v & & i < sizeof str / sizeof * str ) ;
if ( f & 8 ) str [ i + + ] = ' - ' ;
j = i ; d = ( f & 1 ) ? ' 0 ' : ' ' ;
if ( ! ( f & 2 ) ) {
while ( j + + < w ) putc_bfd ( & pb , d ) ; /* Right pad */
}
do {
putc_bfd ( & pb , str [ - - i ] ) ; /* Number body */
} while ( i ) ;
while ( j + + < w ) putc_bfd ( & pb , d ) ; /* Left pad */
}
va_end ( arp ) ;
return putc_flush ( & pb ) ;
}
# endif /* !FF_FS_READONLY */
# endif /* FF_USE_STRFUNC */
# if FF_CODE_PAGE == 0
/*-----------------------------------------------------------------------*/
/* Set Active Codepage for the Path Name */
/*-----------------------------------------------------------------------*/
FRESULT f_setcp (
WORD cp /* Value to be set as active code page */
)
{
static const WORD validcp [ ] = { 437 , 720 , 737 , 771 , 775 , 850 , 852 , 857 , 860 , 861 , 862 , 863 , 864 , 865 , 866 , 869 , 932 , 936 , 949 , 950 , 0 } ;
static const BYTE * const tables [ ] = { Ct437 , Ct720 , Ct737 , Ct771 , Ct775 , Ct850 , Ct852 , Ct857 , Ct860 , Ct861 , Ct862 , Ct863 , Ct864 , Ct865 , Ct866 , Ct869 , Dc932 , Dc936 , Dc949 , Dc950 , 0 } ;
UINT i ;
for ( i = 0 ; validcp [ i ] ! = 0 & & validcp [ i ] ! = cp ; i + + ) ; /* Find the code page */
if ( validcp [ i ] ! = cp ) return FR_INVALID_PARAMETER ;
CodePage = cp ;
if ( cp > = 900 ) { /* DBCS */
ExCvt = 0 ;
DbcTbl = tables [ i ] ;
} else { /* SBCS */
ExCvt = tables [ i ] ;
DbcTbl = 0 ;
}
return FR_OK ;
}
# endif /* FF_CODE_PAGE == 0 */