1 /*****************************************************************************
2 * FullFAT - High Performance, Thread-Safe Embedded FAT File-System *
3 * Copyright (C) 2009 James Walmsley (james@worm.me.uk) *
5 * This program is free software: you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation, either version 3 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
20 * Alternative Licensing is available directly from the Copyright holder, *
21 * (James Walmsley). For more information consult LICENSING.TXT to obtain *
22 * a Commercial license. *
24 * See RESTRICTIONS.TXT for extra restrictions on the use of FullFAT. *
26 * Removing the above notice is illegal and will invalidate this license. *
27 *****************************************************************************
28 * See http://worm.me.uk/fullfat for more information. *
29 * Or http://fullfat.googlecode.com/ for latest releases and the wiki. *
30 *****************************************************************************/
34 * @author James Walmsley
37 * @defgroup FILEIO FILE I/O Access
38 * @brief Provides an interface to allow File I/O on a mounted IOMAN.
40 * Provides file-system interfaces for the FAT file-system.
44 #include "ff_string.h"
48 * @brief Converts STDIO mode strings into the equivalent FullFAT mode.
50 * @param Mode The mode string e.g. "rb" "rb+" "w" "a" "r" "w+" "a+" etc
52 * @return Returns the mode bits that should be passed to the FF_Open function.
54 FF_T_UINT8
FF_GetModeBits(FF_T_INT8
*Mode
) {
55 FF_T_UINT8 ModeBits
= 0x00;
58 case 'r': // Allow Read
60 ModeBits
|= FF_MODE_READ
;
63 case 'w': // Allow Write
65 ModeBits
|= FF_MODE_WRITE
;
66 ModeBits
|= FF_MODE_CREATE
; // Create if not exist.
67 ModeBits
|= FF_MODE_TRUNCATE
;
70 case 'a': // Append new writes to the end of the file.
72 ModeBits
|= FF_MODE_WRITE
;
73 ModeBits
|= FF_MODE_APPEND
;
74 ModeBits
|= FF_MODE_CREATE
; // Create if not exist.
77 case '+': // Update the file, don't Append!
78 ModeBits
|= FF_MODE_READ
; // RW Mode
79 ModeBits
|= FF_MODE_WRITE
; // RW Mode
82 /*case 'D': // Internal use only!
83 ModeBits |= FF_MODE_DIR;
86 default: // b|B flags not supported (Binary mode is native anyway).
96 * FF_Open() Mode Information
98 * - Allows WRITE access to the file.
101 * - Allows READ access to the file.
104 * - Create file if it doesn't exist.
107 * - Erase the file if it already exists and overwrite.
110 * - Causes all writes to occur at the end of the file. (Its impossible to overwrite other data in a file with this flag set).
115 * - (FF_MODE_WRITE | FF_MODE_CREATE | FF_MODE_TRUNCATE)
116 * - Write access to the file. (Equivalent to "w").
118 * - (FF_MODE_WRITE | FF_MODE_READ)
119 * - Read and Write access to the file. (Equivalent to "rb+").
121 * - (FF_MODE_WRITE | FF_MODE_READ | FF_MODE_APPEND | FF_MODE_CREATE)
122 * - Read and Write append mode access to the file. (Equivalent to "a+").
125 * Be careful when choosing modes. For those using FF_Open() at the application layer
126 * its best to use the provided FF_GetModeBits() function, as this complies to the same
127 * behaviour as the stdio.h fopen() function.
134 * @brief Opens a File for Access
136 * @param pIoman FF_IOMAN object that was created by FF_CreateIOMAN().
137 * @param path Path to the File or object.
138 * @param Mode Access Mode required. Modes are a little complicated, the function FF_GetModeBits()
139 * @param Mode will convert a stdio Mode string into the equivalent Mode bits for this parameter.
140 * @param pError Pointer to a signed byte for error checking. Can be NULL if not required.
141 * @param pError To be checked when a NULL pointer is returned.
143 * @return NULL pointer on Error, in which case pError should be checked for more information.
144 * @return pError can be:
146 FF_FILE
*FF_Open(FF_IOMAN
*pIoman
, const FF_T_INT8
*path
, FF_T_UINT8 Mode
, FF_ERROR
*pError
) {
150 FF_T_UINT32 DirCluster
, FileCluster
;
151 FF_T_UINT32 nBytesPerCluster
;
152 FF_T_INT8 filename
[FF_MAX_FILENAME
];
162 *pError
= FF_ERR_NULL_POINTER
;
164 return (FF_FILE
*)NULL
;
166 pFile
= FF_Malloc(sizeof(FF_FILE
));
169 *pError
= FF_ERR_NOT_ENOUGH_MEMORY
;
171 return (FF_FILE
*)NULL
;
174 // Get the Mode Bits.
177 i
= (FF_T_UINT16
) strlen(path
);
180 if(path
[i
] == '\\' || path
[i
] == '/') {
186 strncpy(filename
, (path
+ i
+ 1), FF_MAX_FILENAME
);
193 DirCluster
= FF_FindDir(pIoman
, path
, i
);
196 //RetVal = //FF_FindEntry(pIoman, DirCluster, filename, &Object, FF_TRUE);
198 //FileCluster = Object.ObjectCluster;//FF_FindEntryInDir(pIoman, DirCluster, filename, 0x00, &Object);
203 FileCluster
= FF_FindEntryInDir(pIoman
, DirCluster
, filename
, 0x00, &Object
);
205 if(!FileCluster
) { // If 0 was returned, it might be because the file has no allocated cluster
206 if(strlen(filename
) == strlen(Object
.FileName
)) {
207 if(Object
.Filesize
== 0 && FF_strmatch(filename
, Object
.FileName
, (FF_T_UINT16
) strlen(filename
)) == FF_TRUE
) {
208 // The file really was found!
215 if((pFile
->Mode
& FF_MODE_CREATE
)) {
216 FileCluster
= FF_CreateFile(pIoman
, DirCluster
, filename
, &Object
);
217 Object
.CurrentItem
+= 1;
222 if(Object
.Attrib
== FF_FAT_ATTR_DIR
) {
223 if(!(pFile
->Mode
& FF_MODE_DIR
)) {
224 // Not the object, File Not Found!
227 *pError
= FF_ERR_FILE_OBJECT_IS_A_DIR
;
229 return (FF_FILE
*) NULL
;
233 //---------- Ensure Read-Only files don't get opened for Writing.
234 if((pFile
->Mode
& FF_MODE_WRITE
) || (pFile
->Mode
& FF_MODE_APPEND
)) {
235 if((Object
.Attrib
& FF_FAT_ATTR_READONLY
)) {
238 *pError
= FF_ERR_FILE_IS_READ_ONLY
;
240 return (FF_FILE
*) NULL
;
243 pFile
->pIoman
= pIoman
;
244 pFile
->FilePointer
= 0;
245 pFile
->ObjectCluster
= Object
.ObjectCluster
;
246 pFile
->Filesize
= Object
.Filesize
;
247 pFile
->CurrentCluster
= 0;
248 pFile
->AddrCurrentCluster
= pFile
->ObjectCluster
;
249 //pFile->Mode = Mode;
251 pFile
->DirCluster
= DirCluster
;
252 pFile
->DirEntry
= Object
.CurrentItem
- 1;
253 nBytesPerCluster
= pFile
->pIoman
->pPartition
->SectorsPerCluster
/ pIoman
->BlkSize
;
254 pFile
->iChainLength
= 0;
255 pFile
->iEndOfChain
= 0;
256 pFile
->FileDeleted
= FF_FALSE
;
258 // File Permission Processing
259 // Only "w" and "w+" mode strings can erase a file's contents.
260 // Any other combinations will not cause an erase.
261 if((pFile
->Mode
& FF_MODE_TRUNCATE
)) {
263 pFile
->FilePointer
= 0;
267 Add pFile onto the end of our linked list of FF_FILE objects.
269 FF_PendSemaphore(pIoman
->pSemaphore
);
271 if(!pIoman
->FirstFile
) {
272 pIoman
->FirstFile
= pFile
;
274 pFileChain
= (FF_FILE
*) pIoman
->FirstFile
;
276 if(pFileChain
->ObjectCluster
== pFile
->ObjectCluster
) {
277 // File is already open! DON'T ALLOW IT!
278 FF_ReleaseSemaphore(pIoman
->pSemaphore
);
281 *pError
= FF_ERR_FILE_ALREADY_OPEN
;
283 return (FF_FILE
*) NULL
;
285 if(!pFileChain
->Next
) {
286 pFileChain
->Next
= pFile
;
289 pFileChain
= (FF_FILE
*) pFileChain
->Next
;
290 }while(pFileChain
!= NULL
);
293 FF_ReleaseSemaphore(pIoman
->pSemaphore
);
299 *pError
= FF_ERR_FILE_NOT_FOUND
;
301 return (FF_FILE
*) NULL
;
305 *pError
= FF_ERR_FILE_INVALID_PATH
;
310 return (FF_FILE
*)NULL
;
316 * @brief Tests if a Directory contains any other files or folders.
318 * @param pIoman FF_IOMAN object returned from the FF_CreateIOMAN() function.
321 FF_T_BOOL
FF_isDirEmpty(FF_IOMAN
*pIoman
, const FF_T_INT8
*Path
) {
324 FF_ERROR RetVal
= FF_ERR_NONE
;
331 RetVal
= FF_FindFirst(pIoman
, &MyDir
, Path
);
334 RetVal
= FF_FindNext(pIoman
, &MyDir
);
343 FF_ERROR
FF_RmDir(FF_IOMAN
*pIoman
, const FF_T_INT8
*path
) {
345 FF_ERROR Error
= FF_ERR_NONE
;
346 FF_T_UINT8 EntryBuffer
[32];
347 FF_T_SINT8 RetVal
= FF_ERR_NONE
;
353 return FF_ERR_NULL_POINTER
;
356 pFile
= FF_Open(pIoman
, path
, FF_MODE_DIR
, &Error
);
359 return Error
; // File in use or File not found!
362 pFile
->FileDeleted
= FF_TRUE
;
366 if(FF_isDirEmpty(pIoman
, path
)) {
369 FF_UnlinkClusterChain(pIoman
, pFile
->ObjectCluster
, 0); // 0 to delete the entire chain!
371 FF_unlockFAT(pIoman
);
373 // Edit the Directory Entry! (So it appears as deleted);
374 FF_RmLFNs(pIoman
, pFile
->DirCluster
, pFile
->DirEntry
);
375 FF_FetchEntry(pIoman
, pFile
->DirCluster
, pFile
->DirEntry
, EntryBuffer
);
376 EntryBuffer
[0] = 0xE5;
377 FF_PushEntry(pIoman
, pFile
->DirCluster
, pFile
->DirEntry
, EntryBuffer
);
379 FF_PendSemaphore(pIoman
->pSemaphore
); // Thread safety on shared object!
381 for(i
= 0; i
< FF_PATH_CACHE_DEPTH
; i
++) {
382 if(FF_strmatch(pIoman
->pPartition
->PathCache
[i
].Path
, path
, (FF_T_UINT16
)strlen(path
))) {
383 pIoman
->pPartition
->PathCache
[i
].Path
[0] = '\0';
384 pIoman
->pPartition
->PathCache
[i
].DirCluster
= 0;
385 FF_ReleaseSemaphore(pIoman
->pSemaphore
);
389 FF_ReleaseSemaphore(pIoman
->pSemaphore
);
392 FF_IncreaseFreeClusters(pIoman
, pFile
->iChainLength
);
394 FF_FlushCache(pIoman
);
396 RetVal
= FF_ERR_DIR_NOT_EMPTY
;
399 FF_unlockDIR(pIoman
);
401 FF_Close(pFile
); // Free the file pointer resources
406 FF_ERROR
FF_RmFile(FF_IOMAN
*pIoman
, const FF_T_INT8
*path
) {
409 FF_T_UINT8 EntryBuffer
[32];
411 pFile
= FF_Open(pIoman
, path
, FF_MODE_READ
, &Error
);
414 return Error
; // File in use or File not found!
417 pFile
->FileDeleted
= FF_TRUE
;
419 FF_lockFAT(pIoman
); // Lock the FAT so its thread-safe.
421 FF_UnlinkClusterChain(pIoman
, pFile
->ObjectCluster
, 0); // 0 to delete the entire chain!
423 FF_unlockFAT(pIoman
);
425 // Edit the Directory Entry! (So it appears as deleted);
428 FF_RmLFNs(pIoman
, pFile
->DirCluster
, pFile
->DirEntry
);
429 FF_FetchEntry(pIoman
, pFile
->DirCluster
, pFile
->DirEntry
, EntryBuffer
);
430 EntryBuffer
[0] = 0xE5;
431 FF_PushEntry(pIoman
, pFile
->DirCluster
, pFile
->DirEntry
, EntryBuffer
);
433 FF_unlockDIR(pIoman
);
435 FF_FlushCache(pIoman
);
437 FF_Close(pFile
); // Free the file pointer resources
443 * @brief Moves a file or directory from source to destination.
445 * @param pIoman The FF_IOMAN object pointer.
446 * @param szSourceFile String of the source file to be moved or renamed.
447 * @param szDestinationFile String of the destination file to where the source should be moved or renamed.
449 * @return FF_ERR_NONE on success.
450 * @return FF_ERR_FILE_DESTINATION_EXISTS if the destination file exists.
451 * @return FF_ERR_FILE_COULD_NOT_CREATE_DIRENT if dirent creation failed (fatal error!).
452 * @return FF_ERR_FILE_DIR_NOT_FOUND if destination directory was not found.
453 * @return FF_ERR_FILE_SOURCE_NOT_FOUND if the source file was not found.
456 FF_ERROR
FF_Move(FF_IOMAN
*pIoman
, const FF_T_INT8
*szSourceFile
, const FF_T_INT8
*szDestinationFile
) {
458 FF_FILE
*pSrcFile
, *pDestFile
;
460 FF_T_UINT8 EntryBuffer
[32];
462 FF_T_UINT32 DirCluster
;
465 return FF_ERR_NULL_POINTER
;
468 // Check destination file doesn't exist!
469 pDestFile
= FF_Open(pIoman
, szDestinationFile
, FF_MODE_READ
, &Error
);
471 if(pDestFile
|| (Error
== FF_ERR_FILE_OBJECT_IS_A_DIR
)) {
473 return FF_ERR_FILE_DESTINATION_EXISTS
; // YES -- FAIL
476 pSrcFile
= FF_Open(pIoman
, szSourceFile
, FF_MODE_READ
, &Error
);
478 if(Error
== FF_ERR_FILE_OBJECT_IS_A_DIR
) {
479 // Open a directory for moving!
480 pSrcFile
= FF_Open(pIoman
, szSourceFile
, FF_MODE_DIR
, &Error
);
485 // Create the new dirent.
486 FF_FetchEntry(pIoman
, pSrcFile
->DirCluster
, pSrcFile
->DirEntry
, EntryBuffer
);
487 MyFile
.Attrib
= FF_getChar(EntryBuffer
, (FF_T_UINT16
)(FF_FAT_DIRENT_ATTRIB
));
488 MyFile
.Filesize
= pSrcFile
->Filesize
;
489 MyFile
.ObjectCluster
= pSrcFile
->ObjectCluster
;
490 MyFile
.CurrentItem
= 0;
492 i
= (FF_T_UINT16
) strlen(szDestinationFile
);
495 if(szDestinationFile
[i
] == '\\' || szDestinationFile
[i
] == '/') {
501 strncpy(MyFile
.FileName
, (szDestinationFile
+ i
+ 1), FF_MAX_FILENAME
);
508 DirCluster
= FF_FindDir(pIoman
, szDestinationFile
, i
);
512 // Destination Dir was found, we can now create the new entry.
513 if(FF_CreateDirent(pIoman
, DirCluster
, &MyFile
)) {
515 return FF_ERR_FILE_COULD_NOT_CREATE_DIRENT
; // FAILED
518 // Edit the Directory Entry! (So it appears as deleted);
521 FF_RmLFNs(pIoman
, pSrcFile
->DirCluster
, pSrcFile
->DirEntry
);
522 FF_FetchEntry(pIoman
, pSrcFile
->DirCluster
, pSrcFile
->DirEntry
, EntryBuffer
);
523 EntryBuffer
[0] = 0xE5;
524 FF_PushEntry(pIoman
, pSrcFile
->DirCluster
, pSrcFile
->DirEntry
, EntryBuffer
);
526 FF_unlockDIR(pIoman
);
529 FF_FlushCache(pIoman
);
534 return FF_ERR_FILE_DIR_NOT_FOUND
;
538 return FF_ERR_FILE_SOURCE_NOT_FOUND
; // Source not found!
544 * @brief Get's the next Entry based on the data recorded in the FF_DIRENT object.
546 * @param pFile FF_FILE object that was created by FF_Open().
548 * @return FF_TRUE if End of File was reached. FF_FALSE if not.
549 * @return FF_FALSE if a null pointer was provided.
552 FF_T_BOOL
FF_isEOF(FF_FILE
*pFile
) {
556 if(pFile
->FilePointer
>= pFile
->Filesize
) {
563 static FF_T_UINT32
FF_GetSequentialClusters(FF_IOMAN
*pIoman
, FF_T_UINT32 StartCluster
, FF_T_UINT32 Limit
) {
564 FF_T_UINT32 CurrentCluster
;
565 FF_T_UINT32 NextCluster
= StartCluster
;
569 CurrentCluster
= NextCluster
;
570 NextCluster
= FF_getFatEntry(pIoman
, CurrentCluster
);
571 if(NextCluster
== (CurrentCluster
+ 1)) {
582 }while(NextCluster
== (CurrentCluster
+ 1));
587 static FF_T_SINT32
FF_ReadClusters(FF_FILE
*pFile
, FF_T_UINT32 Count
, FF_T_UINT8
*buffer
) {
589 FF_T_UINT32 SequentialClusters
= 0;
590 FF_T_UINT32 nItemLBA
;
594 if((Count
- 1) > 0) {
595 SequentialClusters
= FF_GetSequentialClusters(pFile
->pIoman
, pFile
->AddrCurrentCluster
, (Count
- 1));
597 Sectors
= (SequentialClusters
+ 1) * pFile
->pIoman
->pPartition
->SectorsPerCluster
;
598 nItemLBA
= FF_Cluster2LBA(pFile
->pIoman
, pFile
->AddrCurrentCluster
);
599 nItemLBA
= FF_getRealLBA(pFile
->pIoman
, nItemLBA
);
602 if(pFile
->pIoman
->pBlkDevice
->fnReadBlocks
) {
603 RetVal
= pFile
->pIoman
->pBlkDevice
->fnReadBlocks(buffer
, nItemLBA
, Sectors
, pFile
->pIoman
->pBlkDevice
->pParam
);
604 if(RetVal
== FF_ERR_DRIVER_BUSY
) {
606 FF_Sleep(FF_DRIVER_BUSY_SLEEP
);
609 RetVal
= FF_ERR_DEVICE_DRIVER_FAILED
;
612 }while(RetVal
== FF_ERR_DRIVER_BUSY
);
614 Count
-= (SequentialClusters
+ 1);
615 pFile
->AddrCurrentCluster
= FF_TraverseFAT(pFile
->pIoman
, pFile
->AddrCurrentCluster
, (SequentialClusters
+ 1));
616 pFile
->CurrentCluster
+= (SequentialClusters
+ 1);
617 buffer
+= Sectors
* pFile
->pIoman
->BlkSize
;
618 SequentialClusters
= 0;
625 static FF_ERROR
FF_ExtendFile(FF_FILE
*pFile
, FF_T_UINT32 Size
) {
626 FF_IOMAN
*pIoman
= pFile
->pIoman
;
627 FF_T_UINT32 nBytesPerCluster
= pIoman
->pPartition
->BlkSize
* pIoman
->pPartition
->SectorsPerCluster
;
628 FF_T_UINT32 nTotalClustersNeeded
= Size
/ nBytesPerCluster
;
629 FF_T_UINT32 nClusterToExtend
;
630 FF_T_UINT32 CurrentCluster
, NextCluster
;
632 FF_DIRENT OriginalEntry
;
634 if((pFile
->Mode
& FF_MODE_WRITE
) != FF_MODE_WRITE
) {
635 return FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE
;
638 if(pFile
->Filesize
== 0 && pFile
->ObjectCluster
== 0) { // No Allocated clusters.
639 // Create a Cluster chain!
640 pFile
->AddrCurrentCluster
= FF_CreateClusterChain(pFile
->pIoman
);
641 if(!FF_GetEntry(pIoman
, pFile
->DirEntry
, pFile
->DirCluster
, &OriginalEntry
)) {
642 OriginalEntry
.ObjectCluster
= pFile
->AddrCurrentCluster
;
643 FF_PutEntry(pIoman
, pFile
->DirEntry
, pFile
->DirCluster
, &OriginalEntry
);
645 return FF_ERR_FILE_EXTEND_FAILED
;
647 pFile
->ObjectCluster
= pFile
->AddrCurrentCluster
;
648 pFile
->iChainLength
= 1;
649 pFile
->CurrentCluster
= 0;
650 pFile
->iEndOfChain
= pFile
->AddrCurrentCluster
;
653 if(Size
% nBytesPerCluster
) {
654 nTotalClustersNeeded
+= 1;
657 if(pFile
->iChainLength
== 0) { // First extension requiring the chain length,
658 pFile
->iChainLength
= FF_GetChainLength(pIoman
, pFile
->ObjectCluster
, &pFile
->iEndOfChain
);
661 nClusterToExtend
= (nTotalClustersNeeded
- pFile
->iChainLength
);
663 if(nTotalClustersNeeded
> pFile
->iChainLength
) {
665 NextCluster
= pFile
->AddrCurrentCluster
;
668 for(i
= 0; i
<= nClusterToExtend
; i
++) {
669 CurrentCluster
= FF_FindEndOfChain(pIoman
, NextCluster
);
670 NextCluster
= FF_FindFreeCluster(pIoman
);
672 FF_unlockFAT(pIoman
);
673 return FF_ERR_FAT_NO_FREE_CLUSTERS
;
675 FF_putFatEntry(pIoman
, CurrentCluster
, NextCluster
);
676 FF_putFatEntry(pIoman
, NextCluster
, 0xFFFFFFFF);
679 pFile
->iEndOfChain
= FF_FindEndOfChain(pIoman
, NextCluster
);
681 FF_unlockFAT(pIoman
);
683 pFile
->iChainLength
+= i
;
684 FF_DecreaseFreeClusters(pIoman
, i
); // Keep Tab of Numbers for fast FreeSize()
690 static FF_T_SINT32
FF_WriteClusters(FF_FILE
*pFile
, FF_T_UINT32 Count
, FF_T_UINT8
*buffer
) {
692 FF_T_UINT32 SequentialClusters
= 0;
693 FF_T_UINT32 nItemLBA
;
697 if((Count
- 1) > 0) {
698 SequentialClusters
= FF_GetSequentialClusters(pFile
->pIoman
, pFile
->AddrCurrentCluster
, (Count
- 1));
700 Sectors
= (SequentialClusters
+ 1) * pFile
->pIoman
->pPartition
->SectorsPerCluster
;
701 nItemLBA
= FF_Cluster2LBA(pFile
->pIoman
, pFile
->AddrCurrentCluster
);
702 nItemLBA
= FF_getRealLBA(pFile
->pIoman
, nItemLBA
);
705 if(pFile
->pIoman
->pBlkDevice
->fnWriteBlocks
) {
706 RetVal
= pFile
->pIoman
->pBlkDevice
->fnWriteBlocks(buffer
, nItemLBA
, Sectors
, pFile
->pIoman
->pBlkDevice
->pParam
);
707 if(RetVal
== FF_ERR_DRIVER_BUSY
) {
709 FF_Sleep(FF_DRIVER_BUSY_SLEEP
);
712 RetVal
= FF_ERR_DEVICE_DRIVER_FAILED
;
715 }while(RetVal
== FF_ERR_DRIVER_BUSY
);
717 Count
-= (SequentialClusters
+ 1);
718 pFile
->AddrCurrentCluster
= FF_TraverseFAT(pFile
->pIoman
, pFile
->AddrCurrentCluster
, (SequentialClusters
+ 1));
719 pFile
->CurrentCluster
+= (SequentialClusters
+ 1);
720 buffer
+= Sectors
* pFile
->pIoman
->BlkSize
;
721 SequentialClusters
= 0;
729 * @brief Equivalent to fread()
731 * @param pFile FF_FILE object that was created by FF_Open().
732 * @param ElementSize The size of an element to read.
733 * @param Count The number of elements to read.
734 * @param buffer A pointer to a buffer of adequate size to be filled with the requested data.
736 * @return Number of bytes read.
739 FF_T_SINT32
FF_Read(FF_FILE
*pFile
, FF_T_UINT32 ElementSize
, FF_T_UINT32 Count
, FF_T_UINT8
*buffer
) {
740 FF_T_UINT32 nBytes
= ElementSize
* Count
;
741 FF_T_UINT32 nBytesRead
= 0;
742 FF_T_UINT32 nBytesToRead
;
745 FF_T_UINT32 nRelBlockPos
;
746 FF_T_UINT32 nItemLBA
;
747 FF_T_SINT32 RetVal
= 0;
748 FF_T_UINT16 sSectors
;
749 FF_T_UINT32 nRelClusterPos
;
750 FF_T_UINT32 nBytesPerCluster
;
751 FF_T_UINT32 nClusterDiff
;
754 return FF_ERR_NULL_POINTER
;
757 if(!(pFile
->Mode
& FF_MODE_READ
)) {
758 return FF_ERR_FILE_NOT_OPENED_IN_READ_MODE
;
761 pIoman
= pFile
->pIoman
;
763 if(pFile
->FilePointer
== pFile
->Filesize
) {
767 if((pFile
->FilePointer
+ nBytes
) > pFile
->Filesize
) {
768 nBytes
= pFile
->Filesize
- pFile
->FilePointer
;
771 nClusterDiff
= FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1) - pFile
->CurrentCluster
;
773 if(pFile
->CurrentCluster
< FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1)) {
774 pFile
->AddrCurrentCluster
= FF_TraverseFAT(pIoman
, pFile
->AddrCurrentCluster
, nClusterDiff
);
775 pFile
->CurrentCluster
+= nClusterDiff
;
779 nRelBlockPos
= FF_getMinorBlockEntry(pIoman
, pFile
->FilePointer
, 1); // Get the position within a block.
781 nItemLBA
= FF_Cluster2LBA(pIoman
, pFile
->AddrCurrentCluster
);
782 nItemLBA
= FF_getRealLBA(pIoman
, nItemLBA
+ FF_getMajorBlockNumber(pIoman
, pFile
->FilePointer
, 1)) + FF_getMinorBlockNumber(pIoman
, pFile
->FilePointer
, 1);
784 if((nRelBlockPos
+ nBytes
) < pIoman
->BlkSize
) { // Bytes to read are within a block and less than a block size.
785 pBuffer
= FF_GetBuffer(pIoman
, nItemLBA
, FF_MODE_READ
);
787 memcpy(buffer
, (pBuffer
->pBuffer
+ nRelBlockPos
), nBytes
);
789 FF_ReleaseBuffer(pIoman
, pBuffer
);
791 pFile
->FilePointer
+= nBytes
;
793 return nBytes
; // Return the number of bytes read.
797 //---------- Read (memcpy) to a Sector Boundary
798 if(nRelBlockPos
!= 0) { // Not on a sector boundary, at this point the LBA is known.
799 nBytesToRead
= pIoman
->BlkSize
- nRelBlockPos
;
800 pBuffer
= FF_GetBuffer(pIoman
, nItemLBA
, FF_MODE_READ
);
802 // Here we copy to the sector boudary.
803 memcpy(buffer
, (pBuffer
->pBuffer
+ nRelBlockPos
), nBytesToRead
);
805 FF_ReleaseBuffer(pIoman
, pBuffer
);
807 nBytes
-= nBytesToRead
;
808 nBytesRead
+= nBytesToRead
;
809 pFile
->FilePointer
+= nBytesToRead
;
810 buffer
+= nBytesToRead
;
814 //---------- Read to a Cluster Boundary
816 nRelClusterPos
= FF_getClusterPosition(pIoman
, pFile
->FilePointer
, 1);
817 nBytesPerCluster
= (pIoman
->pPartition
->SectorsPerCluster
* pIoman
->BlkSize
);
818 if(nRelClusterPos
!= 0 && nBytes
>= nBytesPerCluster
) { // Need to get to cluster boundary
820 nClusterDiff
= FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1) - pFile
->CurrentCluster
;
822 if(pFile
->CurrentCluster
< FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1)) {
823 pFile
->AddrCurrentCluster
= FF_TraverseFAT(pIoman
, pFile
->AddrCurrentCluster
, nClusterDiff
);
824 pFile
->CurrentCluster
+= nClusterDiff
;
828 nItemLBA
= FF_Cluster2LBA(pIoman
, pFile
->AddrCurrentCluster
);
829 nItemLBA
= FF_getRealLBA(pIoman
, nItemLBA
+ FF_getMajorBlockNumber(pIoman
, pFile
->FilePointer
, 1)) + FF_getMinorBlockNumber(pIoman
, pFile
->FilePointer
, 1);
831 sSectors
= (FF_T_UINT16
) (pIoman
->pPartition
->SectorsPerCluster
- (nRelClusterPos
/ pIoman
->BlkSize
));
834 if(pIoman
->pBlkDevice
->fnReadBlocks
) {
835 RetVal
= pFile
->pIoman
->pBlkDevice
->fnReadBlocks(buffer
, nItemLBA
, sSectors
, pIoman
->pBlkDevice
->pParam
);
837 if(RetVal
== FF_ERR_DRIVER_BUSY
) {
839 FF_Sleep(FF_DRIVER_BUSY_SLEEP
);
841 }while(RetVal
== FF_ERR_DRIVER_BUSY
);
843 nBytesToRead
= sSectors
* pIoman
->BlkSize
;
844 nBytes
-= nBytesToRead
;
845 buffer
+= nBytesToRead
;
846 nBytesRead
+= nBytesToRead
;
847 pFile
->FilePointer
+= nBytesToRead
;
851 //---------- Read Clusters
852 if(nBytes
>= nBytesPerCluster
) {
853 //----- Thanks to Christopher Clark of DigiPen Institute of Technology in Redmond, US adding this traversal check.
854 nClusterDiff
= FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1) - pFile
->CurrentCluster
;
856 if(pFile
->CurrentCluster
< FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1)) {
857 pFile
->AddrCurrentCluster
= FF_TraverseFAT(pIoman
, pFile
->AddrCurrentCluster
, nClusterDiff
);
858 pFile
->CurrentCluster
+= nClusterDiff
;
861 //----- End of Contributor fix.
863 FF_ReadClusters(pFile
, (nBytes
/ nBytesPerCluster
), buffer
);
864 nBytesToRead
= (nBytesPerCluster
* (nBytes
/ nBytesPerCluster
));
866 pFile
->FilePointer
+= nBytesToRead
;
868 nBytes
-= nBytesToRead
;
869 buffer
+= nBytesToRead
;
870 nBytesRead
+= nBytesToRead
;
873 //---------- Read Remaining Blocks
874 if(nBytes
>= pIoman
->BlkSize
) {
875 sSectors
= (FF_T_UINT16
) (nBytes
/ pIoman
->BlkSize
);
877 nClusterDiff
= FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1) - pFile
->CurrentCluster
;
879 if(pFile
->CurrentCluster
< FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1)) {
880 pFile
->AddrCurrentCluster
= FF_TraverseFAT(pIoman
, pFile
->AddrCurrentCluster
, nClusterDiff
);
881 pFile
->CurrentCluster
+= nClusterDiff
;
885 nItemLBA
= FF_Cluster2LBA(pIoman
, pFile
->AddrCurrentCluster
);
886 nItemLBA
= FF_getRealLBA(pIoman
, nItemLBA
+ FF_getMajorBlockNumber(pIoman
, pFile
->FilePointer
, 1)) + FF_getMinorBlockNumber(pIoman
, pFile
->FilePointer
, 1);
889 if(pIoman
->pBlkDevice
->fnReadBlocks
) {
890 RetVal
= pFile
->pIoman
->pBlkDevice
->fnReadBlocks(buffer
, nItemLBA
, sSectors
, pIoman
->pBlkDevice
->pParam
);
892 if(RetVal
== FF_ERR_DRIVER_BUSY
) {
894 FF_Sleep(FF_DRIVER_BUSY_SLEEP
);
896 }while(RetVal
== FF_ERR_DRIVER_BUSY
);
898 nBytesToRead
= sSectors
* pIoman
->BlkSize
;
899 pFile
->FilePointer
+= nBytesToRead
;
900 nBytes
-= nBytesToRead
;
901 buffer
+= nBytesToRead
;
902 nBytesRead
+= nBytesToRead
;
905 //---------- Read (memcpy) Remaining Bytes
908 nClusterDiff
= FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1) - pFile
->CurrentCluster
;
910 if(pFile
->CurrentCluster
< FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1)) {
911 pFile
->AddrCurrentCluster
= FF_TraverseFAT(pIoman
, pFile
->AddrCurrentCluster
, nClusterDiff
);
912 pFile
->CurrentCluster
+= nClusterDiff
;
916 nItemLBA
= FF_Cluster2LBA(pIoman
, pFile
->AddrCurrentCluster
);
917 nItemLBA
= FF_getRealLBA(pIoman
, nItemLBA
+ FF_getMajorBlockNumber(pIoman
, pFile
->FilePointer
, 1)) + FF_getMinorBlockNumber(pIoman
, pFile
->FilePointer
, 1);
918 pBuffer
= FF_GetBuffer(pIoman
, nItemLBA
, FF_MODE_READ
);
920 memcpy(buffer
, pBuffer
->pBuffer
, nBytes
);
922 FF_ReleaseBuffer(pIoman
, pBuffer
);
924 nBytesToRead
= nBytes
;
925 pFile
->FilePointer
+= nBytesToRead
;
926 nBytes
-= nBytesToRead
;
927 buffer
+= nBytesToRead
;
928 nBytesRead
+= nBytesToRead
;
941 * @brief Equivalent to fgetc()
943 * @param pFile FF_FILE object that was created by FF_Open().
945 * @return The character that was read (cast as a 32-bit interger). -1 on EOF.
946 * @return -2 If a null file pointer was provided.
947 * @return -3 Device access failed.
950 FF_T_SINT32
FF_GetC(FF_FILE
*pFile
) {
954 FF_T_UINT32 relMinorBlockPos
;
955 FF_T_UINT32 clusterNum
;
956 FF_T_UINT32 nClusterDiff
;
960 return FF_ERR_NULL_POINTER
;
963 if(!(pFile
->Mode
& FF_MODE_READ
)) {
964 return FF_ERR_FILE_NOT_OPENED_IN_READ_MODE
;
967 if(pFile
->FilePointer
>= pFile
->Filesize
) {
971 relMinorBlockPos
= FF_getMinorBlockEntry(pFile
->pIoman
, pFile
->FilePointer
, 1);
972 clusterNum
= FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1);
974 nClusterDiff
= FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1) - pFile
->CurrentCluster
;
976 if(pFile
->CurrentCluster
< FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1)) {
977 pFile
->AddrCurrentCluster
= FF_TraverseFAT(pFile
->pIoman
, pFile
->AddrCurrentCluster
, nClusterDiff
);
978 pFile
->CurrentCluster
+= nClusterDiff
;
983 fileLBA
= FF_Cluster2LBA(pFile
->pIoman
, pFile
->AddrCurrentCluster
) + FF_getMajorBlockNumber(pFile
->pIoman
, pFile
->FilePointer
, (FF_T_UINT16
) 1);
984 fileLBA
= FF_getRealLBA (pFile
->pIoman
, fileLBA
) + FF_getMinorBlockNumber(pFile
->pIoman
, pFile
->FilePointer
, (FF_T_UINT16
) 1);
986 pBuffer
= FF_GetBuffer(pFile
->pIoman
, fileLBA
, FF_MODE_READ
);
991 retChar
= pBuffer
->pBuffer
[relMinorBlockPos
];
993 FF_ReleaseBuffer(pFile
->pIoman
, pBuffer
);
995 pFile
->FilePointer
+= 1;
997 return (FF_T_INT32
) retChar
;
1000 FF_T_UINT32
FF_Tell(FF_FILE
*pFile
) {
1001 return pFile
->FilePointer
;
1007 * @brief Writes data to a File.
1009 * @param pFile FILE Pointer.
1010 * @param ElementSize Size of an Element of Data to be copied. (in bytes).
1011 * @param Count Number of Elements of Data to be copied. (ElementSize * Count must not exceed ((2^31)-1) bytes. (2GB). For best performance, multiples of 512 bytes or Cluster sizes are best.
1012 * @param buffer Byte-wise buffer containing the data to be written.
1016 FF_T_SINT32
FF_Write(FF_FILE
*pFile
, FF_T_UINT32 ElementSize
, FF_T_UINT32 Count
, FF_T_UINT8
*buffer
) {
1017 FF_T_UINT32 nBytes
= ElementSize
* Count
;
1018 FF_T_UINT32 nBytesWritten
= 0;
1019 FF_T_UINT32 nBytesToWrite
;
1022 FF_T_UINT32 nRelBlockPos
;
1023 FF_T_UINT32 nItemLBA
;
1024 FF_T_SINT32 RetVal
= 0;
1025 FF_T_UINT16 sSectors
;
1026 FF_T_UINT32 nRelClusterPos
;
1027 FF_T_UINT32 nBytesPerCluster
, nClusterDiff
, nClusters
;
1031 return FF_ERR_NULL_POINTER
;
1034 if(!(pFile
->Mode
& FF_MODE_WRITE
)) {
1035 return FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE
;
1038 // Make sure a write is after the append point.
1039 if((pFile
->Mode
& FF_MODE_APPEND
)) {
1040 if(pFile
->FilePointer
< pFile
->Filesize
) {
1041 FF_Seek(pFile
, 0, FF_SEEK_END
);
1045 pIoman
= pFile
->pIoman
;
1047 nBytesPerCluster
= (pIoman
->pPartition
->SectorsPerCluster
* pIoman
->BlkSize
);
1049 // Extend File for atleast nBytes!
1050 // Handle file-space allocation
1051 Error
= FF_ExtendFile(pFile
, pFile
->FilePointer
+ nBytes
);
1057 nRelBlockPos
= FF_getMinorBlockEntry(pIoman
, pFile
->FilePointer
, 1); // Get the position within a block.
1059 nClusterDiff
= FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1) - pFile
->CurrentCluster
;
1061 if(pFile
->CurrentCluster
!= FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1)) {
1062 pFile
->AddrCurrentCluster
= FF_TraverseFAT(pIoman
, pFile
->AddrCurrentCluster
, nClusterDiff
);
1063 pFile
->CurrentCluster
+= nClusterDiff
;
1067 nItemLBA
= FF_Cluster2LBA(pIoman
, pFile
->AddrCurrentCluster
);
1068 nItemLBA
= FF_getRealLBA(pIoman
, nItemLBA
+ FF_getMajorBlockNumber(pIoman
, pFile
->FilePointer
, 1)) + FF_getMinorBlockNumber(pIoman
, pFile
->FilePointer
, 1);
1070 if((nRelBlockPos
+ nBytes
) < pIoman
->BlkSize
) { // Bytes to read are within a block and less than a block size.
1071 pBuffer
= FF_GetBuffer(pIoman
, nItemLBA
, FF_MODE_WRITE
);
1073 memcpy((pBuffer
->pBuffer
+ nRelBlockPos
), buffer
, nBytes
);
1075 FF_ReleaseBuffer(pIoman
, pBuffer
);
1077 pFile
->FilePointer
+= nBytes
;
1078 nBytesWritten
= nBytes
;
1079 //return nBytes; // Return the number of bytes read.
1083 //---------- Write (memcpy) to a Sector Boundary
1084 if(nRelBlockPos
!= 0) { // Not on a sector boundary, at this point the LBA is known.
1085 nBytesToWrite
= pIoman
->BlkSize
- nRelBlockPos
;
1086 pBuffer
= FF_GetBuffer(pIoman
, nItemLBA
, FF_MODE_WRITE
);
1088 // Here we copy to the sector boudary.
1089 memcpy((pBuffer
->pBuffer
+ nRelBlockPos
), buffer
, nBytesToWrite
);
1091 FF_ReleaseBuffer(pIoman
, pBuffer
);
1093 nBytes
-= nBytesToWrite
;
1094 nBytesWritten
+= nBytesToWrite
;
1095 pFile
->FilePointer
+= nBytesToWrite
;
1096 buffer
+= nBytesToWrite
;
1099 //---------- Write to a Cluster Boundary
1101 nRelClusterPos
= FF_getClusterPosition(pIoman
, pFile
->FilePointer
, 1);
1102 if(nRelClusterPos
!= 0 && nBytes
>= nBytesPerCluster
) { // Need to get to cluster boundary
1104 nClusterDiff
= FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1) - pFile
->CurrentCluster
;
1106 if(pFile
->CurrentCluster
< FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1)) {
1107 pFile
->AddrCurrentCluster
= FF_TraverseFAT(pIoman
, pFile
->AddrCurrentCluster
, nClusterDiff
);
1108 pFile
->CurrentCluster
+= nClusterDiff
;
1112 nItemLBA
= FF_Cluster2LBA(pIoman
, pFile
->AddrCurrentCluster
);
1113 nItemLBA
= FF_getRealLBA(pIoman
, nItemLBA
+ FF_getMajorBlockNumber(pIoman
, pFile
->FilePointer
, 1)) + FF_getMinorBlockNumber(pIoman
, pFile
->FilePointer
, 1);
1115 sSectors
= (FF_T_UINT16
) (pIoman
->pPartition
->SectorsPerCluster
- (nRelClusterPos
/ pIoman
->BlkSize
));
1118 if(pIoman
->pBlkDevice
->fnWriteBlocks
) {
1119 RetVal
= pFile
->pIoman
->pBlkDevice
->fnWriteBlocks(buffer
, nItemLBA
, sSectors
, pIoman
->pBlkDevice
->pParam
);
1121 if(RetVal
== FF_ERR_DRIVER_BUSY
) {
1123 FF_Sleep(FF_DRIVER_BUSY_SLEEP
);
1125 }while(RetVal
== FF_ERR_DRIVER_BUSY
);
1127 nBytesToWrite
= sSectors
* pIoman
->BlkSize
;
1128 nBytes
-= nBytesToWrite
;
1129 buffer
+= nBytesToWrite
;
1130 nBytesWritten
+= nBytesToWrite
;
1131 pFile
->FilePointer
+= nBytesToWrite
;
1135 //---------- Write Clusters
1136 if(nBytes
>= nBytesPerCluster
) {
1137 //----- Thanks to Christopher Clark of DigiPen Institute of Technology in Redmond, US adding this traversal check.
1138 nClusterDiff
= FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1) - pFile
->CurrentCluster
;
1140 if(pFile
->CurrentCluster
< FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1)) {
1141 pFile
->AddrCurrentCluster
= FF_TraverseFAT(pIoman
, pFile
->AddrCurrentCluster
, nClusterDiff
);
1142 pFile
->CurrentCluster
+= nClusterDiff
;
1145 //----- End of Contributor fix.
1147 nClusters
= (nBytes
/ nBytesPerCluster
);
1149 FF_WriteClusters(pFile
, nClusters
, buffer
);
1151 nBytesToWrite
= (nBytesPerCluster
* nClusters
);
1153 pFile
->FilePointer
+= nBytesToWrite
;
1155 nBytes
-= nBytesToWrite
;
1156 buffer
+= nBytesToWrite
;
1157 nBytesWritten
+= nBytesToWrite
;
1160 //---------- Write Remaining Blocks
1161 if(nBytes
>= pIoman
->BlkSize
) {
1162 sSectors
= (FF_T_UINT16
) (nBytes
/ pIoman
->BlkSize
);
1164 nClusterDiff
= FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1) - pFile
->CurrentCluster
;
1166 if(pFile
->CurrentCluster
< FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1)) {
1167 pFile
->AddrCurrentCluster
= FF_TraverseFAT(pIoman
, pFile
->AddrCurrentCluster
, nClusterDiff
);
1168 pFile
->CurrentCluster
+= nClusterDiff
;
1172 nItemLBA
= FF_Cluster2LBA(pIoman
, pFile
->AddrCurrentCluster
);
1173 nItemLBA
= FF_getRealLBA(pIoman
, nItemLBA
+ FF_getMajorBlockNumber(pIoman
, pFile
->FilePointer
, 1)) + FF_getMinorBlockNumber(pIoman
, pFile
->FilePointer
, 1);
1176 if(pIoman
->pBlkDevice
->fnWriteBlocks
) {
1177 RetVal
= pFile
->pIoman
->pBlkDevice
->fnWriteBlocks(buffer
, nItemLBA
, sSectors
, pIoman
->pBlkDevice
->pParam
);
1179 if(RetVal
== FF_ERR_DRIVER_BUSY
) {
1181 FF_Sleep(FF_DRIVER_BUSY_SLEEP
);
1183 }while(RetVal
== FF_ERR_DRIVER_BUSY
);
1185 nBytesToWrite
= sSectors
* pIoman
->BlkSize
;
1186 pFile
->FilePointer
+= nBytesToWrite
;
1187 nBytes
-= nBytesToWrite
;
1188 buffer
+= nBytesToWrite
;
1189 nBytesWritten
+= nBytesToWrite
;
1193 //---------- Write (memcpy) Remaining Bytes
1196 nClusterDiff
= FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1) - pFile
->CurrentCluster
;
1198 if(pFile
->CurrentCluster
< FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1)) {
1199 pFile
->AddrCurrentCluster
= FF_TraverseFAT(pIoman
, pFile
->AddrCurrentCluster
, nClusterDiff
);
1200 pFile
->CurrentCluster
+= nClusterDiff
;
1204 nItemLBA
= FF_Cluster2LBA(pIoman
, pFile
->AddrCurrentCluster
);
1205 nItemLBA
= FF_getRealLBA(pIoman
, nItemLBA
+ FF_getMajorBlockNumber(pIoman
, pFile
->FilePointer
, 1)) + FF_getMinorBlockNumber(pIoman
, pFile
->FilePointer
, 1);
1206 pBuffer
= FF_GetBuffer(pIoman
, nItemLBA
, FF_MODE_WRITE
);
1208 memcpy(pBuffer
->pBuffer
, buffer
, nBytes
);
1210 FF_ReleaseBuffer(pIoman
, pBuffer
);
1212 nBytesToWrite
= nBytes
;
1213 pFile
->FilePointer
+= nBytesToWrite
;
1214 nBytes
-= nBytesToWrite
;
1215 buffer
+= nBytesToWrite
;
1216 nBytesWritten
+= nBytesToWrite
;
1221 if(pFile
->FilePointer
> pFile
->Filesize
) {
1222 pFile
->Filesize
= pFile
->FilePointer
;
1225 return nBytesWritten
;
1231 * @brief Writes a char to a FILE.
1233 * @param pFile FILE Pointer.
1234 * @param pa_cValue Char to be placed in the file.
1236 * @return Returns the value written to the file, or a value less than 0.
1239 FF_T_SINT32
FF_PutC(FF_FILE
*pFile
, FF_T_UINT8 pa_cValue
) {
1241 FF_T_UINT32 iItemLBA
;
1242 FF_T_UINT32 iRelPos
;
1243 FF_T_UINT32 nClusterDiff
;
1245 if(!pFile
) { // Ensure we don't have a Null file pointer on a Public interface.
1246 return FF_ERR_NULL_POINTER
;
1249 if(!(pFile
->Mode
& FF_MODE_WRITE
)) {
1250 return FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE
;
1253 // Make sure a write is after the append point.
1254 if((pFile
->Mode
& FF_MODE_APPEND
)) {
1255 if(pFile
->FilePointer
< pFile
->Filesize
) {
1256 FF_Seek(pFile
, 0, FF_SEEK_END
);
1260 iRelPos
= FF_getMinorBlockEntry(pFile
->pIoman
, pFile
->FilePointer
, 1);
1262 // Handle File Space Allocation.
1263 FF_ExtendFile(pFile
, pFile
->FilePointer
+ 1);
1265 nClusterDiff
= FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1) - pFile
->CurrentCluster
;
1267 if(pFile
->CurrentCluster
< FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1)) {
1268 pFile
->AddrCurrentCluster
= FF_TraverseFAT(pFile
->pIoman
, pFile
->AddrCurrentCluster
, nClusterDiff
);
1269 pFile
->CurrentCluster
+= nClusterDiff
;
1273 iItemLBA
= FF_Cluster2LBA(pFile
->pIoman
, pFile
->AddrCurrentCluster
) + FF_getMajorBlockNumber(pFile
->pIoman
, pFile
->FilePointer
, (FF_T_UINT16
) 1);
1274 iItemLBA
= FF_getRealLBA (pFile
->pIoman
, iItemLBA
) + FF_getMinorBlockNumber(pFile
->pIoman
, pFile
->FilePointer
, (FF_T_UINT16
) 1);
1276 pBuffer
= FF_GetBuffer(pFile
->pIoman
, iItemLBA
, FF_MODE_WRITE
);
1279 return FF_ERR_DEVICE_DRIVER_FAILED
;
1281 FF_putChar(pBuffer
->pBuffer
, (FF_T_UINT16
) iRelPos
, pa_cValue
);
1283 FF_ReleaseBuffer(pFile
->pIoman
, pBuffer
);
1285 pFile
->FilePointer
+= 1;
1286 if(pFile
->Filesize
< (pFile
->FilePointer
)) {
1287 pFile
->Filesize
+= 1;
1296 * @brief Equivalent to fseek()
1298 * @param pFile FF_FILE object that was created by FF_Open().
1299 * @param Offset An integer (+/-) to seek to, from the specified origin.
1300 * @param Origin Where to seek from. (FF_SEEK_SET seek from start, FF_SEEK_CUR seek from current position, or FF_SEEK_END seek from end of file).
1302 * @return 0 on Sucess,
1303 * @return -2 if offset results in an invalid position in the file.
1304 * @return -1 if a FF_FILE pointer was not recieved.
1305 * @return -3 if an invalid origin was provided.
1308 FF_ERROR
FF_Seek(FF_FILE
*pFile
, FF_T_SINT32 Offset
, FF_T_INT8 Origin
) {
1311 return FF_ERR_NULL_POINTER
;
1316 if((FF_T_UINT32
) Offset
<= pFile
->Filesize
&& Offset
>= 0) {
1317 pFile
->FilePointer
= Offset
;
1318 pFile
->CurrentCluster
= FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1);
1319 pFile
->AddrCurrentCluster
= FF_TraverseFAT(pFile
->pIoman
, pFile
->ObjectCluster
, pFile
->CurrentCluster
);
1326 if((Offset
+ pFile
->FilePointer
) <= pFile
->Filesize
&& (Offset
+ (FF_T_SINT32
) pFile
->FilePointer
) >= 0) {
1327 pFile
->FilePointer
= Offset
+ pFile
->FilePointer
;
1328 pFile
->CurrentCluster
= FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1);
1329 pFile
->AddrCurrentCluster
= FF_TraverseFAT(pFile
->pIoman
, pFile
->ObjectCluster
, pFile
->CurrentCluster
);
1336 if((Offset
+ (FF_T_SINT32
) pFile
->Filesize
) >= 0 && (Offset
+ pFile
->Filesize
) <= pFile
->Filesize
) {
1337 pFile
->FilePointer
= Offset
+ pFile
->Filesize
;
1338 pFile
->CurrentCluster
= FF_getClusterChainNumber(pFile
->pIoman
, pFile
->FilePointer
, 1);
1339 pFile
->AddrCurrentCluster
= FF_TraverseFAT(pFile
->pIoman
, pFile
->ObjectCluster
, pFile
->CurrentCluster
);
1356 * @brief Equivalent to fclose()
1358 * @param pFile FF_FILE object that was created by FF_Open().
1360 * @return 0 on sucess.
1361 * @return -1 if a null pointer was provided.
1364 FF_ERROR
FF_Close(FF_FILE
*pFile
) {
1366 FF_FILE
*pFileChain
;
1367 FF_DIRENT OriginalEntry
;
1371 return FF_ERR_NULL_POINTER
;
1373 // UpDate Dirent if File-size has changed?
1375 // Update the Dirent!
1376 Error
= FF_GetEntry(pFile
->pIoman
, pFile
->DirEntry
, pFile
->DirCluster
, &OriginalEntry
);
1381 if(!pFile
->FileDeleted
) {
1382 if(pFile
->Filesize
!= OriginalEntry
.Filesize
) {
1383 OriginalEntry
.Filesize
= pFile
->Filesize
;
1384 FF_PutEntry(pFile
->pIoman
, pFile
->DirEntry
, pFile
->DirCluster
, &OriginalEntry
);
1388 //if(pFile->Mode == FF_MODE_WRITE) {
1389 FF_FlushCache(pFile
->pIoman
); // Ensure all modfied blocks are flushed to disk!
1392 // Handle Linked list!
1393 FF_PendSemaphore(pFile
->pIoman
->pSemaphore
);
1394 { // Semaphore is required, or linked list could become corrupted.
1395 if(pFile
->pIoman
->FirstFile
== pFile
) {
1396 pFile
->pIoman
->FirstFile
= pFile
->Next
;
1398 pFileChain
= (FF_FILE
*) pFile
->pIoman
->FirstFile
;
1399 while(pFileChain
->Next
!= pFile
) {
1400 pFileChain
= pFileChain
->Next
;
1402 pFileChain
->Next
= pFile
->Next
;
1404 } // Semaphore released, linked list was shortened!
1405 FF_ReleaseSemaphore(pFile
->pIoman
->pSemaphore
);
1407 // If file written, flush to disk
1409 // Simply free the pointer!