summaryrefslogtreecommitdiff
path: root/FreeRTOS-Labs/Source/FreeRTOS-Plus-FAT/ff_dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'FreeRTOS-Labs/Source/FreeRTOS-Plus-FAT/ff_dir.c')
-rw-r--r--FreeRTOS-Labs/Source/FreeRTOS-Plus-FAT/ff_dir.c3426
1 files changed, 3426 insertions, 0 deletions
diff --git a/FreeRTOS-Labs/Source/FreeRTOS-Plus-FAT/ff_dir.c b/FreeRTOS-Labs/Source/FreeRTOS-Plus-FAT/ff_dir.c
new file mode 100644
index 000000000..77e491f6f
--- /dev/null
+++ b/FreeRTOS-Labs/Source/FreeRTOS-Plus-FAT/ff_dir.c
@@ -0,0 +1,3426 @@
+/*
+ * FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab!
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Authors include James Walmsley, Hein Tibosch and Richard Barry
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * https://www.FreeRTOS.org
+ *
+ */
+
+/**
+ * @file ff_dir.c
+ * @ingroup DIR
+ *
+ * @defgroup DIR Handles Directory Traversal
+ * @brief Handles DIR access and traversal.
+ *
+ * Provides FindFirst() and FindNext() Interfaces
+ **/
+
+#include "ff_headers.h"
+
+#include <stdio.h>
+
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+#include <wchar.h>
+#endif
+
+#if defined( WIN32 )
+ #define wcsicmp _wcsicmp
+#else
+ #define wcsicmp wcscasecmp
+ #include <ctype.h>
+#endif
+
+/* Calculate a simple LFN checmsum. */
+static uint8_t FF_CreateChkSum( const uint8_t *pa_pShortName );
+
+static BaseType_t FF_ShortNameExists( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster, char *pcShortName, FF_Error_t *pxError );
+
+#if( ffconfigSHORTNAME_CASE != 0 )
+ /* For short-name entries, NT/XP etc store case information in byte 0x0c
+ * Use this to show proper case of "README.txt" or "source.H".
+ */
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ static void FF_CaseShortName( FF_T_WCHAR *pcName, uint8_t attrib );
+ #else
+ static void FF_CaseShortName( char *pcName, uint8_t attrib );
+ #endif
+#endif /* ffconfigSHORTNAME_CASE */
+
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+
+ /* For unicode, the short name can be expanded to wchar
+ * by inserting zero's.
+ */
+ static void FF_ShortNameExpand( FF_T_WCHAR * );
+
+#endif
+
+/*
+ * Transform a name as stored on disk "README__TXT"
+ * to a nul-terminated string: "README.TXT", "FILE001".
+ * A dot is only added if an extension is present.
+ */
+static void FF_ProcessShortName( char *pcName );
+
+#if( ffconfigTIME_SUPPORT != 0 )
+ static void FF_PlaceTime( uint8_t *pucEntryBuffer, uint32_t Offset, FF_SystemTime_t *pxTime );
+ static void FF_PlaceDate( uint8_t *pucEntryBuffer, uint32_t Offset, FF_SystemTime_t *pxTime );
+ static void FF_GetTime( FF_SystemTime_t *pxTime, const uint8_t *pucEntryBuffer, uint32_t ulOffset );
+ static void FF_GetDate( FF_SystemTime_t *pxTime, const uint8_t *pucEntryBuffer, uint32_t ulOffset );
+#endif /* ffconfigTIME_SUPPORT */
+static FF_Error_t FF_Traverse( FF_IOManager_t *pxIOManager, uint32_t ulEntry, FF_FetchContext_t *pxContext );
+static int32_t FF_FindFreeDirent( FF_IOManager_t *pxIOManager, FF_FindParams_t *pxFindParams, uint16_t usSequential );
+
+#if( ffconfigLFN_SUPPORT != 0 )
+ static int8_t FF_CreateLFNEntry( uint8_t *pucEntryBuffer, uint8_t *pcName, UBaseType_t uxNameLength, UBaseType_t uxLFN, uint8_t ucCheckSum );
+#endif /* ffconfigLFN_SUPPORT */
+
+static BaseType_t FF_ValidShortChar( char cChar );
+
+#if( ffconfigLFN_SUPPORT != 0 )
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ static FF_Error_t FF_CreateLFNs( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster, FF_T_WCHAR *pcName, uint8_t ucCheckSum, uint16_t usEntry );
+ #else
+ static FF_Error_t FF_CreateLFNs( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster, char *pcName, uint8_t ucCheckSum, uint16_t usEntry );
+ #endif
+#endif
+
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ static void FF_MakeNameCompliant( FF_T_WCHAR *pcName );
+#else
+ static void FF_MakeNameCompliant( char *pcName );
+#endif
+
+#if ( FF_NOSTRCASECMP == 0 )
+ static portINLINE unsigned char prvToLower( unsigned char c )
+ {
+ unsigned char cReturnChar;
+ if( c >= 'A' && c <= 'Z' )
+ {
+ cReturnChar = c + 0x20;
+ }
+ else
+ {
+ cReturnChar = c;
+ }
+
+ return cReturnChar;
+ }
+
+ int strcasecmp( const char *pcString1, const char *pcString2 )
+ {
+ unsigned char c1,c2;
+ do
+ {
+ c1 = *pcString1++;
+ c2 = *pcString2++;
+ c1 = ( unsigned char ) prvToLower( ( unsigned char ) c1 );
+ c2 = ( unsigned char ) prvToLower( ( unsigned char ) c2 );
+ }
+ while( ( c1 == c2 ) && ( c1 != '\0' ) );
+
+ return ( int ) c1 - c2;
+ } /* strcasecmp() */
+#endif
+/*-----------------------------------------------------------*/
+
+static uint8_t FF_CreateChkSum( const uint8_t *pa_pShortName )
+{
+uint8_t cNameLen;
+uint8_t ChkSum = 0;
+
+ for( cNameLen = 11; cNameLen != 0; cNameLen-- )
+ {
+ ChkSum = ( uint8_t )
+ ( ( ( ChkSum & 1 ) ? 0x80 : 0 ) + ( ChkSum >> 1 ) + *( pa_pShortName++ ) );
+ }
+
+ return ChkSum;
+} /* FF_CreateChkSum() */
+/*-----------------------------------------------------------*/
+
+/* _HT_ Does not need a wchar version because a short name is treated a normal string of bytes */
+static BaseType_t FF_ShortNameExists( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster, char *pcShortName, FF_Error_t *pxError )
+{
+BaseType_t xIndex;
+const uint8_t *pucEntryBuffer = NULL; /* initialisation not necessary, just for the compiler */
+uint8_t ucAttrib;
+FF_FetchContext_t xFetchContext;
+char pcMyShortName[ FF_SIZEOF_DIRECTORY_ENTRY ];
+BaseType_t xResult = -1;
+
+#if( ffconfigHASH_CACHE != 0 )
+ uint32_t ulHash;
+#endif
+
+ *pxError = FF_ERR_NONE;
+
+ #if( ffconfigHASH_CACHE != 0 )
+ {
+ if( !FF_DirHashed( pxIOManager, ulDirCluster ) )
+ {
+ /* Hash the directory. */
+ FF_HashDir( pxIOManager, ulDirCluster );
+ }
+
+ #if ffconfigHASH_FUNCTION == CRC16
+ {
+ ulHash = ( uint32_t ) FF_GetCRC16( ( uint8_t * ) pcShortName, strlen( pcShortName ) );
+ }
+ #else /* ffconfigHASH_FUNCTION == CRC8 */
+ {
+ ulHash = ( uint32_t ) FF_GetCRC8( ( uint8_t * ) pcShortName, strlen( pcShortName ) );
+ }
+ #endif
+ {
+ /* FF_CheckDirentHash result: 0 not found, 1 found, -1 directory not hashed */
+ xResult = FF_CheckDirentHash( pxIOManager, ulDirCluster, ulHash );
+ }
+ }
+ #endif
+
+ if( xResult < 0 )
+ {
+ xResult = pdFALSE;
+ *pxError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );
+
+ if( FF_isERR( *pxError ) == pdFALSE )
+ {
+ for( xIndex = 0; xIndex < FF_MAX_ENTRIES_PER_DIRECTORY; xIndex++ )
+ {
+ /* Call FF_FetchEntryWithContext only once for every 512-byte block */
+ if( ( xIndex == 0 ) ||
+ ( pucEntryBuffer >= xFetchContext.pxBuffer->pucBuffer + ( FF_SIZEOF_SECTOR - FF_SIZEOF_DIRECTORY_ENTRY ) ) )
+ {
+ *pxError = FF_FetchEntryWithContext( pxIOManager, ( uint32_t ) xIndex, &xFetchContext, NULL );
+ if( FF_isERR( *pxError ) )
+ {
+ break;
+ }
+ pucEntryBuffer = xFetchContext.pxBuffer->pucBuffer;
+ }
+ else
+ {
+ /* Advance 32 bytes to get the next directory entry. */
+ pucEntryBuffer += FF_SIZEOF_DIRECTORY_ENTRY;
+ }
+
+ if( FF_isEndOfDir( pucEntryBuffer ) )
+ {
+ break;
+ }
+ if( FF_isDeleted( pucEntryBuffer ) == pdFALSE )
+ {
+ ucAttrib = FF_getChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB );
+ if( ( ucAttrib & FF_FAT_ATTR_LFN ) != FF_FAT_ATTR_LFN )
+ {
+ memcpy( pcMyShortName, pucEntryBuffer, sizeof( pcMyShortName ) );
+ FF_ProcessShortName( pcMyShortName );
+ if( strcmp( ( const char * )pcShortName, ( const char * )pcMyShortName ) == 0 )
+ {
+ xResult = pdTRUE;
+ break;
+ }
+ }
+ }
+ } /* for ( xIndex = 0; xIndex < FF_MAX_ENTRIES_PER_DIRECTORY; xIndex++ ) */
+ }
+ *pxError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
+ }
+
+ return xResult;
+} /* FF_ShortNameExists() */
+/*-----------------------------------------------------------*/
+
+/* _HT_ When adding many files to a single directory, FF_FindEntryInDir was sometimes */
+/* _HT_ called 3 times before inserting a single file. With these changes it is called one time only */
+/* _HT_ pxFindParams caches some information: */
+/* _HT_ 1: the first free entry 2: whether the short-name version already exists */
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+uint32_t FF_FindEntryInDir( FF_IOManager_t *pxIOManager, FF_FindParams_t *pxFindParams, const FF_T_WCHAR *pcName, uint8_t pa_Attrib, FF_DirEnt_t *pxDirEntry, FF_Error_t *pxError )
+#else
+uint32_t FF_FindEntryInDir( FF_IOManager_t *pxIOManager, FF_FindParams_t *pxFindParams, const char *pcName, uint8_t pa_Attrib, FF_DirEnt_t *pxDirEntry, FF_Error_t *pxError )
+#endif
+{
+FF_FetchContext_t xFetchContext;
+/* const pointer to read from pBuffer */
+const uint8_t *src = NULL;
+/* As we're walking through a directory, we might as well
+find the first free entry to help FF_FindFreeDirent( )
+The result will be stored in 'pxFindParams->lFreeEntry' */
+BaseType_t entriesNeeded;
+BaseType_t freeCount = 0;
+FF_Error_t xError;
+/* If the file name fits into a short file name
+then the existence of that short file name will be checked as well. */
+BaseType_t testShortname;
+uint32_t xResult = 0ul;
+#if( ffconfigUNICODE_UTF8_SUPPORT == 1 )
+ int32_t utf8Error;
+#endif
+
+#if( ffconfigLFN_SUPPORT != 0 )
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ FF_T_WCHAR *pcCurPtr; /* Pointer to store a LFN. */
+ FF_T_WCHAR *pcLastPtr = pxDirEntry->pcFileName + sizeof( pxDirEntry->pcFileName );
+ #else
+ char *pcCurPtr; /* Pointer to store a LFN. */
+ char *pcLastPtr = pxDirEntry->pcFileName + sizeof( pxDirEntry->pcFileName );
+ #endif /* ffconfigUNICODE_UTF16_SUPPORT */
+
+ uint16_t lfnItem = 0;
+ uint8_t ucCheckSum = 0;
+ BaseType_t xLFNCount = 0;
+ BaseType_t xLFNTotal = 0;
+ uint8_t lastAttrib;
+
+ BaseType_t xIndex;
+#endif /* ffconfigLFN_SUPPORT */
+
+ #if( ffconfigLFN_SUPPORT != 0 )
+ {
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ BaseType_t NameLen = ( BaseType_t ) wcslen( ( const char * )pcName );
+ #else
+ BaseType_t NameLen = ( BaseType_t ) strlen( ( const char * )pcName );
+ #endif
+ /* Find enough places for the LFNs and the ShortName. */
+ entriesNeeded = ( uint8_t )( ( NameLen + 12 ) / 13 ) + 1;
+ }
+ #else
+ {
+ entriesNeeded = 1;
+ }
+ #endif /* ffconfigLFN_SUPPORT */
+
+ if( ( pxFindParams->ulFlags & FIND_FLAG_FITS_SHORT_OK ) == FIND_FLAG_FITS_SHORT_OK )
+ {
+ testShortname = pdTRUE;
+ }
+ else
+ {
+ testShortname = pdFALSE;
+ }
+
+ pxDirEntry->ucAttrib = 0;
+
+ if( ( pxFindParams->ulFlags & FIND_FLAG_CREATE_FLAG ) != 0 )
+ {
+ /* A file is to be created: keep track of the first free entry big enough
+ to hold this file name. */
+ pxFindParams->lFreeEntry = -1;
+ }
+ else
+ {
+ pxFindParams->lFreeEntry = 0;
+ }
+
+ xError = FF_InitEntryFetch( pxIOManager, pxFindParams->ulDirCluster, &xFetchContext );
+
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ for( pxDirEntry->usCurrentItem = 0; pxDirEntry->usCurrentItem < FF_MAX_ENTRIES_PER_DIRECTORY; pxDirEntry->usCurrentItem++ )
+ {
+ if( ( src == NULL ) ||
+ ( src >= xFetchContext.pxBuffer->pucBuffer + ( FF_SIZEOF_SECTOR - FF_SIZEOF_DIRECTORY_ENTRY ) ) )
+ {
+ xError = FF_FetchEntryWithContext( pxIOManager, pxDirEntry->usCurrentItem, &xFetchContext, NULL );
+
+ if( FF_isERR( xError ) != pdFALSE )
+ {
+ break;
+ }
+ src = xFetchContext.pxBuffer->pucBuffer;
+ }
+ else
+ {
+ /* Advance 32 bytes. */
+ src += FF_SIZEOF_DIRECTORY_ENTRY;
+ }
+
+ if( FF_isEndOfDir( src ) )
+ {
+ /* 0x00 end-of-dir. */
+ break;
+ }
+
+ if( FF_isDeleted( src ) )
+ {
+ /* Entry not used or deleted. */
+ pxDirEntry->ucAttrib = 0;
+ if( ( pxFindParams->lFreeEntry < 0 ) && ( ++freeCount == entriesNeeded ) )
+ {
+ /* Remember the beginning entry in the sequential sequence. */
+ pxFindParams->lFreeEntry = ( pxDirEntry->usCurrentItem - ( entriesNeeded - 1 ) );
+ }
+ continue;
+ }
+
+ /* The current entry is in use, so reset the free-entry-counter */
+ freeCount = 0;
+ #if( ffconfigLFN_SUPPORT != 0 )
+ {
+ lastAttrib = pxDirEntry->ucAttrib;
+ }
+ #endif
+
+ pxDirEntry->ucAttrib = FF_getChar( src, FF_FAT_DIRENT_ATTRIB );
+
+ if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_LFN ) == FF_FAT_ATTR_LFN )
+ {
+ /* LFN Processing. */
+ #if( ffconfigLFN_SUPPORT != 0 )
+ {
+ if( ( xLFNCount == 0 ) || ( lastAttrib & FF_FAT_ATTR_LFN ) != FF_FAT_ATTR_LFN )
+ {
+ xLFNTotal = xLFNCount = ( BaseType_t )( src[ 0 ] & ~0x40 );
+ lfnItem = pxDirEntry->usCurrentItem;
+ ucCheckSum = FF_getChar( src, FF_FAT_LFN_CHECKSUM );
+ pcLastPtr[ -1 ] = '\0';
+ }
+
+ if( xLFNCount != 0 )
+ {
+ xLFNCount--;
+ pcCurPtr = pxDirEntry->pcFileName + ( xLFNCount * 13 );
+
+ /*
+ This section needs to extract the name and do the comparison
+ dependent on UNICODE settings in the FreeRTOSFATConfig.h file.
+ */
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ {
+ /* Add UTF-16 Routine here. */
+ /* Copy first 5 UTF-16 chars ( 10 bytes ). */
+ memcpy( pcCurPtr, &src[ FF_FAT_LFN_NAME_1 ], 10 );
+ /* Increment Filename pointer 5 utf16 chars. */
+ pcCurPtr += 5;
+
+ /* Copy next 6 chars ( 12 bytes ). */
+ memcpy( pcCurPtr, &src[ FF_FAT_LFN_NAME_2 ], 12 );
+ pcCurPtr += 6;
+
+ /* You're getting the idea by now! */
+ memcpy( pcCurPtr, &src[ FF_FAT_LFN_NAME_3 ], 4 );
+ pcCurPtr += 2;
+ } /* ffconfigUNICODE_UTF16_SUPPORT */
+ #elif( ffconfigUNICODE_UTF8_SUPPORT != 0 )
+ {
+ /* UTF-8 Routine here. */
+ for( xIndex = 0; ( xIndex < 5 ) && ( pcCurPtr < pcLastPtr ); xIndex++ )
+ {
+ /* Was there a surrogate sequence? -- Add handling here. */
+ utf8Error = FF_Utf16ctoUtf8c( ( uint8_t * ) pcCurPtr, ( uint16_t * ) &src[ FF_FAT_LFN_NAME_1 + ( 2 * xIndex ) ], pcLastPtr - pcCurPtr );
+ if( utf8Error > 0 )
+ {
+ pcCurPtr += utf8Error;
+ }
+ else
+ {
+ if( FF_GETERROR( utf8Error ) == FF_ERR_UNICODE_INVALID_SEQUENCE )
+ {
+ /* Handle potential surrogate sequence across entries. */
+ }
+ }
+ }
+
+ for( xIndex = 0; xIndex < 6 && pcCurPtr < pcLastPtr; xIndex++ )
+ {
+ /* Was there a surrogate sequence? -- To add handling here. */
+ utf8Error = FF_Utf16ctoUtf8c( ( uint8_t * ) pcCurPtr, ( uint16_t * ) &src[ FF_FAT_LFN_NAME_2 + ( 2 * xIndex ) ], pcLastPtr - pcCurPtr );
+ if( utf8Error > 0 )
+ {
+ pcCurPtr += utf8Error;
+ }
+ else
+ {
+ if( FF_GETERROR( utf8Error ) == FF_ERR_UNICODE_INVALID_SEQUENCE )
+ {
+ /* Handle potential surrogate sequence across entries. */
+ }
+ }
+ }
+
+ for( xIndex = 0; xIndex < 2 && pcCurPtr < pcLastPtr; xIndex++ )
+ {
+ /* Was there a surrogate sequence? -- To add handling here. */
+ utf8Error = FF_Utf16ctoUtf8c( ( uint8_t * ) pcCurPtr, ( uint16_t * ) &src[ FF_FAT_LFN_NAME_3 + ( 2 * xIndex ) ], pcLastPtr - pcCurPtr );
+ if( utf8Error > 0 )
+ {
+ pcCurPtr += utf8Error;
+ }
+ else
+ {
+ if( FF_GETERROR( utf8Error ) == FF_ERR_UNICODE_INVALID_SEQUENCE )
+ {
+ /* Handle potential surrogate sequence across entries. */
+ }
+ }
+ }
+ } /* ffconfigUNICODE_UTF8_SUPPORT */
+ #else
+ { /* use ASCII notation. */
+ for( xIndex = 0; ( xIndex < 10 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 )
+ {
+ *( pcCurPtr++ ) = src[ FF_FAT_LFN_NAME_1 + xIndex ];
+ }
+ for( xIndex = 0; ( xIndex < 12 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 )
+ {
+ *( pcCurPtr++ ) = src[ FF_FAT_LFN_NAME_2 + xIndex ];
+ }
+
+ for( xIndex = 0; ( xIndex < 4 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 )
+ {
+ *( pcCurPtr++ ) = src[ FF_FAT_LFN_NAME_3 + xIndex ];
+ }
+ }
+ #endif /* ( ffconfigUNICODE_UTF16_SUPPORT == 0 ) && !( ffconfigUNICODE_UTF8_SUPPORT == 0 ) */
+ if( ( xLFNCount == xLFNTotal - 1 ) && ( pcCurPtr < pcLastPtr ) )
+ {
+ *pcCurPtr = '\0'; /* Important when name len is multiple of 13. */
+ }
+ } /* if( xLFNCount ) */
+ }
+ #endif /* ffconfigLFN_SUPPORT */
+ continue;
+ }
+
+ if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_VOLID ) == FF_FAT_ATTR_VOLID )
+ {
+ #if( ffconfigLFN_SUPPORT != 0 )
+ {
+ xLFNTotal = 0;
+ }
+ #endif /* ffconfigLFN_SUPPORT */
+ continue;
+ }
+
+ #if( ffconfigLFN_SUPPORT != 0 )
+ if( ( xLFNTotal == 0 ) || ( ucCheckSum != FF_CreateChkSum( src ) ) )
+ #endif /* ffconfigLFN_SUPPORT */
+ {
+ /* This entry has only a short name, or the checksum isn't correct
+ * Use the short name for comparison */
+ memcpy( pxDirEntry->pcFileName, src, 11 );
+ FF_ProcessShortName( ( char * ) pxDirEntry->pcFileName );
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ {
+ /* FileName now contains a 8-bit short name
+ Expand it to a FF_T_WCHAR string. */
+ FF_ShortNameExpand( pxDirEntry->pcFileName );
+ }
+ #endif /* ffconfigUNICODE_UTF16_SUPPORT */
+ #if( ffconfigLFN_SUPPORT != 0)
+ {
+ xLFNTotal = 0;
+ }
+ #endif /* ffconfigLFN_SUPPORT */
+ }
+
+ /* This function FF_FindEntryInDir( ) is either called with
+ * pa_Attrib==0 or with pa_Attrib==FF_FAT_ATTR_DIR
+ * In the last case the caller is looking for a directory */
+ if( ( pxDirEntry->ucAttrib & pa_Attrib ) == pa_Attrib )
+ {
+ if( testShortname )
+ {
+ /* Both strings are stored in the directory format
+ * e.g. "README TXT", without a dot */
+ if( memcmp( src, pxFindParams->pcEntryBuffer, 11 ) == 0 )
+ {
+ pxFindParams->ulFlags |= FIND_FLAG_SHORTNAME_CHECKED | FIND_FLAG_SHORTNAME_FOUND;
+ }
+ }
+
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ if( wcsicmp( ( const char * )pcName, ( const char * )pxDirEntry->pcFileName ) == 0 )
+ #else
+ if( FF_stricmp( ( const char * )pcName, ( const char * )pxDirEntry->pcFileName ) == 0 )
+ #endif /* ffconfigUNICODE_UTF16_SUPPORT */
+ {
+ /* Finally get the complete information. */
+ #if( ffconfigLFN_SUPPORT != 0 )
+ if( xLFNTotal )
+ {
+ xError = FF_PopulateLongDirent( pxIOManager, pxDirEntry, ( uint16_t ) lfnItem, &xFetchContext );
+ if( FF_isERR( xError ) )
+ {
+ break;
+ }
+ }
+ else
+ #endif /* ffconfigLFN_SUPPORT */
+ {
+ FF_PopulateShortDirent( pxIOManager, pxDirEntry, src );
+ /* HT: usCurrentItem wasn't increased here. */
+ pxDirEntry->usCurrentItem++;
+ }
+ /* Object found, the cluster number will be returned. */
+ xResult = pxDirEntry->ulObjectCluster;
+ break;
+ }
+ }
+ #if( ffconfigLFN_SUPPORT != 0 )
+ {
+ xLFNTotal = 0;
+ }
+ #endif
+ } /* for( ; pxDirEntry->usCurrentItem < FF_MAX_ENTRIES_PER_DIRECTORY; pxDirEntry->usCurrentItem++ ) */
+
+ {
+ FF_Error_t xTempError;
+ xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ xError = xTempError;
+ }
+ }
+ } /* if( FF_isERR( xError ) == pdFALSE ) */
+
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ /* If a free entry wasn't found yet, put it to the current (last) item */
+ if( pxFindParams->lFreeEntry < 0 )
+ {
+ pxFindParams->lFreeEntry = pxDirEntry->usCurrentItem;
+ }
+
+ /* If we were checking the existence of the short-name
+ set the Checked flag now */
+ if( testShortname )
+ {
+ pxFindParams->ulFlags |= FIND_FLAG_SHORTNAME_CHECKED;
+ }
+ }
+
+ if( pxError != NULL )
+ {
+ *pxError = xError;
+ }
+
+ return xResult;
+} /* FF_FindEntryInDir() */
+/*-----------------------------------------------------------*/
+
+
+/**
+ * @private
+ **/
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+uint32_t FF_FindDir( FF_IOManager_t *pxIOManager, const FF_T_WCHAR *pcPath, uint16_t pathLen, FF_Error_t *pxError )
+#else
+uint32_t FF_FindDir( FF_IOManager_t *pxIOManager, const char *pcPath, uint16_t pathLen, FF_Error_t *pxError )
+#endif
+{
+uint16_t it = 0; /* Re-entrancy Variables for FF_strtok( ). */
+BaseType_t last = pdFALSE;
+FF_DirEnt_t xMyDirectory;
+FF_FindParams_t xFindParams;
+FF_Error_t xError;
+BaseType_t xFound;
+
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ FF_T_WCHAR mytoken[ ffconfigMAX_FILENAME ];
+ FF_T_WCHAR *token;
+#else
+ char mytoken[ ffconfigMAX_FILENAME ];
+ char *pcToken;
+#endif
+
+#if( ffconfigPATH_CACHE != 0 )
+ BaseType_t xIndex;
+#endif
+
+ memset( &xFindParams, '\0', sizeof( xFindParams ) );
+ xFindParams.ulDirCluster = pxIOManager->xPartition.ulRootDirCluster;
+
+ xError = FF_ERR_NONE;
+
+ if( pathLen <= 1 )
+ {
+ /* Must be the root directory.
+ 'xFindParams.ulDirCluster' will be returned containing 'pxIOManager->xPartition.ulRootDirCluster'. */
+ xFound = pdTRUE;
+ }
+ else
+ {
+ /* Only the root directory '/' shall have a trailing slash in its name. */
+ if( ( pcPath[ pathLen - 1 ] == '\\' ) || ( pcPath[ pathLen - 1 ] == '/' ) )
+ {
+ pathLen--;
+ }
+ xFound = pdFALSE;
+
+ #if( ffconfigPATH_CACHE != 0 ) /* Is the requested pcPath in the PATH CACHE? */
+ {
+ FF_PendSemaphore( pxIOManager->pvSemaphore ); /* Thread safety on shared object! */
+ {
+ for( xIndex = 0; xIndex < ffconfigPATH_CACHE_DEPTH; xIndex++ )
+ {
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ if( wcslen( pxIOManager->xPartition.pxPathCache[ xIndex ].pcPath ) == ( size_t )pathLen )
+ #else
+ if( strlen( pxIOManager->xPartition.pxPathCache[ xIndex ].pcPath ) == ( size_t )pathLen )
+ #endif
+ {
+ if( FF_strmatch( pxIOManager->xPartition.pxPathCache[ xIndex ].pcPath, pcPath, pathLen ) )
+ {
+ xFindParams.ulDirCluster = pxIOManager->xPartition.pxPathCache[ xIndex ].ulDirCluster;
+ xFound = pdTRUE;
+ break;
+ }
+ }
+ }
+ }
+ FF_ReleaseSemaphore( pxIOManager->pvSemaphore );
+ }
+ #endif /* ffconfigPATH_CACHE */
+ }
+ if( xFound == pdFALSE )
+ {
+ pcToken = FF_strtok( pcPath, mytoken, &it, &last, pathLen );
+
+ do
+ {
+ xMyDirectory.usCurrentItem = 0;
+ xFindParams.ulDirCluster = FF_FindEntryInDir( pxIOManager, &xFindParams, pcToken, ( uint8_t ) FF_FAT_ATTR_DIR, &xMyDirectory, &xError );
+
+ if( xFindParams.ulDirCluster == 0ul )
+ {
+ break;
+ }
+
+ pcToken = FF_strtok( pcPath, mytoken, &it, &last, pathLen );
+
+ } while( pcToken != NULL );
+ if( ( pcToken != NULL ) &&
+ ( ( FF_isERR( xError ) == pdFALSE ) || ( FF_GETERROR( xError ) == FF_ERR_DIR_END_OF_DIR ) ) )
+ {
+ xError = ( FF_Error_t ) ( FF_FINDDIR | FF_ERR_FILE_INVALID_PATH );
+ }
+ #if( ffconfigPATH_CACHE != 0 ) /* Update the PATH CACHE with a new PATH. */
+ {
+ /* Only cache if the dir was actually found! */
+ if( ( FF_isERR( xError ) == pdFALSE ) && ( xFindParams.ulDirCluster != 0ul ) )
+ {
+ FF_PendSemaphore( pxIOManager->pvSemaphore );
+ {
+ if( pathLen < ffconfigMAX_FILENAME ) /* Ensure the PATH won't cause a buffer overrun. */
+ {
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ {
+ memcpy( pxIOManager->xPartition.pxPathCache[ pxIOManager->xPartition.ulPCIndex ].pcPath, pcPath, pathLen * sizeof( FF_T_WCHAR ) );
+ }
+ #else
+ {
+ memcpy( pxIOManager->xPartition.pxPathCache[ pxIOManager->xPartition.ulPCIndex ].pcPath, pcPath, pathLen );
+ }
+ #endif
+
+ pxIOManager->xPartition.pxPathCache[ pxIOManager->xPartition.ulPCIndex ].pcPath[ pathLen ] = '\0';
+ pxIOManager->xPartition.pxPathCache[ pxIOManager->xPartition.ulPCIndex ].ulDirCluster = xFindParams.ulDirCluster;
+
+ pxIOManager->xPartition.ulPCIndex += 1;
+ if( pxIOManager->xPartition.ulPCIndex >= ffconfigPATH_CACHE_DEPTH )
+ {
+ pxIOManager->xPartition.ulPCIndex = 0;
+ }
+ }
+ }
+ FF_ReleaseSemaphore( pxIOManager->pvSemaphore );
+ }
+ }
+ #endif /* ffconfigPATH_CACHE */
+ } /* if( pathLen > 1 ) */
+
+ if( pxError != NULL )
+ {
+ *pxError = xError;
+ }
+
+ return xFindParams.ulDirCluster;
+} /* FF_FindDir() */
+/*-----------------------------------------------------------*/
+
+
+#if( ffconfigSHORTNAME_CASE != 0 )
+ /**
+ * @private
+ * For short-name entries, NT/XP etc store case information in byte 0x0c
+ * Use this to show proper case of "README.txt" or "source.H"
+ **/
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ static void FF_CaseShortName( FF_T_WCHAR *pcName, uint8_t attrib )
+ #else
+ static void FF_CaseShortName( char *pcName, uint8_t attrib )
+ #endif
+ {
+ uint8_t testAttrib = FF_FAT_CASE_ATTR_BASE;
+
+ for ( ; *pcName != '\0'; pcName++ )
+ {
+ if( *pcName == '.' )
+ {
+ testAttrib = FF_FAT_CASE_ATTR_EXT;
+ }
+ else if ( ( attrib & testAttrib ) != 0 )
+ {
+ if( ( *pcName >= 'A' ) && ( *pcName <= 'Z' ) )
+ {
+ *pcName += 0x20;
+ }
+ }
+ else if( ( *pcName >= 'a' ) && ( *pcName <= 'z' ) )
+ {
+ *pcName -= 0x20;
+ }
+ }
+ }
+#endif /* ffconfigSHORTNAME_CASE */
+/*-----------------------------------------------------------*/
+
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ /**
+ * @private
+ * Expand a short-name, adding a zero after each character
+ **/
+
+ static void FF_ShortNameExpand( FF_T_WCHAR *pFileName )
+ {
+ int8_t *pSource = ( ( int8_t * ) pFileName ) + 13;
+ int8_t *pTarget = ( ( int8_t * ) pFileName ) + 26;
+ /* "aaaaaaaa.eee": 12 characters plus a zero makes 13
+ * Copy from the right to the left */
+ while( pTarget > ( int8_t * ) pFileName )
+ {
+ pTarget[ -1 ] = 0;
+ pTarget[ -2 ] = *( --pSource );
+ pTarget -= 2;
+ }
+ }
+#endif /* ffconfigUNICODE_UTF16_SUPPORT */
+/*-----------------------------------------------------------*/
+
+/**
+ * @private
+ **/
+
+static void FF_ProcessShortName( char *pcName )
+{
+ char pcShortName[ 13 ];
+ char *pcTarget = pcName;
+ int iSource;
+ memcpy( pcShortName, pcName, 11 );
+
+ for( iSource = 0; iSource < 11; iSource++ )
+ {
+ if( pcShortName[ iSource ] == 0x20 )
+ {
+ if( iSource >= 8 )
+ {
+ break;
+ }
+ iSource = 7;
+ }
+ else
+ {
+ if( iSource == 8 )
+ {
+ *( pcTarget++ ) = '.';
+ }
+ *( pcTarget++ ) = pcShortName[ iSource ];
+ }
+ }
+ *pcTarget = '\0';
+}
+/*-----------------------------------------------------------*/
+
+
+#if( ffconfigTIME_SUPPORT != 0 )
+ static void FF_PlaceTime( uint8_t *pucEntryBuffer, uint32_t Offset, FF_SystemTime_t *pxTime )
+ {
+ uint16_t myShort;
+
+ /* HT time changes:
+ E.g. Unzip needs to use original time rather than
+ the result of FF_GetSystemTime */
+
+ myShort = 0;
+ myShort |= ( ( pxTime->Hour << 11 ) & 0xF800 );
+ myShort |= ( ( pxTime->Minute << 5 ) & 0x07E0 );
+ myShort |= ( ( pxTime->Second / 2 ) & 0x001F );
+ FF_putShort( pucEntryBuffer, ( uint16_t ) Offset, myShort );
+ }
+#endif /* ffconfigTIME_SUPPORT */
+/*-----------------------------------------------------------*/
+
+
+#if( ffconfigTIME_SUPPORT != 0 )
+ static void FF_PlaceDate( uint8_t *pucEntryBuffer, uint32_t Offset, FF_SystemTime_t *pxTime )
+ {
+ uint16_t myShort;
+
+ /* HT time changes:
+ Unzip needs to use original date rather than
+ the current date, so make it a parameter. */
+ myShort = 0;
+ myShort |= ( ( ( pxTime->Year- 1980 ) << 9 ) & 0xFE00 ) ;
+ myShort |= ( ( pxTime->Month << 5 ) & 0x01E0 );
+ myShort |= ( pxTime->Day & 0x001F );
+ FF_putShort( pucEntryBuffer, ( uint16_t ) Offset, myShort );
+ }
+#endif /* ffconfigTIME_SUPPORT */
+/*-----------------------------------------------------------*/
+
+
+#if( ffconfigTIME_SUPPORT != 0 )
+ static void FF_GetTime( FF_SystemTime_t *pxTime, const uint8_t *pucEntryBuffer, uint32_t Offset )
+ {
+ uint16_t myShort;
+ myShort = FF_getShort( pucEntryBuffer, ( uint16_t ) Offset );
+ pxTime->Hour = ( ( ( myShort & 0xF800 ) >> 11 ) & 0x001F );
+ pxTime->Minute = ( ( ( myShort & 0x07E0 ) >> 5 ) & 0x003F );
+ pxTime->Second = 2 * ( myShort & 0x01F );
+ }
+#endif /* ffconfigTIME_SUPPORT */
+/*-----------------------------------------------------------*/
+
+
+#if( ffconfigTIME_SUPPORT != 0 )
+ static void FF_GetDate( FF_SystemTime_t *pxTime, const uint8_t *pucEntryBuffer, uint32_t Offset )
+ {
+ uint16_t myShort;
+ myShort = FF_getShort( pucEntryBuffer, ( uint16_t ) Offset );
+ pxTime->Year = 1980 + ( ( ( myShort & 0xFE00 ) >> 9 ) & 0x07F );
+ pxTime->Month = ( ( ( myShort & 0x01E0 ) >> 5 ) & 0x000F );
+ pxTime->Day = myShort & 0x01F;
+ }
+#endif /* ffconfigTIME_SUPPORT */
+/*-----------------------------------------------------------*/
+
+void FF_PopulateShortDirent( FF_IOManager_t *pxIOManager, FF_DirEnt_t *pxDirEntry, const uint8_t *pucEntryBuffer )
+{
+ memcpy( pxDirEntry->pcFileName, pucEntryBuffer, 11 ); /* Copy the filename into the Dirent object. */
+#if( ffconfigLFN_SUPPORT != 0 ) && ( ffconfigINCLUDE_SHORT_NAME != 0 )
+ memcpy( pxDirEntry->pcShortName, pucEntryBuffer, 11 );
+ pxDirEntry->pcShortName[ 11 ] = '\0';
+ FF_ProcessShortName( pxDirEntry->pcShortName ); /* For debuggers only. */
+#endif
+ FF_ProcessShortName( ( char * ) pxDirEntry->pcFileName ); /* Format the shortname, for pleasant viewing. */
+
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ /* FileName contains a 8-bit short name
+ * Expand it to a FF_T_WCHAR string */
+ FF_ShortNameExpand( pxDirEntry->pcFileName );
+#endif
+
+ ( void )pxIOManager; /* Silence a compiler warning, about not referencing pxIOManager. */
+
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ FF_tolower( pxDirEntry->pcFileName, ( uint32_t )wcslen( pxDirEntry->pcFileName ) );
+#else
+ FF_tolower( pxDirEntry->pcFileName, ( uint32_t )strlen( pxDirEntry->pcFileName ) );
+#endif
+
+ /* Get the item's Cluster address. */
+ pxDirEntry->ulObjectCluster =
+ ( ( uint32_t )FF_getShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH ) << 16 ) |
+ ( uint32_t )FF_getShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW );
+#if( ffconfigTIME_SUPPORT != 0 )
+ /* Get the creation Time & Date. */
+ FF_GetTime( &pxDirEntry->xCreateTime, pucEntryBuffer, FF_FAT_DIRENT_CREATE_TIME );
+ FF_GetDate( &pxDirEntry->xCreateTime, pucEntryBuffer, FF_FAT_DIRENT_CREATE_DATE );
+ /* Get the modified Time & Date
+ HT Here xCreateTime became xModifiedTime: */
+ FF_GetTime( &pxDirEntry->xModifiedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_TIME );
+ FF_GetDate( &pxDirEntry->xModifiedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_DATE );
+ /* Get the last accessed Date. */
+ FF_GetDate( &pxDirEntry->xAccessedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTACC_DATE );
+ pxDirEntry->xAccessedTime.Hour = 0;
+ pxDirEntry->xAccessedTime.Minute = 0;
+ pxDirEntry->xAccessedTime.Second = 0;
+#endif
+ /* Get the filesize. */
+ pxDirEntry->ulFileSize = FF_getLong( pucEntryBuffer, ( uint16_t )( FF_FAT_DIRENT_FILESIZE ) );
+ /* Get the attribute. */
+ pxDirEntry->ucAttrib = FF_getChar( pucEntryBuffer, ( uint16_t )( FF_FAT_DIRENT_ATTRIB ) );
+}
+/*-----------------------------------------------------------*/
+
+
+/* Initialises a context object for FF_FetchEntryWithContext( */
+FF_Error_t FF_InitEntryFetch( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster, FF_FetchContext_t *pxContext )
+{
+ FF_Error_t xError;
+
+ memset( pxContext, 0, sizeof( FF_FetchContext_t ) );
+
+ /* Get the total length of the chain. */
+ pxContext->ulChainLength = FF_GetChainLength( pxIOManager, ulDirCluster, NULL, &xError );
+
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ pxContext->ulDirCluster = ulDirCluster;
+ pxContext->ulCurrentClusterLCN = ulDirCluster;
+
+ if( pxIOManager->xPartition.ucType != FF_T_FAT32 )
+ {
+ /* Handle Root Dirs that don't have cluster chains! */
+ if( pxContext->ulDirCluster == pxIOManager->xPartition.ulRootDirCluster )
+ {
+ /* This is a RootDIR, special consideration needs to be made, because it doesn't have a Cluster chain! */
+ pxContext->ulChainLength = pxIOManager->xPartition.ulRootDirSectors / pxIOManager->xPartition.ulSectorsPerCluster;
+ if( pxContext->ulChainLength == 0 ) /* Some media has ulRootDirSectors < ulSectorsPerCluster. This is wrong, as it should be atleast 1 cluster! */
+ {
+ pxContext->ulChainLength = 1;
+ }
+ }
+ }
+ }
+
+ return xError;
+} /* FF_InitEntryFetch() */
+/*-----------------------------------------------------------*/
+
+FF_Error_t FF_CleanupEntryFetch( FF_IOManager_t *pxIOManager, FF_FetchContext_t *pxContext )
+{
+ FF_Error_t xError = FF_ERR_NONE;
+ if( pxContext->pxBuffer )
+ {
+ xError = FF_ReleaseBuffer( pxIOManager, pxContext->pxBuffer );
+ pxContext->pxBuffer = NULL;
+ }
+
+ return xError;
+} /* FF_CleanupEntryFetch() */
+/*-----------------------------------------------------------*/
+
+/**
+ * @private
+ * @brief Find the cluster for a given Entry within a directory
+ * @brief Make an exception for the root directory ( non FAT32 only ):
+ * @brief Just calculate the cluster ( don't consult the actual FAT )
+ *
+ * @param pxIOManager FF_IOManager_t object that was created by FF_CreateIOManger( ).
+ * @param ulEntry The sequence number of the entry of interest
+ * @param pxContext Context of current search
+ *
+ * @Return FF_ERR_NONE on success
+ * @Return Possible error returned by FF_TraverseFAT( ) or END_OF_DIR
+ *
+ * Side effects:
+ * - pxContext->ulCurrentClusterNum : relative cluster number ( 0 <= Num < ulChainLength )
+ * - pxContext->ulCurrentClusterLCN : fysical cluster on the partition
+ **/
+
+static FF_Error_t FF_Traverse( FF_IOManager_t *pxIOManager, uint32_t ulEntry, FF_FetchContext_t *pxContext )
+{
+uint32_t ulClusterNum = FF_getClusterChainNumber( pxIOManager, ulEntry, ( uint16_t ) FF_SIZEOF_DIRECTORY_ENTRY );
+FF_Error_t xError = FF_ERR_NONE;
+
+ /* Check if we're past the last cluster ( ulChainLength is also valid for root sectors ). */
+ if( ( ulClusterNum + 1 ) > pxContext->ulChainLength )
+ {
+ xError = FF_ERR_DIR_END_OF_DIR | FF_TRAVERSE; /* End of Dir was reached! */
+ }
+ else if( ( pxIOManager->xPartition.ucType != FF_T_FAT32 ) &&
+ ( pxContext->ulDirCluster == pxIOManager->xPartition.ulRootDirCluster ) )
+ {
+ /* Double-check if the entry number isn't too high. */
+ if( ulEntry > ( ( pxIOManager->xPartition.ulRootDirSectors * pxIOManager->xPartition.usBlkSize ) / FF_SIZEOF_DIRECTORY_ENTRY ) )
+ {
+ xError = ( FF_Error_t ) ( FF_ERR_DIR_END_OF_DIR | FF_FETCHENTRYWITHCONTEXT );
+ }
+ else
+ {
+ pxContext->ulCurrentClusterLCN = pxContext->ulDirCluster;
+ }
+ }
+ else if( ulClusterNum != pxContext->ulCurrentClusterNum )
+ {
+ /* Traverse the fat gently! */
+ if( ulClusterNum > pxContext->ulCurrentClusterNum )
+ {
+ /* Start traverse from the current entry. */
+ pxContext->ulCurrentClusterLCN = FF_TraverseFAT( pxIOManager, pxContext->ulCurrentClusterLCN, ( ulClusterNum - pxContext->ulCurrentClusterNum ), &xError );
+ }
+ else
+ {
+ /* Start traverse from the beginning. */
+ pxContext->ulCurrentClusterLCN = FF_TraverseFAT( pxIOManager, pxContext->ulDirCluster, ulClusterNum, &xError );
+ }
+ }
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ pxContext->ulCurrentClusterNum = ulClusterNum;
+ }
+
+ return xError;
+} /* FF_Traverse() */
+/*-----------------------------------------------------------*/
+
+FF_Error_t FF_FetchEntryWithContext( FF_IOManager_t *pxIOManager, uint32_t ulEntry, FF_FetchContext_t *pxContext, uint8_t *pEntryBuffer )
+{
+ uint32_t ulItemLBA;
+ uint32_t ulRelItem;
+ FF_Error_t xError;
+
+ xError = FF_Traverse( pxIOManager, ulEntry, pxContext );
+
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ ulRelItem = FF_getMinorBlockEntry ( pxIOManager, ulEntry, ( uint32_t )FF_SIZEOF_DIRECTORY_ENTRY );
+
+ ulItemLBA = FF_Cluster2LBA ( pxIOManager, pxContext->ulCurrentClusterLCN ) +
+ FF_getMajorBlockNumber( pxIOManager, ulEntry, ( uint32_t ) FF_SIZEOF_DIRECTORY_ENTRY );
+ if( ( pxIOManager->xPartition.ucType != FF_T_FAT32 ) &&
+ ( pxContext->ulDirCluster == pxIOManager->xPartition.ulRootDirCluster ) )
+ {
+ ulItemLBA += ( ulEntry / ( ( pxIOManager->xPartition.usBlkSize * pxIOManager->xPartition.ulSectorsPerCluster ) /
+ FF_SIZEOF_DIRECTORY_ENTRY ) * pxIOManager->xPartition.ulSectorsPerCluster );
+ }
+
+ ulItemLBA = FF_getRealLBA ( pxIOManager, ulItemLBA ) + FF_getMinorBlockNumber( pxIOManager, ulRelItem, ( uint32_t )FF_SIZEOF_DIRECTORY_ENTRY );
+
+ if( ( pxContext->pxBuffer == NULL ) ||
+ ( pxContext->pxBuffer->ulSector != ulItemLBA ) ||
+ ( ( pxContext->pxBuffer->ucMode & FF_MODE_WRITE ) != 0 ) )
+ {
+ if( pxContext->pxBuffer != NULL )
+ {
+ xError = FF_ReleaseBuffer( pxIOManager, pxContext->pxBuffer );
+ pxContext->pxBuffer = NULL;
+ }
+
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ pxContext->pxBuffer = FF_GetBuffer( pxIOManager, ulItemLBA, FF_MODE_READ );
+ if( pxContext->pxBuffer == NULL )
+ {
+ xError = ( FF_Error_t ) ( FF_ERR_DEVICE_DRIVER_FAILED | FF_FETCHENTRYWITHCONTEXT );
+ }
+ }
+ }
+
+ if ( ( pEntryBuffer != NULL ) && ( pxContext->pxBuffer != NULL ) )
+ {
+ memcpy( pEntryBuffer, pxContext->pxBuffer->pucBuffer + ( ulRelItem * FF_SIZEOF_DIRECTORY_ENTRY ), FF_SIZEOF_DIRECTORY_ENTRY );
+ }
+ }
+
+ return xError;
+} /* FF_FetchEntryWithContext() */
+/*-----------------------------------------------------------*/
+
+
+FF_Error_t FF_PushEntryWithContext( FF_IOManager_t *pxIOManager, uint32_t ulEntry, FF_FetchContext_t *pxContext, uint8_t *pEntryBuffer )
+{
+ uint32_t ulItemLBA;
+ uint32_t ulRelItem;
+ FF_Error_t xError;
+
+ xError = FF_Traverse( pxIOManager, ulEntry, pxContext );
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ ulRelItem = FF_getMinorBlockEntry ( pxIOManager, ulEntry, ( uint32_t ) FF_SIZEOF_DIRECTORY_ENTRY );
+
+ ulItemLBA = FF_Cluster2LBA ( pxIOManager, pxContext->ulCurrentClusterLCN ) + FF_getMajorBlockNumber( pxIOManager, ulEntry, ( uint32_t ) FF_SIZEOF_DIRECTORY_ENTRY );
+ if( ( pxIOManager->xPartition.ucType != FF_T_FAT32 ) &&
+ ( pxContext->ulDirCluster == pxIOManager->xPartition.ulRootDirCluster ) )
+ {
+ ulItemLBA += ( ulEntry /
+ ( ( pxIOManager->xPartition.usBlkSize * pxIOManager->xPartition.ulSectorsPerCluster ) / FF_SIZEOF_DIRECTORY_ENTRY ) * pxIOManager->xPartition.ulSectorsPerCluster );
+ }
+
+ ulItemLBA = FF_getRealLBA ( pxIOManager, ulItemLBA ) + FF_getMinorBlockNumber( pxIOManager, ulRelItem, ( uint32_t )FF_SIZEOF_DIRECTORY_ENTRY );
+
+ if( ( pxContext->pxBuffer == NULL ) ||
+ ( pxContext->pxBuffer->ulSector != ulItemLBA ) ||
+ ( ( pxContext->pxBuffer->ucMode & FF_MODE_WRITE ) == 0 ) )
+ {
+ if( pxContext->pxBuffer != NULL )
+ {
+ xError = FF_ReleaseBuffer( pxIOManager, pxContext->pxBuffer );
+ pxContext->pxBuffer = NULL;
+ }
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ pxContext->pxBuffer = FF_GetBuffer( pxIOManager, ulItemLBA, FF_MODE_WRITE );
+ if( pxContext->pxBuffer == NULL )
+ {
+ xError = ( FF_Error_t ) ( FF_ERR_DEVICE_DRIVER_FAILED | FF_FETCHENTRYWITHCONTEXT );
+ }
+ }
+ }
+
+ /* Now change the entry: */
+ if( pxContext->pxBuffer != NULL )
+ {
+ memcpy( pxContext->pxBuffer->pucBuffer + ( ulRelItem * FF_SIZEOF_DIRECTORY_ENTRY ), pEntryBuffer, FF_SIZEOF_DIRECTORY_ENTRY );
+ }
+ }
+
+ return xError;
+} /* FF_PushEntryWithContext() */
+/*-----------------------------------------------------------*/
+
+
+/**
+ * @private
+ **/
+FF_Error_t FF_GetEntry( FF_IOManager_t *pxIOManager, uint16_t usEntry, uint32_t ulDirCluster, FF_DirEnt_t *pxDirEntry )
+{
+/* A 32 byte directory entry. */
+uint8_t ucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];
+FF_FetchContext_t xFetchContext;
+FF_Error_t xError;
+
+#if( ffconfigLFN_SUPPORT == 0 )
+ BaseType_t xLFNCount;
+#endif
+ xError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );
+
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ xError = FF_FetchEntryWithContext( pxIOManager, usEntry, &xFetchContext, ucEntryBuffer );
+
+ if( ( FF_isERR( xError ) == pdFALSE ) &&
+ ( FF_isDeleted( ucEntryBuffer ) == pdFALSE ) )
+ {
+ if( FF_isEndOfDir( ucEntryBuffer ) != pdFALSE )
+ {
+ xError = ( FF_Error_t ) ( FF_ERR_DIR_END_OF_DIR | FF_GETENTRY );
+ }
+ else
+ {
+ pxDirEntry->ucAttrib = FF_getChar( ucEntryBuffer, ( uint16_t )( FF_FAT_DIRENT_ATTRIB ) );
+
+ if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_LFN ) == FF_FAT_ATTR_LFN )
+ {
+ #if( ffconfigLFN_SUPPORT != 0 )
+ xError = FF_PopulateLongDirent( pxIOManager, pxDirEntry, usEntry, &xFetchContext );
+ #else
+ /* LFN Processing. */
+ xLFNCount = ( BaseType_t )( ucEntryBuffer[0] & ~0x40 );
+ pxDirEntry->usCurrentItem += ( xLFNCount - 1 );
+ #endif
+ }
+ else if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_VOLID ) != FF_FAT_ATTR_VOLID )
+ {
+ FF_PopulateShortDirent( pxIOManager, pxDirEntry, ucEntryBuffer );
+ pxDirEntry->usCurrentItem += 1;
+ }
+ }
+ }
+ {
+ FF_Error_t xTempError;
+ xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ xError = xTempError;
+ }
+ }
+ }
+
+ return xError;
+} /* FF_GetEntry() */
+/*-----------------------------------------------------------*/
+
+
+FF_Error_t FF_PopulateLongDirent( FF_IOManager_t *pxIOManager, FF_DirEnt_t *pxDirEntry, uint16_t usEntry, FF_FetchContext_t *pxFetchContext )
+{
+/* First get the entire name as UTF-16 from the LFN's.
+Then transform into the API's native string format. */
+
+FF_Error_t xError;
+BaseType_t xNumLFNs;
+uint8_t ucCheckSum;
+/* A 32 byte directory entry. */
+uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];
+char pcShortName[ 13 ];
+
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ UBaseType_t uiLfnLength = 0;
+#endif
+
+#if( ffconfigUNICODE_UTF16_SUPPORT == 0 )
+ BaseType_t xIndex, y;
+#endif
+
+#if( ffconfigUNICODE_UTF16_SUPPORT == 0 ) && ( ffconfigUNICODE_UTF8_SUPPORT == 0 )
+ char *pcLastPtr = pxDirEntry->pcFileName + sizeof( pxDirEntry->pcFileName );
+ char *pcCurPtr;
+#endif
+
+#if( ffconfigUNICODE_UTF8_SUPPORT != 0 )
+ uint16_t nLfnBegin;
+ uint16_t usUtf8Len = 0;
+#endif /* ffconfigUNICODE_UTF8_SUPPORT */
+
+ do
+ {
+ xError = FF_FetchEntryWithContext( pxIOManager, usEntry++, pxFetchContext, pucEntryBuffer );
+ if( FF_isERR( xError ) )
+ {
+ /* After breaking from this do {} while ( pdFALSE ) loop, xResult will be returned. */
+ break;
+ }
+
+ xNumLFNs = ( BaseType_t )( pucEntryBuffer[0] & ~0x40 );
+ ucCheckSum = FF_getChar( pucEntryBuffer, FF_FAT_LFN_CHECKSUM );
+
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ {
+ /* UTF-16 Can simply get segments of the UTF-16 sequence
+ going forward in the directory entries ( but reversed order ). */
+ while( xNumLFNs > 0 )
+ {
+ /* Avoid stack intensive use of a UTF-16 buffer. Stream direct to FileName dirent field in correct format. */
+ /* memcpy direct! -UTF-16 support. */
+ memcpy( pxDirEntry->pcFileName + ( ( xNumLFNs - 1 ) * 13 ) + 0, &( pucEntryBuffer[ FF_FAT_LFN_NAME_1] ), 10 );
+ memcpy( pxDirEntry->pcFileName + ( ( xNumLFNs - 1 ) * 13 ) + 5, &( pucEntryBuffer[ FF_FAT_LFN_NAME_2] ), 12 );
+ memcpy( pxDirEntry->pcFileName + ( ( xNumLFNs - 1 ) * 13 ) + 11, &( pucEntryBuffer[ FF_FAT_LFN_NAME_3] ), 4 );
+ uiLfnLength += 13;
+
+ xError = FF_FetchEntryWithContext( pxIOManager, usEntry++, pxFetchContext, pucEntryBuffer );
+ if( FF_isERR( xError ) )
+ {
+ break;
+ }
+ xNumLFNs--;
+ }
+ if( FF_isERR( xError ) )
+ {
+ break;
+ }
+ pxDirEntry->pcFileName[ uiLfnLength ] = '\0';
+ }
+ #endif /* ffconfigUNICODE_UTF16_SUPPORT */
+
+ #if ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
+ {
+ /* UTF-8 Sequence, we can only convert this from the beginning, must receive entries in reverse. */
+ nLfnBegin = usEntry - 1;
+
+ for( xIndex = 0; xIndex < xNumLFNs; xIndex++ )
+ {
+ xError = FF_FetchEntryWithContext( pxIOManager, ( nLfnBegin + ( xNumLFNs - 1 ) - xIndex ), pxFetchContext, pucEntryBuffer );
+ if( FF_isERR( xError ) )
+ {
+ break;
+ }
+
+ /* Now have the first part of the UTF-16 sequence. Stream into a UTF-8 sequence. */
+ for( y = 0; y < 5; y++ )
+ {
+ xError = FF_Utf16ctoUtf8c( ( uint8_t * )&pxDirEntry->pcFileName[ usUtf8Len ],
+ ( uint16_t * )&pucEntryBuffer[ FF_FAT_LFN_NAME_1 + ( y * 2 ) ], sizeof( pxDirEntry->pcFileName ) - usUtf8Len );
+ if( xError > 0 )
+ {
+ usUtf8Len += ( uint16_t )xError;
+ }
+ }
+
+ for( y = 0; y < 6; y++ )
+ {
+ xError = FF_Utf16ctoUtf8c( ( uint8_t * )&pxDirEntry->pcFileName[ usUtf8Len ],
+ ( uint16_t * )&pucEntryBuffer[ FF_FAT_LFN_NAME_2 + ( y * 2 ) ], sizeof( pxDirEntry->pcFileName ) - usUtf8Len );
+ if( xError > 0 )
+ {
+ usUtf8Len += ( uint16_t )xError;
+ }
+ }
+
+ for( y = 0; y < 2; y++ )
+ {
+ xError = FF_Utf16ctoUtf8c( ( uint8_t * )&pxDirEntry->pcFileName[ usUtf8Len ],
+ ( uint16_t * )&pucEntryBuffer[ FF_FAT_LFN_NAME_3 + ( y * 2 ) ], sizeof( pxDirEntry->pcFileName ) - usUtf8Len );
+ if( xError > 0 )
+ {
+ usUtf8Len += ( uint16_t ) xError;
+ }
+ }
+ usEntry++;
+ }
+ if( FF_isERR( xError ) )
+ {
+ break;
+ }
+
+ pxDirEntry->pcFileName[ usUtf8Len ] = '\0';
+
+ /* Put Entry context to correct position. */
+ xError = FF_FetchEntryWithContext( pxIOManager, usEntry-1, pxFetchContext, pucEntryBuffer );
+ if( FF_isERR( xError ) )
+ {
+ break;
+ }
+ }
+ #endif /* ( ffconfigUNICODE_UTF8_SUPPORT != 0 ) */
+
+ #if( ffconfigUNICODE_UTF16_SUPPORT == 0 ) && ( ffconfigUNICODE_UTF8_SUPPORT == 0 ) /* No Unicode, simple ASCII. */
+ {
+ pcLastPtr[ -1 ] = '\0';
+ y = xNumLFNs;
+ while( xNumLFNs-- )
+ {
+ pcCurPtr = pxDirEntry->pcFileName + ( xNumLFNs * 13 );
+ for( xIndex = 0; ( xIndex < 10 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 )
+ {
+ *( pcCurPtr++ ) = pucEntryBuffer[ FF_FAT_LFN_NAME_1 + xIndex ];
+ }
+
+ for( xIndex = 0; ( xIndex < 12 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 )
+ {
+ *( pcCurPtr++ ) = pucEntryBuffer[ FF_FAT_LFN_NAME_2 + xIndex ];
+ }
+
+ for( xIndex = 0; ( xIndex < 4 ) && ( pcCurPtr < pcLastPtr ); xIndex += 2 )
+ {
+ *( pcCurPtr++ ) = pucEntryBuffer[ FF_FAT_LFN_NAME_3 + xIndex ];
+ }
+ if( ( xNumLFNs == ( y - 1 ) ) && ( pcCurPtr < pcLastPtr ) )
+ {
+ *pcCurPtr = '\0';
+ }
+ xError = FF_FetchEntryWithContext( pxIOManager, usEntry++, pxFetchContext, pucEntryBuffer );
+ if( FF_isERR( xError ) )
+ {
+ break;
+ }
+ }
+ if( FF_isERR( xError ) )
+ {
+ break;
+ }
+ }
+ #endif /* ( ffconfigUNICODE_UTF16_SUPPORT == 0 ) && ( ffconfigUNICODE_UTF8_SUPPORT == 0 ) */
+
+ /* Process the Shortname. -- LFN Transformation is now complete.
+ Process the ShortName Entry. */
+
+ /* if SHORTNAMES must be included, simple byte copy into shortname buffer. */
+ #if( ffconfigLFN_SUPPORT != 0 ) && ( ffconfigINCLUDE_SHORT_NAME != 0 )
+ {
+ memcpy( pxDirEntry->pcShortName, pucEntryBuffer, 11 );
+ pxDirEntry->pcShortName[ 11 ] = '\0';
+ FF_ProcessShortName( pxDirEntry->pcShortName );
+ }
+ #endif /* ( != 0 ffconfigLFN_SUPPORT ) && ( ffconfigINCLUDE_SHORT_NAME != 0 ) */
+
+ memcpy( pcShortName, pucEntryBuffer, 11 );
+ FF_ProcessShortName( pcShortName );
+ if( ucCheckSum != FF_CreateChkSum( pucEntryBuffer ) )
+ {
+ strcpy( pxDirEntry->pcFileName, pcShortName );
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ {
+ FF_ShortNameExpand( pxDirEntry->pcFileName );
+ }
+ #endif /* ffconfigUNICODE_UTF16_SUPPORT */
+ }
+
+ /* Finally fill in the other details. */
+ pxDirEntry->ulObjectCluster =
+ ( ( uint32_t )FF_getShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH ) << 16 ) |
+ ( uint32_t )FF_getShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW );
+
+ #if( ffconfigTIME_SUPPORT != 0 )
+ {
+ /* Get the creation Time & Date. */
+ FF_GetTime( &pxDirEntry->xCreateTime, pucEntryBuffer, FF_FAT_DIRENT_CREATE_TIME );
+ FF_GetDate( &pxDirEntry->xCreateTime, pucEntryBuffer, FF_FAT_DIRENT_CREATE_DATE );
+ /* Get the modified Time & Date. */
+ /* HT Here xCreateTime has become xModifiedTime, as it should: */
+ FF_GetTime( &pxDirEntry->xModifiedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_TIME );
+ FF_GetDate( &pxDirEntry->xModifiedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_DATE );
+ /* Get the last accessed Date. */
+ FF_GetDate( &pxDirEntry->xAccessedTime, pucEntryBuffer, FF_FAT_DIRENT_LASTACC_DATE );
+ /* HT Why should these times be zero'd ? */
+ pxDirEntry->xAccessedTime.Hour = 0;
+ pxDirEntry->xAccessedTime.Minute = 0;
+ pxDirEntry->xAccessedTime.Second = 0;
+ }
+ #endif /* ffconfigTIME_SUPPORT */
+
+ /* Get the filesize. */
+ pxDirEntry->ulFileSize = FF_getLong( pucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_FILESIZE ) );
+ /* Get the attribute. */
+ pxDirEntry->ucAttrib = FF_getChar( pucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_ATTRIB ) );
+
+ pxDirEntry->usCurrentItem = usEntry;
+ }
+ while ( pdFALSE );
+
+ return xError;
+} /* FF_PopulateLongDirent() */
+/*-----------------------------------------------------------*/
+
+/**
+ * @public
+ * @brief Find's the first directory entry for the provided path.
+ *
+ * All values recorded in pxDirEntry must be preserved to and between calls to
+ * FF_FindNext( ).
+ *
+ * If ffconfigFINDAPI_ALLOW_WILDCARDS is defined, then path will have the following behaviour:
+ *
+ * path = "\" - Open the root dir, and iterate through all items.
+ * path = "\*.c" - Open the root dir, showing only files matching *.c wildcard.
+ * path = "\sub1\newdir" - Get the DIRENT for the newdir directory in /sub1/ if one exists.
+ * path = "\sub1\newdir\" - Open the directory /sub1/newdir/ and iterate through all items.
+ * path = "\sub1\newdir\*.c" - Open the directory /sub1/newdir/ and iterate through all items matching the *.c wildcard.
+ *
+ * It is important to distinguish the differences in behaviour between opening a Find operation
+ * on a path like /sub1 and /sub1/. ( /sub1 gets the sub1 dirent from the / dir, whereas /sub/ opens the sub1 dir ).
+ *
+ * Note, as compatible with other similar APIs, FreeRTOS+FAT also accepts \sub1\* for the same behaviour as
+ * /sub1/.
+ *
+ * @param pxIOManager FF_IOManager_t object that was created by FF_CreateIOManger( ).
+ * @param pxDirEntry FF_DirEnt_t object to store the entry information.
+ * @param path String to of the path to the Dir being listed.
+ *
+ * @Return 0 on success
+ * @Return FF_ERR_DEVICE_DRIVER_FAILED if device access failed.
+ * @Return -2 if Dir was not found.
+ *
+ **/
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+FF_Error_t FF_FindFirst( FF_IOManager_t *pxIOManager, FF_DirEnt_t *pxDirEntry, const FF_T_WCHAR *pcPath )
+#else
+FF_Error_t FF_FindFirst( FF_IOManager_t *pxIOManager, FF_DirEnt_t *pxDirEntry, const char *pcPath )
+#endif
+{
+FF_Error_t xError;
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ uint16_t PathLen = ( uint16_t ) wcslen( pcPath );
+#else
+ uint16_t PathLen = ( uint16_t ) strlen( pcPath );
+#endif
+
+#if( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 )
+ BaseType_t xIndex = 0;
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ const FF_T_WCHAR *pcWildCard; /* Check for a Wild-card. */
+ #else
+ const char *pcWildCard; /* Check for a Wild-card. */
+ #endif
+#endif
+
+ memset( pxDirEntry, 0, sizeof( FF_DirEnt_t ) );
+
+ if( pxIOManager == NULL )
+ {
+ xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_FINDFIRST );
+ }
+#if( ffconfigREMOVABLE_MEDIA != 0 )
+ else if( ( pxIOManager->ucFlags & FF_IOMAN_DEVICE_IS_EXTRACTED ) != 0 )
+ {
+ xError = ( FF_Error_t ) ( FF_ERR_IOMAN_DRIVER_NOMEDIUM | FF_FINDFIRST );
+ }
+#endif /* ffconfigREMOVABLE_MEDIA */
+ else
+ {
+ #if( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 )
+ {
+ pxDirEntry->pcWildCard[0] = '\0'; /* WildCard blank if its not a wildCard. */
+
+ pcWildCard = &pcPath[ PathLen - 1 ];
+
+ if( PathLen != 0 )
+ {
+ /* Open the dir of the last token. */
+ while( ( *pcWildCard != '\\' ) && ( *pcWildCard != '/' ) )
+ {
+ xIndex++;
+ pcWildCard--;
+ if( PathLen == xIndex )
+ {
+ break;
+ }
+ }
+ }
+
+ pxDirEntry->ulDirCluster = FF_FindDir( pxIOManager, pcPath, PathLen - xIndex, &xError );
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ if( pxDirEntry->ulDirCluster != 0 )
+ {
+ /* Valid Dir found, copy the wildCard to filename! */
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ wcsncpy( pxDirEntry->pcWildCard, ++pcWildCard, ffconfigMAX_FILENAME );
+ #else
+ strncpy( pxDirEntry->pcWildCard, ++pcWildCard, ffconfigMAX_FILENAME );
+ #endif
+ if( pxDirEntry->pcWildCard[ xIndex - 1 ] == ':' )
+ {
+ pxDirEntry->xInvertWildCard = pdTRUE;
+ pxDirEntry->pcWildCard[ xIndex - 1 ] = '\0';
+ }
+ }
+ }
+ }
+ #else /* ffconfigFINDAPI_ALLOW_WILDCARDS */
+ {
+ /* Get the directory cluster, if it exists. */
+ pxDirEntry->ulDirCluster = FF_FindDir( pxIOManager, pcPath, PathLen, &xError );
+ }
+ #endif /* ffconfigFINDAPI_ALLOW_WILDCARDS */
+
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ if( pxDirEntry->ulDirCluster == 0 )
+ {
+ xError = ( FF_Error_t ) ( FF_ERR_DIR_INVALID_PATH | FF_FINDFIRST );
+ }
+ else
+ {
+ /* Initialise the Fetch Context. */
+ xError = FF_InitEntryFetch( pxIOManager, pxDirEntry->ulDirCluster, &( pxDirEntry->xFetchContext ) );
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ pxDirEntry->usCurrentItem = 0;
+ xError = FF_FindNext( pxIOManager, pxDirEntry );
+ }
+ }
+ }
+ }
+
+ return xError;
+} /* FF_FindFirst() */
+/*-----------------------------------------------------------*/
+
+/**
+ * @public
+ * @brief Get's the next Entry based on the data recorded in the FF_DirEnt_t object.
+ *
+ * All values recorded in pxDirEntry must be preserved to and between calls to
+ * FF_FindNext( ). Please see @see FF_FindFirst( ) for find initialisation.
+ *
+ * @param pxIOManager FF_IOManager_t object that was created by FF_CreateIOManger( ).
+ * @param pxDirEntry FF_DirEnt_t object to store the entry information. ( As initialised by FF_FindFirst( )).
+ *
+ * @Return FF_ERR_DEVICE_DRIVER_FAILED is device access failed.
+ *
+ **/
+FF_Error_t FF_FindNext( FF_IOManager_t *pxIOManager, FF_DirEnt_t *pxDirEntry )
+{
+FF_Error_t xError;
+BaseType_t xLFNCount;
+const uint8_t *pucEntryBuffer = NULL;
+#if( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 )
+ BaseType_t b;
+#endif
+
+ if( pxIOManager == NULL )
+ {
+ xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_FINDNEXT );
+ }
+#if( ffconfigREMOVABLE_MEDIA != 0 )
+ else if( ( pxIOManager->ucFlags & FF_IOMAN_DEVICE_IS_EXTRACTED ) != 0 )
+ {
+ xError = ( FF_Error_t ) ( FF_ERR_IOMAN_DRIVER_NOMEDIUM | FF_FINDNEXT );
+ }
+#endif /* ffconfigREMOVABLE_MEDIA */
+ else
+ {
+ xError = FF_ERR_NONE;
+ for( ; pxDirEntry->usCurrentItem < FF_MAX_ENTRIES_PER_DIRECTORY; pxDirEntry->usCurrentItem++ )
+ {
+ if( ( pucEntryBuffer == NULL ) ||
+ ( pucEntryBuffer >= ( pxDirEntry->xFetchContext.pxBuffer->pucBuffer + ( FF_SIZEOF_SECTOR - FF_SIZEOF_DIRECTORY_ENTRY ) ) ) )
+ {
+ xError = FF_FetchEntryWithContext( pxIOManager, pxDirEntry->usCurrentItem, &( pxDirEntry->xFetchContext ), NULL );
+
+ if( FF_isERR( xError ) )
+ {
+ break;
+ }
+
+ if( pucEntryBuffer == NULL )
+ {
+ pucEntryBuffer = pxDirEntry->xFetchContext.pxBuffer->pucBuffer +
+ ( FF_SIZEOF_DIRECTORY_ENTRY * ( pxDirEntry->usCurrentItem % ( FF_SIZEOF_SECTOR/FF_SIZEOF_DIRECTORY_ENTRY ) ) );
+ }
+ else
+ {
+ pucEntryBuffer = pxDirEntry->xFetchContext.pxBuffer->pucBuffer;
+ }
+ }
+ else
+ {
+ pucEntryBuffer += FF_SIZEOF_DIRECTORY_ENTRY;
+ }
+
+ if( FF_isDeleted( pucEntryBuffer ) != pdFALSE )
+ {
+ /* The entry is not in use or deleted. */
+ continue;
+ }
+
+ if( FF_isEndOfDir( pucEntryBuffer ) )
+ {
+ /* End of directory, generate a pseudo error 'DIR_END_OF_DIR'. */
+ xError = ( FF_Error_t ) ( FF_ERR_DIR_END_OF_DIR | FF_FINDNEXT );
+ break;
+ }
+
+ pxDirEntry->ucAttrib = FF_getChar( pucEntryBuffer, ( uint16_t ) ( FF_FAT_DIRENT_ATTRIB ) );
+
+ if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_LFN ) == FF_FAT_ATTR_LFN )
+ {
+ /* LFN Processing. */
+ xLFNCount = ( BaseType_t )( pucEntryBuffer[0] & ~0x40 );
+ /* Get the shortname and check if it is marked deleted. */
+ #if( ffconfigLFN_SUPPORT != 0 )
+ {
+ /* Reserve 32 bytes to hold one directory entry. */
+ uint8_t Buffer[ FF_SIZEOF_DIRECTORY_ENTRY ];
+
+ /* Fetch the shortname, and get it's checksum, or for a deleted item with
+ orphaned LFN entries. */
+ xError = FF_FetchEntryWithContext( pxIOManager, ( uint32_t ) ( pxDirEntry->usCurrentItem + xLFNCount ), &pxDirEntry->xFetchContext, Buffer );
+ if( FF_isERR( xError ) )
+ {
+ break;
+ }
+
+ if( FF_isDeleted( Buffer ) == pdFALSE )
+ {
+ xError = FF_PopulateLongDirent( pxIOManager, pxDirEntry, pxDirEntry->usCurrentItem, &pxDirEntry->xFetchContext );
+ if( FF_isERR( xError ) )
+ {
+ break;
+ }
+ #if( ffconfigINCLUDE_SHORT_NAME != 0 )
+ {
+ pxDirEntry->ucAttrib |= FF_FAT_ATTR_IS_LFN;
+ }
+ #endif
+
+ #if( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 )
+ {
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ if( wcscmp( pxDirEntry->pcWildCard, L"" ) )
+ #else
+ if( pxDirEntry->pcWildCard[0] )
+ #endif
+ {
+ b = FF_wildcompare( pxDirEntry->pcWildCard, pxDirEntry->pcFileName );
+ if( pxDirEntry->xInvertWildCard != pdFALSE )
+ {
+ b = !b;
+ }
+ if( b != 0 )
+ {
+ break;
+ }
+
+ /* 'usCurrentItem' has already incremented by FF_PopulateLongDirent(),
+ this loop will incremente it again. */
+ pxDirEntry->usCurrentItem -= 1;
+
+ /* xFetchContext/usCurrentItem have changed. Update
+ 'pucEntryBuffer' to point to the current buffer position. */
+ pucEntryBuffer = pxDirEntry->xFetchContext.pxBuffer->pucBuffer +
+ ( FF_SIZEOF_DIRECTORY_ENTRY * ( pxDirEntry->usCurrentItem % ( FF_SIZEOF_SECTOR/FF_SIZEOF_DIRECTORY_ENTRY ) ) );
+ }
+ else
+ {
+ break;
+ }
+ }
+ #else /* ffconfigFINDAPI_ALLOW_WILDCARDS == 0 */
+ {
+ /* usCurrentItem has been incremented by FF_PopulateLongDirent().
+ Entry will be returned. */
+ break;
+ }
+ #endif
+ }
+ }
+ #else /* ffconfigLFN_SUPPORT */
+ {
+ /* Increment 'usCurrentItem' with (xLFNCount-1),
+ the loop will do an extra increment. */
+ pxDirEntry->usCurrentItem += ( xLFNCount - 1 );
+ }
+ #endif /* ffconfigLFN_SUPPORT */
+ } /* ( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_LFN ) == FF_FAT_ATTR_LFN ) */
+ else if( ( pxDirEntry->ucAttrib & FF_FAT_ATTR_VOLID ) != FF_FAT_ATTR_VOLID )
+ {
+ /* If it's not a LFN entry, neither a Volume ID, it is a normal short name entry. */
+ FF_PopulateShortDirent( pxIOManager, pxDirEntry, pucEntryBuffer );
+ #if( ffconfigSHORTNAME_CASE != 0 )
+ {
+ /* Apply NT/XP+ bits to get correct case. */
+ FF_CaseShortName( pxDirEntry->pcFileName, FF_getChar( pucEntryBuffer, FF_FAT_CASE_OFFS ) );
+ }
+ #endif
+
+ #if( ffconfigFINDAPI_ALLOW_WILDCARDS != 0 )
+ {
+ if( pxDirEntry->pcWildCard[ 0 ] )
+ {
+ b = FF_wildcompare( pxDirEntry->pcWildCard, pxDirEntry->pcFileName );
+ if( pxDirEntry->xInvertWildCard != pdFALSE )
+ {
+ b = !b;
+ }
+ if( b != 0 )
+ {
+ pxDirEntry->usCurrentItem += 1;
+ break;
+ }
+ }
+ else
+ {
+ pxDirEntry->usCurrentItem += 1;
+ break;
+ }
+ }
+ #else /* ffconfigFINDAPI_ALLOW_WILDCARDS */
+ {
+ pxDirEntry->usCurrentItem += 1;
+ break;
+ }
+ #endif
+ }
+ } /* for ( ; pxDirEntry->usCurrentItem < FF_MAX_ENTRIES_PER_DIRECTORY; pxDirEntry->usCurrentItem++ ) */
+
+ if( pxDirEntry->usCurrentItem == FF_MAX_ENTRIES_PER_DIRECTORY )
+ {
+ xError = ( FF_Error_t ) ( FF_ERR_DIR_END_OF_DIR | FF_FINDNEXT );
+ }
+
+ {
+ FF_Error_t xTempError;
+ xTempError = FF_CleanupEntryFetch( pxIOManager, &pxDirEntry->xFetchContext );
+
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ xError = xTempError;
+ }
+ }
+ }
+
+ return xError;
+} /* FF_FindNext() */
+/*-----------------------------------------------------------*/
+
+
+/*
+ Returns >= 0 for a free dirent entry.
+ Returns < 0 with and xError code if anything goes wrong.
+*/
+static int32_t FF_FindFreeDirent( FF_IOManager_t *pxIOManager, FF_FindParams_t *pxFindParams, uint16_t usSequential )
+{
+const uint8_t *pucEntryBuffer = NULL;
+uint16_t freeCount = 0;
+UBaseType_t uxEntry = 0;
+BaseType_t xEntryFound = pdFALSE;
+FF_Error_t xError;
+uint32_t DirLength;
+FF_FetchContext_t xFetchContext;
+uint32_t ulDirCluster = pxFindParams->ulDirCluster;
+
+ xError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );
+
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ uxEntry = pxFindParams->lFreeEntry >= 0 ? pxFindParams->lFreeEntry : 0;
+ for ( ; uxEntry < FF_MAX_ENTRIES_PER_DIRECTORY; uxEntry++ )
+ {
+ if( ( pucEntryBuffer == NULL ) ||
+ ( pucEntryBuffer >= xFetchContext.pxBuffer->pucBuffer + ( FF_SIZEOF_SECTOR - FF_SIZEOF_DIRECTORY_ENTRY ) ) )
+ {
+ xError = FF_FetchEntryWithContext( pxIOManager, uxEntry, &xFetchContext, NULL );
+ if( FF_GETERROR( xError ) == FF_ERR_DIR_END_OF_DIR )
+ {
+
+ xError = FF_ExtendDirectory( pxIOManager, ulDirCluster );
+ /* The value of xEntryFound will be ignored in case there was an error. */
+ xEntryFound = pdTRUE;
+ break;
+ }
+ else if( FF_isERR( xError ) )
+ {
+ break;
+ }
+ if( pucEntryBuffer == NULL )
+ {
+ pucEntryBuffer = xFetchContext.pxBuffer->pucBuffer +
+ ( FF_SIZEOF_DIRECTORY_ENTRY * ( uxEntry % ( FF_SIZEOF_SECTOR / FF_SIZEOF_DIRECTORY_ENTRY ) ) );
+ }
+ else
+ {
+ pucEntryBuffer = xFetchContext.pxBuffer->pucBuffer;
+ }
+ }
+ else
+ {
+ /* Advance 32 bytes to point to the next directory entry. */
+ pucEntryBuffer += FF_SIZEOF_DIRECTORY_ENTRY;
+ }
+ if( FF_isEndOfDir( pucEntryBuffer ) ) /* If its the end of the Dir, then FreeDirents from here. */
+ {
+ /* Check if the directory has enough space */
+ DirLength = xFetchContext.ulChainLength;
+ if( ( uxEntry + usSequential ) >
+ ( ( DirLength * ( ( UBaseType_t )pxIOManager->xPartition.ulSectorsPerCluster * pxIOManager->xPartition.usBlkSize ) ) / FF_SIZEOF_DIRECTORY_ENTRY ) )
+ {
+ xError = FF_ExtendDirectory( pxIOManager, ulDirCluster );
+ }
+ xEntryFound = pdTRUE;
+ break;
+ }
+ if( FF_isDeleted( pucEntryBuffer ) )
+ {
+ if( ++freeCount == usSequential )
+ {
+ xError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
+ xEntryFound = pdTRUE;
+ uxEntry = ( uxEntry - ( usSequential - 1 ) );/* Return the beginning entry in the sequential sequence. */
+ break;
+ }
+ }
+ else
+ {
+ freeCount = 0;
+ }
+ } /* for ( uxEntry = 0; uxEntry < FF_MAX_ENTRIES_PER_DIRECTORY; uxEntry++ ) */
+
+ {
+ FF_Error_t xTempError;
+
+ xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ xError = xTempError;
+ }
+ }
+
+ }
+
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ if( xEntryFound != pdFALSE )
+ {
+ /* No error has occurred and a free directory entry has been found. */
+ xError = uxEntry;
+ }
+ else
+ {
+ xError = ( FF_Error_t ) ( FF_ERR_DIR_DIRECTORY_FULL | FF_FINDFREEDIRENT );
+ }
+ }
+
+ return xError;
+} /* FF_FindFreeDirent() */
+/*-----------------------------------------------------------*/
+
+/* _HT_ Now FF_PutEntry has a new optional parameter *pucContents */
+/* _HT_ so it can be used FF_MkDir( ) to save some code when adding . and .. entries */
+FF_Error_t FF_PutEntry( FF_IOManager_t *pxIOManager, uint16_t usEntry, uint32_t ulDirCluster, FF_DirEnt_t *pxDirEntry, uint8_t *pucContents )
+{
+FF_Error_t xError;
+/* Reserve 32 bytes to hold one directory entry. */
+uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];
+FF_FetchContext_t xFetchContext;
+
+ /* HT: use the standard access routine to get the same logic for root dirs. */
+ xError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ xError = FF_FetchEntryWithContext( pxIOManager, usEntry, &xFetchContext, pucEntryBuffer );
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ /* Cleanup probably not necessary here?
+ FF_PushEntryWithContext checks for R/W flag. */
+ xError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ if ( pucContents != NULL )
+ {
+ memcpy ( pucEntryBuffer, pucContents, sizeof( pucEntryBuffer ) );
+ }
+ FF_putChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB, ( uint32_t ) pxDirEntry->ucAttrib );
+ FF_putShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH, ( uint32_t ) ( pxDirEntry->ulObjectCluster >> 16 ) );
+ FF_putShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW, ( uint32_t ) ( pxDirEntry->ulObjectCluster ) );
+ FF_putLong( pucEntryBuffer, FF_FAT_DIRENT_FILESIZE, pxDirEntry->ulFileSize );
+ #if( ffconfigTIME_SUPPORT != 0 )
+ {
+ FF_GetSystemTime( &pxDirEntry->xAccessedTime ); /*/< Date of Last Access. */
+ FF_PlaceTime( pucEntryBuffer, FF_FAT_DIRENT_LASTACC_DATE, &pxDirEntry->xAccessedTime );
+ FF_PlaceDate( pucEntryBuffer, FF_FAT_DIRENT_LASTACC_DATE, &pxDirEntry->xAccessedTime ); /* Last accessed date. */
+ FF_PlaceTime( pucEntryBuffer, FF_FAT_DIRENT_CREATE_TIME, &pxDirEntry->xCreateTime );
+ FF_PlaceDate( pucEntryBuffer, FF_FAT_DIRENT_CREATE_DATE, &pxDirEntry->xCreateTime );
+ FF_PlaceTime( pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_TIME, &pxDirEntry->xModifiedTime );
+ FF_PlaceDate( pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_DATE, &pxDirEntry->xModifiedTime );
+ }
+ #endif /* ffconfigTIME_SUPPORT */
+ xError = FF_PushEntryWithContext( pxIOManager, usEntry, &xFetchContext, pucEntryBuffer );
+ }
+ }
+ }
+
+ FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
+
+ return xError;
+} /* FF_PutEntry() */
+/*-----------------------------------------------------------*/
+
+static BaseType_t FF_ValidShortChar( char cChar )
+{
+ return ( cChar >= 'A' && cChar <= 'Z' ) ||
+ ( cChar >= 'a' && cChar <= 'z' ) || /* lower-case can be stored using NT/XP attribute. */
+ ( cChar >= '0' && cChar <= '9' ) ||
+ strchr ( "$%-_@~`!(){}^#&", cChar ) != NULL;
+} /* FF_ValidShortChar() */
+/*-----------------------------------------------------------*/
+
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+void FF_CreateShortName( FF_FindParams_t *pxFindParams, const FF_T_WCHAR *pcLongName )
+#else
+void FF_CreateShortName( FF_FindParams_t *pxFindParams, const char *pcLongName )
+#endif
+{
+BaseType_t xIndex, xPosition, xLastDot;
+uint16_t NameLen;
+
+#if( ffconfigSHORTNAME_CASE != 0 )
+ uint8_t testAttrib = FF_FAT_CASE_ATTR_BASE;
+#endif
+
+ /* Examples:
+ * "readme.TXT" will get the attribute FF_FAT_CASE_ATTR_BASE
+ * "README.txt" will get the attribute FF_FAT_CASE_ATTR_EXT
+ * "Readme.txt" can not be store as a short name */
+
+ pxFindParams->ucCaseAttrib = 0; /* May get the value FF_FAT_CASE_ATTR_BASE or FF_FAT_CASE_ATTR_EXT */
+ pxFindParams->ucFirstTilde = 6; /* The numerical position of the ~ */
+ pxFindParams->ulFlags |= FIND_FLAG_SHORTNAME_SET | FIND_FLAG_FITS_SHORT | FIND_FLAG_SIZE_OK;
+
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ {
+ NameLen = ( uint16_t ) wcslen( pcLongName );
+ }
+ #else
+ {
+ NameLen = ( uint16_t ) strlen( pcLongName );
+ }
+ #endif
+
+ /* Does pcLongName fit a shortname? */
+
+ for( xIndex = 0, xPosition = 0, xLastDot = NameLen; xIndex < NameLen; xIndex++ )
+ {
+ if( pcLongName[ xIndex ] != '.' )
+ {
+ xPosition++;
+ }
+ else
+ {
+ xLastDot = xIndex;
+ }
+ }
+ /* For example:
+ "FILENAME.EXT": NameLen = 12, xLastDot = 8, xPosition = 11
+ ".cproject" : NameLen = 9, xLastDot = 0, xPosition = 8
+ */
+
+ if( ( NameLen > 12 ) || /* If name is longer than 12 characters (8.3). */
+ ( NameLen - xPosition > 1 ) || /* If it contains more than 1 dot. */
+ ( NameLen - xLastDot > 4 ) || /* If the file name extension is longer than 3 characters. */
+ ( xLastDot > 8 ) ) /* If the file name base is too long. */
+ {
+ pxFindParams->ulFlags &= ~FIND_FLAG_SIZE_OK;
+ }
+
+ for( xIndex = 0, xPosition = 0; xIndex < 11; xPosition++ )
+ {
+ char ch = pcLongName[ xPosition ];
+ if( !ch )
+ break;
+ if( ( xIndex == 0 ) && ( ch == '.' ) )
+ {
+ pxFindParams->ulFlags &= ~FIND_FLAG_FITS_SHORT;
+ continue;
+ }
+ if( xPosition == xLastDot )
+ {
+ /* Remember where we put the first space. */
+ if ( pxFindParams->ucFirstTilde > xIndex )
+ {
+ pxFindParams->ucFirstTilde = xIndex;
+ }
+ while ( xIndex < 8 )
+ {
+ pxFindParams->pcEntryBuffer[ xIndex++ ] = 0x20;
+ }
+ #if( ffconfigSHORTNAME_CASE != 0 )
+ {
+ testAttrib = FF_FAT_CASE_ATTR_EXT;
+ }
+ #endif
+ }
+ else
+ {
+ if( xIndex == 8 )
+ {
+ if( xPosition <= xLastDot )
+ {
+ xPosition = xLastDot;
+ ch = ( int8_t ) pcLongName[ xPosition ];
+ if( ch == '\0' )
+ {
+ break;
+ }
+ ch = ( int8_t ) pcLongName[ ++xPosition ];
+ #if( ffconfigSHORTNAME_CASE != 0 )
+ {
+ testAttrib = FF_FAT_CASE_ATTR_EXT;
+ }
+ #endif
+ }
+ }
+ if( !FF_ValidShortChar ( ch ) )
+ {
+ pxFindParams->ulFlags &= ~FIND_FLAG_FITS_SHORT;
+ continue;
+ }
+ #if( ffconfigSHORTNAME_CASE != 0 )
+ {
+ if( ( ch >= 'a' ) && ( ch <= 'z' ) )
+ {
+ ch -= 0x20;
+ if ( testAttrib )
+ {
+ pxFindParams->ucCaseAttrib |= testAttrib;
+ }
+ else
+ {
+ pxFindParams->ulFlags &= ~FIND_FLAG_FITS_SHORT; /* We had capital: does not fit. */
+ }
+ }
+ else if( ( ch >= 'A' ) && ( ch <= 'Z' ) )
+ {
+ if( ( pxFindParams->ucCaseAttrib & testAttrib ) != 0 )
+ {
+ pxFindParams->ulFlags &= ~FIND_FLAG_FITS_SHORT; /* We had lower-case: does not fit. */
+ }
+ testAttrib = 0;
+ }
+ }
+ #else
+ {
+ if( ( ch >= 'a' ) && ( ch <= 'z' ) )
+ {
+ ch -= 0x20;
+ }
+ }
+ #endif /* ffconfigSHORTNAME_CASE */
+ pxFindParams->pcEntryBuffer[ xIndex++ ] = ch;
+ }
+ }
+
+ if( ( xLastDot == 0 ) && ( xIndex < 6 ) )
+ {
+ /* This is a file name like ".info" or ".root" */
+ pxFindParams->ucFirstTilde = xIndex;
+ }
+
+ while ( xIndex < 11 )
+ {
+ pxFindParams->pcEntryBuffer[ xIndex++ ] = 0x20;
+ }
+
+ if( ( xLastDot < pxFindParams->ucFirstTilde ) && ( xLastDot > 0 ) )
+ {
+ pxFindParams->ucFirstTilde = xLastDot;
+ }
+
+ if( NameLen < pxFindParams->ucFirstTilde ) /* Names like "Abc" will become "~Abc". */
+ {
+ pxFindParams->ucFirstTilde = ( uint8_t ) NameLen;
+ }
+} /* FF_CreateShortName() */
+/*-----------------------------------------------------------*/
+
+int32_t FF_FindShortName( FF_IOManager_t *pxIOManager, FF_FindParams_t *pxFindParams )
+{
+char pcMyShortName[ 13 ];
+FF_DirEnt_t xMyDirectory;
+FF_Error_t xResult = 0;
+BaseType_t xIndex, x, y;
+uint16_t NameLen;
+char pcNumberBuf[ 12 ];
+uint32_t ulCluster;
+
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ FF_T_WCHAR pcFileName[ 13 ];
+#else
+ char *pcFileName = pcMyShortName;
+#endif /* ffconfigUNICODE_UTF16_SUPPORT */
+
+#if( ipconfigQUICK_SHORT_FILENAME_CREATION != 0 )
+ uint16_t usShortHash;
+ uint32_t ulRand = 0ul;
+#endif
+
+ memcpy( pcMyShortName, pxFindParams->pcEntryBuffer, 11 );
+ FF_ProcessShortName( pcMyShortName );
+ if( ( pxFindParams->ulFlags & FIND_FLAG_FITS_SHORT_OK ) == FIND_FLAG_FITS_SHORT_OK )
+ {
+ /* This entry will not get a LFN entry because it fits
+ * perfectly into a host name */
+ if( ( pxFindParams->ulFlags & FIND_FLAG_SHORTNAME_CHECKED ) != 0 )
+ {
+ if( ( pxFindParams->ulFlags & FIND_FLAG_SHORTNAME_FOUND ) != 0 )
+ {
+ xResult = ( FF_Error_t ) ( FF_ERR_DIR_OBJECT_EXISTS | FF_CREATESHORTNAME );
+ }
+ else
+ {
+ xResult = pxFindParams->ucCaseAttrib | 0x01;
+ }
+ }
+ else
+ {
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ {
+ memcpy( pcFileName, pcMyShortName, sizeof( pcMyShortName ) );
+ FF_ShortNameExpand( pcFileName );
+ }
+ #endif
+ ulCluster = FF_FindEntryInDir( pxIOManager, pxFindParams, pcFileName, 0x00, &xMyDirectory, &xResult );
+
+ /* END_OF_DIR is not a fatal error, it only means that the entry was not found. */
+ if( ( FF_isERR( xResult ) == pdFALSE ) || ( FF_GETERROR( xResult ) == FF_ERR_DIR_END_OF_DIR ) )
+ {
+ if( ulCluster == 0UL )
+ {
+ xResult = pxFindParams->ucCaseAttrib | 0x01;
+ }
+ else
+ {
+ xResult = ( FF_Error_t ) ( FF_ERR_DIR_OBJECT_EXISTS | FF_CREATESHORTNAME );
+ }
+ }
+ else
+ {
+ /* There was an error, which will be returned. */
+ }
+ }
+ }
+ else
+ {
+ for( xIndex = ( ( pxFindParams->ulFlags & FIND_FLAG_SIZE_OK ) ? 0 : 1 ); ; xIndex++ )
+ {
+ if( xIndex != 0 )
+ {
+ #if( ipconfigQUICK_SHORT_FILENAME_CREATION != 0 )
+ {
+ /* In the first round, check if the original name can be used
+ Makefile will be stored as "makefile" and not as "makefi~1". */
+
+ /* This method saves a lot of time when creating directories with
+ many similar file names: when the short name version of a LFN already
+ exists, try at most 3 entries with a tilde:
+ README~1.TXT
+ README~2.TXT
+ README~3.TXT
+ After that create entries with pseudo-random 4-digit hex digits:
+ REA~E7BB.TXT
+ REA~BA32.TXT
+ REA~D394.TXT
+ */
+ if( xIndex <= 4 )
+ {
+ snprintf( pcNumberBuf, sizeof( pcNumberBuf ), "%d", ( int ) xIndex );
+ }
+ else
+ {
+ if( ulRand == 0ul )
+ {
+ ulRand = pxIOManager->xPartition.ulLastFreeCluster;
+ usShortHash = FF_GetCRC16( ( uint8_t *)&ulRand, sizeof( ulRand ) );
+ }
+ else
+ {
+ usShortHash = FF_GetCRC16( ( uint8_t *)&usShortHash, sizeof( usShortHash ) );
+ }
+ snprintf( pcNumberBuf, sizeof( pcNumberBuf ), "%04X", ( int ) usShortHash );
+ }
+ }
+ #else
+ {
+ snprintf( pcNumberBuf, sizeof( pcNumberBuf ), "%d", ( int ) xIndex );
+ }
+ #endif
+
+ NameLen = ( uint16_t ) strlen( pcNumberBuf );
+ x = 7 - NameLen;
+ if ( x > pxFindParams->ucFirstTilde )
+ {
+ x = pxFindParams->ucFirstTilde;
+ }
+ pxFindParams->pcEntryBuffer[ x++ ] = '~';
+ for( y = 0; y < NameLen; y++ )
+ {
+ pxFindParams->pcEntryBuffer[ x + y ] = pcNumberBuf[ y ];
+ }
+ }
+ memcpy( pcMyShortName, pxFindParams->pcEntryBuffer, 11 );
+ FF_ProcessShortName( pcMyShortName );
+ if( FF_ShortNameExists( pxIOManager, pxFindParams->ulDirCluster, pcMyShortName, &xResult ) == pdFALSE )
+ {
+ break;
+ }
+ if( xIndex >= FF_MAX_ENTRIES_PER_DIRECTORY )
+ {
+ xResult = ( FF_Error_t ) ( FF_ERR_DIR_DIRECTORY_FULL | FF_CREATESHORTNAME );
+ break;
+ }
+ }
+ /* Add a tail and special number until we're happy :D. */
+ }
+
+ return xResult;
+} /* FF_FindShortName () */
+/*-----------------------------------------------------------*/
+
+
+#if( ffconfigLFN_SUPPORT != 0 )
+ static int8_t FF_CreateLFNEntry( uint8_t *pucEntryBuffer, uint8_t *pcName, UBaseType_t uxNameLength, UBaseType_t uxLFN, uint8_t ucCheckSum )
+ {
+ /*
+ * HT for JW:
+ * Changed *pcName from 16- to of 8-bits
+ * The caller of this function doesn't need an expensive
+ * uint16_t usUtf16Name[ffconfigMAX_FILENAME + 1];
+ * in case UNICODE isn't used
+ * Also did quite a bit of optimisation here
+ * and tested well
+ */
+ UBaseType_t uxIndex, x;
+
+ memset( pucEntryBuffer, 0, FF_SIZEOF_DIRECTORY_ENTRY );
+
+ FF_putChar( pucEntryBuffer, FF_FAT_LFN_ORD, ( uint8_t )( ( uxLFN & ~0x40 ) ) );
+ FF_putChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB, ( uint8_t ) FF_FAT_ATTR_LFN );
+ FF_putChar( pucEntryBuffer, FF_FAT_LFN_CHECKSUM, ( uint8_t ) ucCheckSum );
+
+ /* Name_1. */
+ uxIndex = 0;
+ for( x = FF_FAT_LFN_NAME_1; uxIndex < 5u; uxIndex++, x += 2 )
+ {
+ if( uxIndex < uxNameLength )
+ {
+ pucEntryBuffer[ x ] = *( pcName++ );
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 ) || ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
+ {
+ pucEntryBuffer[ x + 1 ] = *( pcName++ );
+ }
+ #endif
+ }
+ else if ( uxIndex > uxNameLength )
+ {
+ pucEntryBuffer[ x] = 0xFF;
+ pucEntryBuffer[ x + 1 ] = 0xFF;
+ }
+ }
+
+ /* Name_2. */
+ for( x = FF_FAT_LFN_NAME_2; uxIndex < 11u; uxIndex++, x += 2 )
+ {
+ if( uxIndex < uxNameLength )
+ {
+ pucEntryBuffer[ x ] = *( pcName++ );
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 ) || ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
+ {
+ pucEntryBuffer[ x + 1 ] = *( pcName++ );
+ }
+ #endif
+ }
+ else if( uxIndex > uxNameLength )
+ {
+ pucEntryBuffer[ x ] = 0xFF;
+ pucEntryBuffer[ x + 1 ] = 0xFF;
+ }
+ }
+
+ /* Name_3. */
+ for( x = FF_FAT_LFN_NAME_3; uxIndex < 13u; uxIndex++, x += 2 )
+ {
+ if( uxIndex < uxNameLength )
+ {
+ pucEntryBuffer[ x ] = *( pcName++ );
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 ) || ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
+ {
+ pucEntryBuffer[ x + 1 ] = *( pcName++ );
+ }
+ #endif
+ }
+ else if( uxIndex > uxNameLength )
+ {
+ pucEntryBuffer[ x ] = 0xFF;
+ pucEntryBuffer[ x + 1 ] = 0xFF;
+ }
+ }
+
+ return FF_ERR_NONE;
+ } /* FF_CreateLFNEntry() */
+#endif /* ffconfigLFN_SUPPORT */
+/*-----------------------------------------------------------*/
+
+#if( ffconfigLFN_SUPPORT != 0 )
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ static FF_Error_t FF_CreateLFNs( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster, FF_T_WCHAR *pcName, uint8_t ucCheckSum, uint16_t usEntry )
+ #else
+ static FF_Error_t FF_CreateLFNs( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster, char *pcName, uint8_t ucCheckSum, uint16_t usEntry )
+ #endif
+ {
+ FF_Error_t xError = FF_ERR_NONE;
+ BaseType_t xNumLFNs;
+ BaseType_t xEndPos;
+ BaseType_t xIndex, y;
+ FF_FetchContext_t xFetchContext;
+ uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];
+
+ #if ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
+ int32_t slRetVal;
+ #endif
+
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 ) || ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
+ uint16_t usUtf16Name[ ffconfigMAX_FILENAME + 1 ];
+ #endif
+
+ #if( ffconfigUNICODE_UTF16_SUPPORT == 0 )
+ char *NamePtr;
+ #else
+ int16_t *NamePtr;
+ #endif
+
+
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ {
+ #if WCHAR_MAX <= 0xFFFF
+ {
+ y = wcslen( pcName );
+ if( y > ffconfigMAX_FILENAME )
+ {
+ xError = ( FF_Error_t ) ( FF_ERR_DIR_NAME_TOO_LONG | FF_CREATELFNS );
+ }
+ else
+ {
+ wcsncpy( usUtf16Name, pcName, ffconfigMAX_FILENAME );
+ }
+ }
+ #else
+ {
+ xIndex = 0;
+ y = 0;
+ while( pcName[ xIndex ] )
+ {
+ FF_Utf32ctoUtf16c( &usUtf16Name[ y ], ( uint32_t ) pcName[xIndex], ffconfigMAX_FILENAME - xIndex );
+ y += FF_GetUtf16SequenceLen( usUtf16Name[ y ] );
+ xIndex++;
+ if( y > ffconfigMAX_FILENAME )
+ {
+ xError = ( FF_Error_t ) ( FF_ERR_DIR_NAME_TOO_LONG | FF_CREATELFNS );
+ break;
+ }
+ }
+ }
+ #endif
+ }
+ #endif /* ffconfigUNICODE_UTF16_SUPPORT */
+
+ /* Convert the name into UTF-16 format. */
+ #if ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
+ {
+ /* Simply convert the UTF8 to UTF16 and be done with it. */
+ xIndex = 0;
+ y = 0;
+ while( pcName[ xIndex ] != 0 )
+ {
+ slRetVal = FF_Utf8ctoUtf16c( &( usUtf16Name[ y ] ), ( uint8_t * )&( pcName[ xIndex ] ), ffconfigMAX_FILENAME - xIndex );
+ if( slRetVal > 0 )
+ {
+ xIndex += slRetVal;
+ }
+ else
+ {
+ break; /* No more space in the UTF-16 buffer, simply truncate for safety. */
+ }
+ y += FF_GetUtf16SequenceLen( usUtf16Name[ y ] );
+ if( y > ffconfigMAX_FILENAME )
+ {
+ xError = ( FF_Error_t ) ( FF_ERR_DIR_NAME_TOO_LONG | FF_CREATELFNS );
+ break;
+ }
+ }
+ }
+ #elif ( ffconfigUNICODE_UTF16_SUPPORT == 0 )
+ {
+ /* Just check the length. */
+ y = strlen( pcName );
+ if( y > ffconfigMAX_FILENAME )
+ {
+ xError = ( FF_Error_t ) ( FF_ERR_DIR_NAME_TOO_LONG | FF_CREATELFNS );
+ }
+ }
+ #endif
+
+ /* Whole name is now in a valid UTF-16 format. Lets go make thos LFN's.
+ At this point, it should a be the length of the name. */
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ xNumLFNs = y / 13; /* Number of LFNs is the total number of UTF-16 units, divided by 13 ( 13 units per LFN ). */
+ xEndPos = y % 13; /* The ending position in an LFN, of the last LFN UTF-16 character. */
+
+ if( xEndPos )
+ {
+ xNumLFNs++;
+ }
+ else
+ {
+ xEndPos = 13;
+ }
+
+ xError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ {
+ NamePtr = ( int16_t * ) ( usUtf16Name + 13 * ( xNumLFNs - 1 ) );
+ }
+ #elif ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
+ {
+ NamePtr = ( int8_t * ) ( usUtf16Name + 13 * ( xNumLFNs - 1 ) );
+ }
+ #else
+ {
+ NamePtr = pcName + 13 * ( xNumLFNs - 1 );
+ }
+ #endif
+ /* After this point, xIndex is no longer the length of the Filename in UTF-16 units. */
+ for( xIndex = xNumLFNs; xIndex > 0; xIndex-- )
+ {
+ if( xIndex == xNumLFNs )
+ {
+ FF_CreateLFNEntry( pucEntryBuffer, ( uint8_t * ) NamePtr, ( UBaseType_t ) xEndPos, ( UBaseType_t ) xIndex, ucCheckSum );
+ pucEntryBuffer[0] |= 0x40;
+ }
+ else
+ {
+ FF_CreateLFNEntry( pucEntryBuffer, ( uint8_t * ) NamePtr, ( UBaseType_t ) 13u, ( UBaseType_t ) xIndex, ucCheckSum );
+ }
+ NamePtr -= 13;
+
+ xError = FF_PushEntryWithContext( pxIOManager, ( uint32_t ) ( usEntry + ( xNumLFNs - xIndex ) ), &xFetchContext, pucEntryBuffer );
+ if( FF_isERR( xError ) )
+ {
+ break;
+ }
+ }
+
+ {
+ FF_Error_t xTempError;
+
+ /* Release any buffers that were used. */
+ xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
+ if( FF_isERR( xTempError ) == pdFALSE )
+ {
+ xError = xTempError;
+ }
+ }
+ }
+ }
+
+ return xError;
+ } /* FF_CreateLFNs() */
+#endif /* ffconfigLFN_SUPPORT */
+/*-----------------------------------------------------------*/
+
+FF_Error_t FF_ExtendDirectory( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster )
+{
+uint32_t xCurrentCluster;
+uint32_t xNextCluster = 0UL;
+FF_Error_t xError = FF_ERR_NONE;
+FF_FATBuffers_t xFATBuffers;
+
+ if( ( ulDirCluster == pxIOManager->xPartition.ulRootDirCluster ) &&
+ ( pxIOManager->xPartition.ucType != FF_T_FAT32 ) )
+ {
+ /* root directories on FAT12 and FAT16 can not be extended. */
+ xError = ( FF_Error_t ) ( FF_ERR_DIR_CANT_EXTEND_ROOT_DIR | FF_EXTENDDIRECTORY );
+ }
+ else if( pxIOManager->xPartition.ulFreeClusterCount == 0UL )
+ {
+ /* The number of free clusters was not yet calculated or equal to zero. */
+ pxIOManager->xPartition.ulFreeClusterCount = FF_CountFreeClusters( pxIOManager, &xError );
+ }
+
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ if( pxIOManager->xPartition.ulFreeClusterCount == 0UL )
+ {
+ xError = ( FF_Error_t ) ( FF_ERR_FAT_NO_FREE_CLUSTERS | FF_EXTENDDIRECTORY );
+ }
+ else
+ {
+ FF_LockFAT( pxIOManager );
+ {
+ xCurrentCluster = FF_FindEndOfChain( pxIOManager, ulDirCluster, &xError );
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ xNextCluster = FF_FindFreeCluster( pxIOManager, &xError, pdTRUE );
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ FF_InitFATBuffers ( &xFATBuffers, FF_MODE_WRITE );
+ /* xNextCluster already has been set to 0xFFFFFFFF,
+ now let xCurrentCluster point to xNextCluster. */
+
+ xError = FF_putFATEntry( pxIOManager, xCurrentCluster, xNextCluster, &xFATBuffers );
+ {
+ FF_Error_t xTempError;
+
+ xTempError = FF_ReleaseFATBuffers( pxIOManager, &xFATBuffers );
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ xError = xTempError;
+ }
+
+ xTempError = FF_DecreaseFreeClusters( pxIOManager, 1 );
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ xError = xTempError;
+ }
+ }
+ }
+ }
+ }
+ FF_UnlockFAT( pxIOManager );
+
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ /* The entire cluster will be filled with zero's,
+ because it will contain directory data. */
+ xError = FF_ClearCluster( pxIOManager, xNextCluster );
+ }
+ }
+ }
+
+ return xError;
+} /* FF_ExtendDirectory() */
+/*-----------------------------------------------------------*/
+
+static const uint8_t forbiddenChrs[] =
+{
+/* Windows says: don't use these characters: '\/:*?"<>|'
+ " * / : < > ? '\' ? | */
+ 0x22, 0x2A, 0x2F, 0x3A, 0x3C, 0x3E, 0x3F, 0x5C, 0x7F, 0x7C
+};
+
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+static void FF_MakeNameCompliant( FF_T_WCHAR *pcName )
+#else
+static void FF_MakeNameCompliant( char *pcName )
+#endif
+{
+ BaseType_t index;
+ if( ( uint8_t ) pcName[ 0 ] == FF_FAT_DELETED ) /* Support Japanese KANJI symbol0xE5. */
+ {
+ pcName[ 0 ] = 0x05;
+ }
+ for( ; *pcName; pcName++ )
+ {
+ for( index = 0; index < ( BaseType_t ) sizeof( forbiddenChrs ); index++ )
+ {
+ if( *pcName == forbiddenChrs[index] )
+ {
+ *pcName = '_';
+ break;
+ }
+ }
+ }
+} /* FF_MakeNameCompliant() */
+/*-----------------------------------------------------------*/
+
+FF_Error_t FF_CreateDirent( FF_IOManager_t *pxIOManager, FF_FindParams_t *pxFindParams, FF_DirEnt_t *pxDirEntry )
+{
+uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];
+
+BaseType_t xLFNCount;
+int32_t lFreeEntry = 0L;
+FF_Error_t xReturn = FF_ERR_NONE;
+BaseType_t xEntryCount;
+FF_FetchContext_t xFetchContext;
+uint32_t ulDirCluster = pxFindParams->ulDirCluster;
+int32_t lFitShort;
+
+#if( ffconfigHASH_CACHE != 0 )
+ char pcShortName[ 13 ];
+#endif
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ uint16_t NameLen = ( uint16_t ) wcslen( pxDirEntry->pcFileName );
+#else
+ uint16_t NameLen = ( uint16_t ) strlen( pxDirEntry->pcFileName );
+#endif
+
+#if( ffconfigLFN_SUPPORT != 0 )
+ uint8_t ucCheckSum;
+#endif
+
+ /* Round-up the number of LFN's needed: */
+ xLFNCount = ( BaseType_t ) ( ( NameLen + 12 ) / 13 );
+
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ {
+ FF_MakeNameCompliant( pxDirEntry->pcFileName ); /* Ensure we don't break the Dir tables. */
+ }
+ #else
+ {
+ FF_MakeNameCompliant( pxDirEntry->pcFileName ); /* Ensure we don't break the Dir tables. */
+ }
+ #endif
+ memset( pucEntryBuffer, 0, sizeof( pucEntryBuffer ) );
+
+ #if( ffconfigLFN_SUPPORT != 0 )
+ {
+ /* Create and push the LFN's. */
+ /* Find enough places for the LFNs and the ShortName. */
+ xEntryCount = xLFNCount + 1;
+ }
+ #else
+ {
+ xEntryCount = 1;
+ }
+ #endif
+
+ /* Create the ShortName. */
+ FF_LockDirectory( pxIOManager );
+ do
+ {
+ /* Open a do {} while( pdFALSE ) loop to allow the use of break statements. */
+ /* As FF_FindShortName( ) can fail, it should be called before finding a free directory entry. */
+ if( ( pxFindParams->ulFlags & FIND_FLAG_SHORTNAME_SET ) == 0 )
+ {
+ FF_CreateShortName( pxFindParams, pxDirEntry->pcFileName );
+ }
+ lFitShort = FF_FindShortName ( pxIOManager, pxFindParams );
+
+ memcpy( pucEntryBuffer, pxFindParams->pcEntryBuffer, sizeof( pucEntryBuffer ) );
+
+ if( FF_isERR( lFitShort ) )
+ {
+ xReturn = lFitShort;
+ break;
+ }
+ if( lFitShort != 0 )
+ {
+ /* There is no need to create a LFN entry because the file name
+ fits into a normal 32-byte entry.. */
+ xLFNCount = 0;
+ xEntryCount = 1;
+ }
+ lFreeEntry = FF_FindFreeDirent( pxIOManager, pxFindParams, ( uint16_t ) xEntryCount );
+
+ if( FF_isERR( lFreeEntry ) )
+ {
+ xReturn = lFreeEntry;
+ break;
+ }
+ #if( ffconfigLFN_SUPPORT != 0 )
+ {
+ if( xLFNCount > 0 )
+ {
+ ucCheckSum = FF_CreateChkSum( pucEntryBuffer );
+ xReturn = FF_CreateLFNs( pxIOManager, ulDirCluster, pxDirEntry->pcFileName, ucCheckSum, ( uint16_t ) lFreeEntry );
+ }
+ }
+ #else
+ {
+ xLFNCount = 0;
+ }
+ #endif /* ffconfigLFN_SUPPORT */
+ if( FF_isERR( xReturn ) == pdFALSE )
+ {
+ #if( ffconfigTIME_SUPPORT != 0 )
+ {
+ FF_GetSystemTime( &pxDirEntry->xCreateTime ); /* Date and Time Created. */
+ pxDirEntry->xModifiedTime = pxDirEntry->xCreateTime; /* Date and Time Modified. */
+ pxDirEntry->xAccessedTime = pxDirEntry->xCreateTime; /* Date of Last Access. */
+ FF_PlaceTime( pucEntryBuffer, FF_FAT_DIRENT_CREATE_TIME, &pxDirEntry->xCreateTime );
+ FF_PlaceDate( pucEntryBuffer, FF_FAT_DIRENT_CREATE_DATE, &pxDirEntry->xCreateTime );
+ FF_PlaceTime( pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_TIME, &pxDirEntry->xModifiedTime );
+ FF_PlaceDate( pucEntryBuffer, FF_FAT_DIRENT_LASTMOD_DATE, &pxDirEntry->xModifiedTime );
+ }
+ #endif /* ffconfigTIME_SUPPORT */
+
+ FF_putChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB, pxDirEntry->ucAttrib );
+ #if( ffconfigSHORTNAME_CASE != 0 )
+ FF_putChar( pucEntryBuffer, FF_FAT_CASE_OFFS, ( uint32_t ) lFitShort & ( FF_FAT_CASE_ATTR_BASE | FF_FAT_CASE_ATTR_EXT ) );
+ #endif
+ FF_putShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_HIGH, ( uint16_t )( pxDirEntry->ulObjectCluster >> 16 ) );
+ FF_putShort( pucEntryBuffer, FF_FAT_DIRENT_CLUS_LOW, ( uint16_t )( pxDirEntry->ulObjectCluster ) );
+ FF_putLong( pucEntryBuffer, FF_FAT_DIRENT_FILESIZE, pxDirEntry->ulFileSize );
+
+ xReturn = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );
+ if( FF_isERR( xReturn ) )
+ {
+ break;
+ }
+ xReturn = FF_PushEntryWithContext( pxIOManager, ( uint16_t ) ( lFreeEntry + xLFNCount ), &xFetchContext, pucEntryBuffer );
+
+ {
+ FF_Error_t xTempError;
+
+ xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
+ if( FF_isERR( xReturn ) == pdFALSE )
+ {
+ xReturn = xTempError;
+ }
+ }
+ if( FF_isERR( xReturn ) )
+ {
+ break;
+ }
+
+ #if( ffconfigHASH_CACHE != 0 )
+ {
+ if( FF_DirHashed( pxIOManager, ulDirCluster ) == pdFALSE )
+ {
+ /* Hash the directory. */
+ FF_HashDir( pxIOManager, ulDirCluster );
+ }
+ memcpy( pcShortName, pucEntryBuffer, 11 );
+ FF_ProcessShortName( pcShortName ); /* Format the shortname to 8.3. */
+ #if( ffconfigHASH_FUNCTION == CRC16 )
+ {
+ FF_AddDirentHash( pxIOManager, ulDirCluster, ( uint32_t )FF_GetCRC16( ( uint8_t * ) pcShortName, strlen( pcShortName ) ) );
+ }
+ #elif( ffconfigHASH_FUNCTION == CRC8 )
+ {
+ FF_AddDirentHash( pxIOManager, ulDirCluster, ( uint32_t )FF_GetCRC8( ( uint8_t * ) pcShortName, strlen( pcShortName ) ) );
+ }
+ #endif /* ffconfigHASH_FUNCTION */
+ }
+ #endif /* ffconfigHASH_CACHE*/
+ }
+ }
+ while( pdFALSE );
+
+ FF_UnlockDirectory( pxIOManager );
+
+ if( FF_isERR( xReturn ) == pdFALSE )
+ {
+ if( pxDirEntry != NULL )
+ {
+ pxDirEntry->usCurrentItem = ( uint16_t )( lFreeEntry + xLFNCount );
+ }
+ }
+
+ return xReturn;
+} /* FF_CreateDirent() */
+/*-----------------------------------------------------------*/
+
+
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+uint32_t FF_CreateFile( FF_IOManager_t *pxIOManager, FF_FindParams_t *pxFindParams, FF_T_WCHAR *pcFileName, FF_DirEnt_t *pxDirEntry, FF_Error_t *pxError )
+#else
+uint32_t FF_CreateFile( FF_IOManager_t *pxIOManager, FF_FindParams_t *pxFindParams, char *pcFileName, FF_DirEnt_t *pxDirEntry, FF_Error_t *pxError )
+#endif
+{
+FF_DirEnt_t xMyFile;
+FF_Error_t xTempError, xError = FF_ERR_NONE;
+uint32_t ulResult;
+
+ memset ( &xMyFile, '\0', sizeof( xMyFile ) );
+
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ {
+ wcsncpy( xMyFile.pcFileName, pcFileName, ffconfigMAX_FILENAME );
+ }
+ #else
+ {
+ strncpy( xMyFile.pcFileName, pcFileName, ffconfigMAX_FILENAME );
+ }
+ #endif
+
+ xMyFile.ulObjectCluster = FF_CreateClusterChain( pxIOManager, &xError );
+
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ xError = FF_CreateDirent( pxIOManager, pxFindParams, &xMyFile );
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ /* The new file now has a cluster chain and it has an entry
+ in its directory. Copy data to a pointer provided by caller: */
+ if( pxDirEntry != NULL )
+ {
+ memcpy( pxDirEntry, &xMyFile, sizeof( FF_DirEnt_t ) );
+ }
+ }
+ else
+ {
+ /* An error occurred in FF_CreateDirent().
+ Unlink the file's cluster chain: */
+ FF_LockFAT( pxIOManager );
+ {
+ FF_UnlinkClusterChain( pxIOManager, xMyFile.ulObjectCluster, 0 );
+ xMyFile.ulObjectCluster = 0ul;
+ }
+ FF_UnlockFAT( pxIOManager );
+ }
+ /* Now flush all buffers to disk. */
+ xTempError = FF_FlushCache( pxIOManager );
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ xError = xTempError;
+ }
+ }
+
+ *pxError = xError;
+
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ ulResult = xMyFile.ulObjectCluster;
+ }
+ else
+ {
+ ulResult = 0;
+ }
+
+ return ulResult;
+} /* FF_CreateFile() */
+/*-----------------------------------------------------------*/
+
+
+/**
+ * @brief Creates a Directory of the specified path.
+ *
+ * @param pxIOManager Pointer to the FF_IOManager_t object.
+ * @param pcPath Path of the directory to create.
+ *
+ * @Return FF_ERR_NULL_POINTER if pxIOManager was NULL.
+ * @Return FF_ERR_DIR_OBJECT_EXISTS if the object specified by path already exists.
+ * @Return FF_ERR_DIR_INVALID_PATH
+ * @Return FF_ERR_NONE on success.
+ **/
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+FF_Error_t FF_MkDir( FF_IOManager_t *pxIOManager, const FF_T_WCHAR *pcPath )
+#else
+FF_Error_t FF_MkDir( FF_IOManager_t *pxIOManager, const char *pcPath )
+#endif
+{
+FF_DirEnt_t xMyDirectory;
+
+#if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ const FF_T_WCHAR *pcDirName;
+#else
+ const char *pcDirName;
+#endif
+uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];
+uint32_t ulObjectCluster;
+BaseType_t xIndex;
+FF_Error_t xError = FF_ERR_NONE;
+
+FF_FindParams_t xFindParams;
+
+ memset ( &xFindParams, '\0', sizeof( xFindParams ) );
+ /* Inform the functions that the entry will be created if not found */
+ xFindParams.ulFlags |= FIND_FLAG_CREATE_FLAG;
+
+ /* Open a do {} while ( pdFALSE ) loop */
+ do
+ {
+ if( pxIOManager == NULL )
+ {
+ xError = ( FF_Error_t ) ( FF_ERR_NULL_POINTER | FF_MKDIR );
+ break;
+ }
+#if( ffconfigREMOVABLE_MEDIA != 0 )
+ if( ( pxIOManager->ucFlags & FF_IOMAN_DEVICE_IS_EXTRACTED ) != 0 )
+ {
+ xError = ( FF_Error_t ) ( FF_ERR_IOMAN_DRIVER_NOMEDIUM | FF_MKDIR );
+ break;
+ }
+#endif /* ffconfigREMOVABLE_MEDIA */
+
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ {
+ xIndex = ( BaseType_t ) wcslen( pcPath );
+ }
+ #else
+ {
+ xIndex = ( BaseType_t ) strlen( pcPath );
+ }
+ #endif
+
+ /* Find the last slash in the path. */
+ while( xIndex != 0 )
+ {
+ if( ( pcPath[ xIndex ] == '\\' ) || ( pcPath[ xIndex ] == '/' ) )
+ {
+ break;
+ }
+ xIndex--;
+ }
+
+ pcDirName = pcPath + xIndex + 1;
+
+ if( xIndex == 0 )
+ {
+ xIndex = 1;
+ }
+
+ if( pcDirName[ 0 ] == '\0' )
+ {
+ xError = ( FF_ERR_DIR_OBJECT_EXISTS | FF_MKDIR );
+ break;
+ }
+
+ xFindParams.ulDirCluster = FF_FindDir( pxIOManager, pcPath, ( uint16_t ) xIndex, &xError );
+
+ if( FF_isERR( xError ) )
+ {
+ break;
+ }
+
+ if( xFindParams.ulDirCluster == 0UL )
+ {
+ xError = ( FF_Error_t ) ( FF_ERR_DIR_INVALID_PATH | FF_MKDIR );
+ break;
+ }
+ memset( &xMyDirectory, '\0', sizeof( xMyDirectory ) );
+
+ /* Will set flags FIND_FLAG_FITS_SHORT and FIND_FLAG_SIZE_OK */
+ FF_CreateShortName( &xFindParams, pcDirName );
+
+ if( FF_FindEntryInDir( pxIOManager, &xFindParams, pcDirName, 0x00, &xMyDirectory, &xError ) )
+ {
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ xError = ( FF_Error_t ) ( FF_ERR_DIR_OBJECT_EXISTS | FF_MKDIR );
+ }
+ break;
+ }
+
+ if( ( FF_isERR( xError ) ) && ( FF_GETERROR( xError ) != FF_ERR_DIR_END_OF_DIR ) )
+ {
+ break;
+ }
+
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ {
+ wcsncpy( xMyDirectory.pcFileName, pcDirName, ffconfigMAX_FILENAME );
+ }
+ #else
+ {
+ strncpy( xMyDirectory.pcFileName, pcDirName, ffconfigMAX_FILENAME );
+ }
+ #endif
+
+ xMyDirectory.ulFileSize = 0;
+ xMyDirectory.ucAttrib = FF_FAT_ATTR_DIR;
+ xMyDirectory.ulObjectCluster = FF_CreateClusterChain( pxIOManager, &xError );
+
+ /* Give all entries a proper time stamp, looks nicer than 1 Jan 1970 */
+ #if( ffconfigTIME_SUPPORT != 0 )
+ {
+ FF_GetSystemTime( &xMyDirectory.xCreateTime );
+ FF_GetSystemTime( &xMyDirectory.xModifiedTime );
+ }
+ #endif
+
+ if( FF_isERR( xError ) )
+ {
+ break;
+ }
+ if( xMyDirectory.ulObjectCluster == 0UL )
+ {
+ /* Couldn't allocate any space for the dir! */
+ xError = ( FF_Error_t ) ( FF_ERR_DIR_EXTEND_FAILED | FF_MKDIR );
+ break;
+ }
+
+ xError = FF_ClearCluster( pxIOManager, xMyDirectory.ulObjectCluster );
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ xError = FF_CreateDirent( pxIOManager, &xFindParams, &xMyDirectory );
+ }
+
+ if( FF_isERR( xError ) )
+ {
+ FF_LockFAT( pxIOManager );
+ {
+ FF_UnlinkClusterChain( pxIOManager, xMyDirectory.ulObjectCluster, 0 );
+ }
+ FF_UnlockFAT( pxIOManager );
+ FF_FlushCache( pxIOManager ); /* Don't override error. */
+ break;
+ }
+
+ /* Write 8.3 entry "." */
+ pucEntryBuffer[ 0 ] = '.';
+ /* folowed by 10 spaces: */
+ memset( pucEntryBuffer + 1, ' ', 10 );
+ /* Clear the rest of the structure. */
+ memset( pucEntryBuffer + 11, 0, FF_SIZEOF_DIRECTORY_ENTRY - 11 );
+
+ ulObjectCluster = xMyDirectory.ulObjectCluster;
+ xError = FF_PutEntry( pxIOManager, ( uint16_t ) 0u, ulObjectCluster, &xMyDirectory, pucEntryBuffer );
+
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ pucEntryBuffer[ 1 ] = '.';
+
+ if( xFindParams.ulDirCluster == pxIOManager->xPartition.ulRootDirCluster )
+ {
+ xMyDirectory.ulObjectCluster = 0;
+ }
+ else
+ {
+ xMyDirectory.ulObjectCluster = xFindParams.ulDirCluster;
+ }
+ xError = FF_PutEntry( pxIOManager, 1u, ulObjectCluster, &xMyDirectory, pucEntryBuffer );
+
+ xMyDirectory.ulObjectCluster = ulObjectCluster;
+ }
+
+ if( FF_isERR( xError ) )
+ {
+ FF_LockFAT( pxIOManager );
+ {
+ FF_UnlinkClusterChain( pxIOManager, xMyDirectory.ulObjectCluster, 0 );
+ }
+ FF_UnlockFAT( pxIOManager );
+ }
+ FF_FlushCache( pxIOManager );
+ }
+ while( pdFALSE );
+
+ return xError;
+} /* FF_MkDir() */
+/*-----------------------------------------------------------*/
+
+
+FF_Error_t FF_RmLFNs( FF_IOManager_t *pxIOManager, uint16_t usDirEntry, FF_FetchContext_t *pxContext )
+{
+FF_Error_t xError = FF_ERR_NONE;
+uint8_t pucEntryBuffer[ FF_SIZEOF_DIRECTORY_ENTRY ];
+
+ if( usDirEntry != 0 )
+ {
+ usDirEntry--;
+
+ do
+ {
+ xError = FF_FetchEntryWithContext( pxIOManager, usDirEntry, pxContext, pucEntryBuffer );
+ if( FF_isERR( xError ) )
+ {
+ break;
+ }
+
+ if( FF_getChar( pucEntryBuffer, ( uint16_t )( FF_FAT_DIRENT_ATTRIB ) ) == FF_FAT_ATTR_LFN )
+ {
+ FF_putChar( pucEntryBuffer, ( uint16_t ) 0, ( uint8_t ) FF_FAT_DELETED );
+ xError = FF_PushEntryWithContext( pxIOManager, usDirEntry, pxContext, pucEntryBuffer );
+ if( FF_isERR( xError ) )
+ {
+ break;
+ }
+ }
+
+ if( usDirEntry == 0 )
+ {
+ break;
+ }
+ usDirEntry--;
+ } while( FF_getChar( pucEntryBuffer, ( uint16_t )( FF_FAT_DIRENT_ATTRIB ) ) == FF_FAT_ATTR_LFN );
+ }
+
+ return xError;
+} /* FF_RmLFNs() */
+/*-----------------------------------------------------------*/
+
+#if( ffconfigHASH_CACHE != 0 )
+ FF_Error_t FF_HashDir( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster )
+ {
+ /* Find most suitable Hash Table to replace! */
+ BaseType_t xIndex;
+ FF_HashTable_t *pxHashCache = NULL;
+ FF_FetchContext_t xFetchContext;
+ const uint8_t *pucEntryBuffer = NULL;
+ uint8_t ucAttrib;
+ uint32_t ulHash;
+ FF_Error_t xError;
+
+ for( xIndex = 0; xIndex < ffconfigHASH_CACHE_DEPTH; xIndex++ )
+ {
+ if( pxIOManager->xHashCache[ xIndex ].ulNumHandles == 0 )
+ {
+ if( pxHashCache == NULL )
+ {
+ pxHashCache = &pxIOManager->xHashCache[ xIndex ];
+ }
+ else
+ {
+ if( ( pxIOManager->xHashCache[ xIndex ].ulMisses > pxHashCache->ulMisses ) )
+ {
+ pxHashCache = &pxIOManager->xHashCache[ xIndex ];
+ }
+ }
+ }
+ }
+
+ if( pxHashCache != NULL )
+ {
+ #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
+ FF_T_WCHAR pcMyShortName[ 13 ];
+ #else
+ char pcMyShortName[ 13 ];
+ #endif
+ /* Clear the hash table! */
+ memset( pxHashCache, '\0', sizeof( *pxHashCache ) );
+ pxHashCache->ulDirCluster = ulDirCluster;
+ pxHashCache->ulMisses = 0;
+
+ /* Hash the directory! */
+
+ xError = FF_InitEntryFetch( pxIOManager, ulDirCluster, &xFetchContext );
+
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ for( xIndex = 0; xIndex < FF_MAX_ENTRIES_PER_DIRECTORY; xIndex++ )
+ {
+ if( ( pucEntryBuffer == NULL ) ||
+ ( pucEntryBuffer >= xFetchContext.pxBuffer->pucBuffer + ( FF_SIZEOF_SECTOR - FF_SIZEOF_DIRECTORY_ENTRY ) ) )
+ {
+ xError = FF_FetchEntryWithContext( pxIOManager, ( uint32_t ) xIndex, &xFetchContext, NULL );
+ if( FF_isERR( xError ) )
+ {
+ break;
+ }
+ pucEntryBuffer = xFetchContext.pxBuffer->pucBuffer;
+ }
+ else
+ {
+ /* Advance the pointer 32 bytes to the next directory entry. */
+ pucEntryBuffer += FF_SIZEOF_DIRECTORY_ENTRY;
+ }
+ if( FF_isDeleted( pucEntryBuffer ) == pdFALSE )
+ {
+ ucAttrib = FF_getChar( pucEntryBuffer, FF_FAT_DIRENT_ATTRIB );
+ if( ( ( ucAttrib & FF_FAT_ATTR_LFN ) != FF_FAT_ATTR_LFN ) &&
+ ( ( ucAttrib & FF_FAT_ATTR_VOLID ) != FF_FAT_ATTR_VOLID ) )
+ {
+ memcpy ( pcMyShortName, pucEntryBuffer, 11 );
+ FF_ProcessShortName( pcMyShortName );
+ if( FF_isEndOfDir( pucEntryBuffer ) )
+ {
+ break;
+ }
+
+ /* Generate the Hash. */
+ #if( ffconfigHASH_FUNCTION == CRC16 )
+ {
+ ulHash = FF_GetCRC16( ( uint8_t * ) pcMyShortName, strlen( pcMyShortName ) );
+ }
+ #else /* ffconfigHASH_FUNCTION == CRC8 */
+ {
+ ulHash = FF_GetCRC8( pcMyShortName, strlen( pcMyShortName ) );
+ }
+ #endif
+ FF_SetHash( pxHashCache, ulHash );
+ }
+ }
+ } /* for( xIndex = 0; xIndex < FF_MAX_ENTRIES_PER_DIRECTORY; xIndex++ ) */
+ {
+ FF_Error_t xTempError;
+ xTempError = FF_CleanupEntryFetch( pxIOManager, &xFetchContext );
+ if( FF_isERR( xError ) == pdFALSE )
+ {
+ xError = xTempError;
+ }
+ }
+ }
+ } /* if( pxHashCache != NULL ) */
+ else
+ {
+ xError = -1;
+ }
+
+ return xError;
+ } /* FF_HashDir() */
+#endif /* ffconfigHASH_CACHE != 0 */
+/*-----------------------------------------------------------*/
+
+#if( ffconfigHASH_CACHE != 0 )
+ /* FF_UnHashDir() : invalidate the hash tables of a given directory.
+ It is called when a file or sub-directory is removed or when the
+ directory itself is removed. */
+ void FF_UnHashDir( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster )
+ {
+ FF_HashTable_t *pxHash = pxIOManager->xHashCache;
+ FF_HashTable_t *pxLast = pxIOManager->xHashCache + ffconfigHASH_CACHE_DEPTH;
+
+ for( ; pxHash < pxLast; pxHash++ )
+ {
+ if( pxHash->ulDirCluster == ulDirCluster )
+ {
+ pxHash->ulDirCluster = 0;
+ break;
+ }
+ }
+ } /* FF_UnHashDir() */
+#endif /* ffconfigHASH_CACHE */
+/*-----------------------------------------------------------*/
+
+#if( ffconfigHASH_CACHE != 0 )
+ /**
+ *
+ *
+ **/
+ void FF_SetHash( FF_HashTable_t *pxHash, uint32_t ulHash )
+ {
+ uint32_t tblIndex = ( ulHash / 32 ) % FF_HASH_TABLE_ENTRY_COUNT;
+ uint32_t tblBit = ulHash % 32;
+
+ pxHash->ulBitTable[ tblIndex ] |= ( 0x80000000ul >> tblBit );
+ } /* FF_SetHash() */
+#endif /* ffconfigHASH_CACHE */
+/*-----------------------------------------------------------*/
+
+#if( ffconfigHASH_CACHE != 0 )
+ void FF_ClearHash( FF_HashTable_t *pxHash, uint32_t ulHash )
+ {
+ if( pxHash != NULL )
+ {
+ uint32_t tblIndex = ( ulHash / 32 ) % FF_HASH_TABLE_ENTRY_COUNT;
+ uint32_t tblBit = ulHash % 32;
+
+ pxHash->ulBitTable[ tblIndex ] &= ~( 0x80000000ul >> tblBit );
+ }
+ } /* FF_ClearHash() */
+#endif /* ffconfigHASH_CACHE */
+/*-----------------------------------------------------------*/
+
+#if( ffconfigHASH_CACHE != 0 )
+ BaseType_t FF_isHashSet( FF_HashTable_t *pxHash, uint32_t ulHash )
+ {
+ FF_Error_t xResult;
+
+ xResult = pdFALSE;
+
+ if( pxHash != NULL )
+ {
+ uint32_t tblIndex = ( ulHash / 32 ) % FF_HASH_TABLE_ENTRY_COUNT;
+ uint32_t tblBit = ulHash % 32;
+
+ if( pxHash->ulBitTable[ tblIndex ] & ( 0x80000000ul >> tblBit ) )
+ {
+ xResult = pdTRUE;
+ }
+ }
+
+ return xResult;
+ } /* FF_isHashSet() */
+#endif /* ffconfigHASH_CACHE */
+/*-----------------------------------------------------------*/
+
+#if( ffconfigHASH_CACHE != 0 )
+ void FF_AddDirentHash( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster, uint32_t ulHash )
+ {
+ FF_HashTable_t *pxHash = pxIOManager->xHashCache;
+ FF_HashTable_t *pxLast = pxIOManager->xHashCache + ffconfigHASH_CACHE_DEPTH;
+
+ for( ; pxHash < pxLast; pxHash++ )
+ {
+ if( pxHash->ulDirCluster == ulDirCluster )
+ {
+ FF_SetHash( pxHash, ulHash );
+ break;
+ }
+ }
+ } /* FF_AddDirentHash() */
+#endif /* ffconfigHASH_CACHE*/
+/*-----------------------------------------------------------*/
+
+#if( ffconfigHASH_CACHE != 0 )
+ BaseType_t FF_CheckDirentHash( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster, uint32_t ulHash )
+ {
+ FF_HashTable_t *pxHash = pxIOManager->xHashCache;
+ FF_HashTable_t *pxLast = pxIOManager->xHashCache + ffconfigHASH_CACHE_DEPTH;
+ BaseType_t xResult;
+
+ for( ; ; )
+ {
+ if( pxHash->ulDirCluster == ulDirCluster )
+ {
+ xResult = FF_isHashSet( pxHash, ulHash );
+ break;
+ }
+ pxHash++;
+ if( pxHash >= pxLast )
+ {
+ xResult = -1;
+ break;
+ }
+ }
+
+ return xResult;
+ } /* FF_CheckDirentHash() */
+#endif /* ffconfigHASH_CACHE */
+/*-----------------------------------------------------------*/
+
+#if( ffconfigHASH_CACHE != 0 )
+ BaseType_t FF_DirHashed( FF_IOManager_t *pxIOManager, uint32_t ulDirCluster )
+ {
+ FF_HashTable_t *pxHash = pxIOManager->xHashCache;
+ FF_HashTable_t *pxLast = pxIOManager->xHashCache + ffconfigHASH_CACHE_DEPTH;
+ BaseType_t xResult;
+
+ for( ; ; )
+ {
+ if( pxHash->ulDirCluster == ulDirCluster )
+ {
+ xResult = pdTRUE;
+ break;
+ }
+ pxHash++;
+ if( pxHash >= pxLast )
+ {
+ xResult = pdFALSE;
+ break;
+ }
+ }
+
+ return xResult;
+ } /* FF_DirHashed() */
+#endif /* ffconfigHASH_CACHE */
+/*-----------------------------------------------------------*/
+