// --------------------------------------------------------------------
// Copyright (c) 2005 by Terasic Technologies Inc. 
// --------------------------------------------------------------------
//
// Permission:
//
//   Terasic grants permission to use this code for all Terasic Development Boards 
//   and Altera Development Kits made by Terasic.  
//   Other use of this code, including the selling 
//   ,duplication, or modification of any portion is strictly prohibited.
//
//
// --------------------------------------------------------------------
//           
//                     Terasic Technologies Inc
//                     356 Fu-Shin E. Rd Sec. 1. JhuBei City,
//                     HsinChu County, Taiwan
//                     302
//
//                     web: http://www.terasic.com/
//                     email: support@terasic.com
//
// --------------------------------------------------------------------
//
//---------------------------------------------------------------------------

#include <string.h> // memcpy
#include "my_includes.h"
#include "SDCardDriver.h"
#include "FatFileSystem.h"
#include "FatInternal.h"


#ifdef DEBUG_FAT
    #define FAT_DEBUG(x)    DEBUG(x)
#else
    #define FAT_DEBUG(x)
#endif




VOLUME_INFO gVolumeInfo;

// internal function
CLUSTER_TYPE fatClusterType(unsigned short Fat);
unsigned int fatNextCluster(unsigned short ThisCluster);
void fatDumpDate(unsigned short Date);
void fatDumpTime(unsigned short Date);
bool fatIsValid(FAT_DIRECTORY *pDir);



// For FAT16 only
CLUSTER_TYPE fatClusterType(unsigned short Fat){
    CLUSTER_TYPE Type;
    
    if (Fat > 0 && Fat < 0xFFF0)
        Type = CLUSTER_NEXT_INFILE;
    else if (Fat >= 0xFFF8) // && Fat <= (unsigned short)0xFFFF)
        Type = CLUSTER_LAST_INFILE;
    else if (Fat == (unsigned short)0x00)
        Type = CLUSTER_UNUSED;
    else if (Fat >= 0xFFF0 && Fat <= 0xFFF6)
        Type = CLUSTER_RESERVED;
    else if (Fat == 0xFFF7)
        Type = CLUSTER_BAD;
        
    return Type;        
         
}

unsigned int fatNextCluster(unsigned short ThisCluster){
    CLUSTER_TYPE ClusterType;
    unsigned int NextCluster;
#ifdef FAT_READONLY    
//    const int nFatEntrySize = 2; // 2 byte for FAT16

//    NextCluster =  *(unsigned short *)(gVolumeInfo.szFatTable + ThisCluster*nFatEntrySize); 
    NextCluster =  *(unsigned short *)(gVolumeInfo.szFatTable + (ThisCluster << 1)); 
    ClusterType = fatClusterType(NextCluster);
    if (ClusterType != CLUSTER_NEXT_INFILE && ClusterType != CLUSTER_LAST_INFILE)
        NextCluster = 0;  // invalid cluster
#else
    int nFatEntryPerSecter;
    const int nFatEntrySize = 2; // 2 byte for FAT16
    unsigned int Secter;
    char szBlock[512];
    nFatEntryPerSecter = gVolumeInfo.BPB_BytsPerSec/nFatEntrySize; 
    Secter = gVolumeInfo.FatEntrySecter + (ThisCluster*nFatEntrySize)/gVolumeInfo.BPB_BytsPerSec;
    if (SD_read_block(Secter,szBlock)){
        NextCluster = *(unsigned short *)(szBlock + (ThisCluster%nFatEntryPerSecter)*nFatEntrySize); 
        ClusterType = fatClusterType(NextCluster);
        if (ClusterType != CLUSTER_NEXT_INFILE && ClusterType != CLUSTER_LAST_INFILE)
            NextCluster = 0;  // invalid cluster
    }                
    
    
    return NextCluster;

#endif    
    
    return NextCluster;
}

void fatDumpDate(unsigned short Date){
    int Year, Month, Day;
    Year = ((Date >> 9) & 0x1F) + 1980;
    Month = ((Date >> 5) & 0xF);
    Day = ((Date >> 0) & 0x1F);
    FAT_DEBUG(("%d,%d,%d", Year, Month, Day)); 
}

void fatDumpTime(unsigned short Date){
    int H,M,S;
    H = ((Date >> 9) & 0x1F);
    M = ((Date >> 5) & 0x3F);
    S = ((Date >> 0) & 0x1F)*2;
    FAT_DEBUG(("%d:%d:%d", H, M, S));     
}

bool fatIsLast(FAT_DIRECTORY *pDir){
    if (pDir->Name[0] == 0x00)
        return TRUE;
    return FALSE;        
}


bool fatIsValid(FAT_DIRECTORY *pDir){
    char szTest[] = {0x00, 0xE5, 0x22, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x3A, 0x3B, 0x3C, 0x3E, 0x3F, 0x5B, 0x5C, 0x5D, 0x7C};
    int i;
    
    for(i=0;i<sizeof(szTest)/sizeof(szTest[0]);i++){
        if (pDir->Name[0] == szTest[i]){
            return FALSE;
        }
    }    
    return TRUE;
    
}


// debug
void Fat_Dump(FAT_DIRECTORY *pDir){
    char szInvalidName[] = {0x22, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x3A, 0x3B, 0x3C, 0x3E, 0x3F, 0x5B, 0x5C, 0x5D, 0x7C};
    int i;
    if (pDir->Name[0] == (char)0xE5){
        FAT_DEBUG(("the directory entry is free.\n"));
        return;
    }
    if (pDir->Name[0] == 0x00){
        FAT_DEBUG(("the directory entry is free, and there are no allocated directory entries after tis one.\n"));
        return;
    }
    
    if (pDir->Name[0] <= 0x20 && pDir->Name[0] != 0x05){
        FAT_DEBUG(("Invalid file name.\n"));
        return;
    }
    
    for(i=0;i<sizeof(szInvalidName)/sizeof(szInvalidName[0]);i++){
        if (pDir->Name[0] == szInvalidName[i]){
            FAT_DEBUG(("Invalid file name.\n"));
            return;
        }
    }    
    
    //printf("sizeof(FAT_TABLE):%d\n", (int)sizeof(FAT_TABLE));
    if (pDir->Name[0] == 0x05){
        FAT_DEBUG(("Name:%c%c%c%c%c%c%c%c\n", 0xE5,pDir->Name[1],pDir->Name[2],pDir->Name[3],pDir->Name[4],pDir->Name[5],pDir->Name[6],pDir->Name[6]));
    }else{
        FAT_DEBUG(("Name:%c%c%c%c%c%c%c%c\n", pDir->Name[0],pDir->Name[1],pDir->Name[2],pDir->Name[3],pDir->Name[4],pDir->Name[5],pDir->Name[6],pDir->Name[6]));
    }        
    FAT_DEBUG(("Extention:%c%c%c\n", pDir->Extension[0],pDir->Extension[1],pDir->Extension[2]));
    FAT_DEBUG(("Attribute:%02Xh\n", pDir->Attribute));
    if (pDir->Attribute & ATTR_READ_ONLY)
        FAT_DEBUG(("  Read-Only\n"));
    if (pDir->Attribute & ATTR_HIDDEN)
        FAT_DEBUG(("  Hidden\n"));
    if (pDir->Attribute & ATTR_SYSTEM)
        FAT_DEBUG(("  System\n"));
    if (pDir->Attribute & ATTR_VOLUME_ID)
        FAT_DEBUG(("  Volume\n"));
    if (pDir->Attribute & ATTR_DIRECTORY)
        FAT_DEBUG(("  Directory\n"));
    if (pDir->Attribute & ATTR_ARCHIVE)
        FAT_DEBUG(("  Archive\n"));
    if (pDir->Attribute & ATTR_LONG_NAME)
        FAT_DEBUG(("  Long Name\n"));
    FAT_DEBUG(("CreateTime:")); fatDumpTime(pDir->CreateTime);FAT_DEBUG(("\n"));
    FAT_DEBUG(("CreateDate:")); fatDumpDate(pDir->LastAccessDate);FAT_DEBUG(("\n"));
    FAT_DEBUG(("ClusterHi:%04Xh\n", pDir->FirstLogicalClusterHi));
    FAT_DEBUG(("LastWriteTime:")); fatDumpTime(pDir->LastWriteTime);FAT_DEBUG(("\n"));
    FAT_DEBUG(("LastWriteDate:")); fatDumpDate(pDir->LastWriteDate);FAT_DEBUG(("\n"));
    FAT_DEBUG(("Cluster:%04Xh(%d)\n", pDir->FirstLogicalCluster,pDir->FirstLogicalCluster));
    FAT_DEBUG(("File Size:%08Xh(%ld)\n", pDir->FileSize, (long)pDir->FileSize));
}


#define SD_CHECK
bool Fat_Mount(FAT_DEVICE FatDevice){
#ifdef SD_CHECK    
    const int nMaxTry=10;
    bool bFind = FALSE;
    int nTry=0;
#endif
    bool bSuccess = TRUE;
    unsigned char szBlock[512];
    int FirstPartitionEntry,PartitionType,FirstSectionInVolume1;
    int nFatTableSize,nFatTableSecterNum;
#ifdef FAT_READONLY
    int i;
#endif    
    //int BPB_BytsPerSec,BPB_RsvdSecCnt,BPB_NumFATs,BPB_RootEntCnt,BPB_FATSz;
    
    Fat_Unmount();
    
    if (FatDevice != FAT_SD_CARD)
        return FALSE;
        
#ifdef SD_CHECK        
    //1. chek whether SD Card existed. Init SD card if it is present.
    while(!bFind && nTry++ < nMaxTry){
        bFind = SD_card_init();
        if (!bFind)
            usleep(100*1000);
    }
    if (!bFind){
        FAT_DEBUG(("Cannot find SD card.\n"));
        return FALSE;
    }    
#endif
        
    //2. parsing Boot Sector system
    //2.1 Read the Master Boot Record(MBR) of FAT file system (Locate the section 0)
    // Offset: 
    // 000h(446 bytes): Executable Code (Boots Computer) 
    // 1BEh( 16 bytes): 1st Partition Entry
    // 1CEh( 16 bytes): 2nd Partition Entry
    // 1DEh( 16 bytes): 3nd Partition Entry
    // 1EEh( 16 bytes): 4nd Partition Entry
    // 1FEh(  2 bytes): Executable Maker (55h AAh)
    
    // read first block
    
    if (!SD_read_block(0, szBlock)){
        FAT_DEBUG(("Read section 0 error.\n"));
        return FALSE;
    }

    // check file system 
    FirstPartitionEntry = 0x1BE;
    PartitionType = szBlock[FirstPartitionEntry + 4];
    if (PartitionType != PARTITION_FAT16){
        FAT_DEBUG(("the partition type is not supported.\n"));
        return FALSE; // only support FAT16 in this example
    }        
    // 2.2 Find the first section of partition 1                    
    FirstSectionInVolume1 = szBlock[FirstPartitionEntry + 8 + 3]*256*256*256 + 
                            szBlock[FirstPartitionEntry + 8 + 2]*256*256 + 
                            szBlock[FirstPartitionEntry + 8 + 1]*256 + 
                            szBlock[FirstPartitionEntry + 8];        
    
    //3 Parsing the Volume Boot Record(BR)
    //3.1  Read the Volume Boot Record(BR)
    if (!SD_read_block(FirstSectionInVolume1, szBlock)){
        FAT_DEBUG(("Read first sector in volume one fail.\n"));
        return FALSE;
    }        
    gVolumeInfo.PartitionStartSecter = FirstSectionInVolume1;     
    gVolumeInfo.BPB_BytsPerSec = szBlock[0x0B+1]*256 + szBlock[0x0B];
    gVolumeInfo.BPB_SecPerCluster = szBlock[0x0D];
    gVolumeInfo.BPB_RsvdSecCnt = szBlock[0x0E + 1]*256 + szBlock[0x0E]; 
    gVolumeInfo.BPB_NumFATs = szBlock[0x10];
    gVolumeInfo.BPB_RootEntCnt = szBlock[0x11+1]*256 + szBlock[0x11]; 
    gVolumeInfo.BPB_FATSz = szBlock[0x16+1]*256 + szBlock[0x16];  
#ifdef DUMP_DEBUG    
    FAT_DEBUG(("First section in partition 1: %04Xh(%d)\n", gVolumeInfo.PartitionStartSecter, gVolumeInfo.PartitionStartSecter));
    FAT_DEBUG(("Byte Per Sector: %04Xh(%d)\n", gVolumeInfo.BPB_BytsPerSec, gVolumeInfo.BPB_BytsPerSec));
    FAT_DEBUG(("Sector Per Clusoter: %02Xh(%d)\n", gVolumeInfo.BPB_SecPerCluster, gVolumeInfo.BPB_SecPerCluster)); 
    FAT_DEBUG(("Reserved Sectors: %04Xh(%d)\n", gVolumeInfo.BPB_RsvdSecCnt, gVolumeInfo.BPB_RsvdSecCnt));
    FAT_DEBUG(("Number of Copyies of FAT: %02Xh(%d)\n", gVolumeInfo.BPB_NumFATs, gVolumeInfo.BPB_NumFATs));
    FAT_DEBUG(("Maxmun Root Directory Entries: %04Xh(%d)\n", gVolumeInfo.BPB_RootEntCnt, gVolumeInfo.BPB_RootEntCnt));
    FAT_DEBUG(("Sectors Per FAT: %04Xh(%d)\n", gVolumeInfo.BPB_FATSz, gVolumeInfo.BPB_FATSz));
#endif    
    //
    gVolumeInfo.FatEntrySecter = gVolumeInfo.PartitionStartSecter + gVolumeInfo.BPB_RsvdSecCnt;
    gVolumeInfo.RootDirectoryEntrySecter = gVolumeInfo.FatEntrySecter + gVolumeInfo.BPB_NumFATs * gVolumeInfo.BPB_FATSz;
    gVolumeInfo.DataEntrySecter = gVolumeInfo.RootDirectoryEntrySecter + ((gVolumeInfo.BPB_RootEntCnt*32)+(gVolumeInfo.BPB_BytsPerSec-1))/gVolumeInfo.BPB_BytsPerSec;
    
    // read FAT table into memory
    gVolumeInfo.nBytesPerCluster = gVolumeInfo.BPB_BytsPerSec * gVolumeInfo.BPB_SecPerCluster;
    nFatTableSecterNum = gVolumeInfo.BPB_NumFATs * gVolumeInfo.BPB_FATSz;
    nFatTableSize = nFatTableSecterNum * gVolumeInfo.BPB_BytsPerSec;
#ifdef FAT_READONLY    
    gVolumeInfo.szFatTable = malloc(nFatTableSize);
    if (!gVolumeInfo.szFatTable){
        FAT_DEBUG(("fat malloc(%d) fail!", nFatTableSize));
        return FALSE;
    }
    for(i=0;i<nFatTableSecterNum; i++ ){
        if (!SD_read_block(gVolumeInfo.FatEntrySecter+i, gVolumeInfo.szFatTable + i*gVolumeInfo.BPB_BytsPerSec)){
            FAT_DEBUG(("Read first sector in volume one fail.\n"));
            bSuccess = FALSE;
        }          
    }
    
    
    if (!bSuccess && gVolumeInfo.szFatTable){
        free(gVolumeInfo.szFatTable);
        gVolumeInfo.szFatTable = 0;
    }
#endif    
    
/*    
 // debug    
    SD_read_block(gVolumeInfo.RootEntry, szBlock);
    DumpFat(szBlock);
    for(i=0;i<512;i++){
        if (i%20 == 0)
            printf("\n[%06d]%02X ", i, szBlock[i]);
        else    
            printf("%02X ", szBlock[i]);
    }      
    
*/    
    if (bSuccess){
        FAT_DEBUG(("Fat_Mount success\n"));
    }else{        
        FAT_DEBUG(("Fat_Mount fail\n"));
    }        
    gVolumeInfo.bMount = bSuccess;
    return bSuccess;   
}

void Fat_Unmount(void){
    
#ifdef FAT_READONLY      
    if (gVolumeInfo.szFatTable){
        free(gVolumeInfo.szFatTable);
        gVolumeInfo.szFatTable = 0;
    }        
#endif //#ifdef FAT_READONLY
      
    gVolumeInfo.bMount = FALSE;
}   

bool Fat_FileBrowseBegin(FAT_BROWSE_HANDLE *pFatBrowseHandle){
    if (!gVolumeInfo.bMount)
        return FALSE;
    pFatBrowseHandle->DirectoryIndex = 0;
    return TRUE;
}

bool Fat_FileBrowseNext(FAT_BROWSE_HANDLE *pFatBrowseHandle, FAT_DIRECTORY *pDirectory){
    bool bFind = FALSE, bError=FALSE;
    FAT_DIRECTORY *pDir;
    unsigned int nSecter, nOffset;
    char szBlock[512];
    
    if (!gVolumeInfo.bMount)
        return FALSE;
    
    
    do{
        nOffset = (sizeof(FAT_DIRECTORY)*pFatBrowseHandle->DirectoryIndex)/gVolumeInfo.BPB_BytsPerSec;
        nSecter = gVolumeInfo.RootDirectoryEntrySecter + nOffset;
        if (!SD_read_block(nSecter, szBlock)){
            bError = TRUE;
        }else{    
            nOffset = (sizeof(FAT_DIRECTORY)*pFatBrowseHandle->DirectoryIndex)%gVolumeInfo.BPB_BytsPerSec; 
            pDir = (FAT_DIRECTORY *)(szBlock + nOffset); 
            pFatBrowseHandle->DirectoryIndex++;
            bFind = fatIsValid(pDir);
            if (bFind)
                *pDirectory = *pDir;
        }            
    }while (!bFind && !fatIsLast(pDir) && !bError);
    
    return bFind;
    
}

unsigned int Fat_FileCount(void){
    unsigned int nCount = 0;
    FAT_BROWSE_HANDLE hBrowse;
    FAT_DIRECTORY Dir;
    
    if (Fat_FileBrowseBegin(&hBrowse)){
        while(Fat_FileBrowseNext(&hBrowse, &Dir))
            nCount++;
    }
    
    return nCount;
}

void Fat_ComposeFilename(FAT_DIRECTORY *pDir, char *szFilename){
    int i,nPos=0;
    i=0;
    while(i < 8 && pDir->Name[i] != 0 && pDir->Name[i] != ' ')
        szFilename[nPos++] = pDir->Name[i++];
    szFilename[nPos++] = '.';   
    i=0;
    while(i < 3 && pDir->Extension[i] != 0 && pDir->Extension[i] != ' ')
        szFilename[nPos++] = pDir->Extension[i++];
    szFilename[nPos++] = 0;   
    
}
 
// File Access
bool Fat_FileOpen(FAT_FILE_HANDLE *pFileHandle, const char *pFilename){
    bool bSuccess = FALSE;
    char szFilename[32];
    FAT_BROWSE_HANDLE hBrowse;     
    FAT_DIRECTORY Dir;
    
    pFileHandle->IsOpened = FALSE;
    if (Fat_FileBrowseBegin(&hBrowse)){
        while (!bSuccess && Fat_FileBrowseNext(&hBrowse, &Dir)){
            Fat_ComposeFilename(&Dir, szFilename);
            if (strcmpi(szFilename, pFilename) == 0){
                pFileHandle->SeekPos = 0;
                pFileHandle->Directory = Dir;
                pFileHandle->IsOpened = TRUE;
                pFileHandle->Cluster = Dir.FirstLogicalCluster;
                pFileHandle->ClusterSeq = 0;
                bSuccess = TRUE;
            }
            
        }    
    }  
    return bSuccess;  
}

unsigned int Fat_FileSize(FAT_FILE_HANDLE *pFileHandle){
    if (pFileHandle->IsOpened)
        return pFileHandle->Directory.FileSize;
    return 0;        
}


bool Fat_FileRead(FAT_FILE_HANDLE *pFileHandle, void *pBuffer, const int nBufferSize){
    unsigned int Pos, PhysicalSecter, NextCluster, Cluster, BytesPerCluster, nReadCount=0, nClusterSeq;
    int s;
    bool bSuccess= TRUE;
    char szBlock[512];
    if (!pFileHandle->IsOpened){
        FAT_DEBUG(("[FAT] Fat_FileRead, file not opened\r\n"));
        return bSuccess;
    }        
        
    BytesPerCluster = gVolumeInfo.nBytesPerCluster; //gVolumeInfo.BPB_BytsPerSec * gVolumeInfo.BPB_SecPerCluster;  
    Pos = pFileHandle->SeekPos;
    if (BytesPerCluster == 16384)
        nClusterSeq = Pos >> 14;
    else        
        nClusterSeq = Pos/BytesPerCluster;
    if (BytesPerCluster == 16384)
       Pos -= (pFileHandle->ClusterSeq << 14);
    else   
       Pos -= pFileHandle->ClusterSeq*BytesPerCluster;
    if (nClusterSeq == pFileHandle->ClusterSeq){
        Cluster = pFileHandle->Cluster;
    }else{
        // move to first clustor for reading
        while (Pos >= BytesPerCluster && bSuccess){
            // go to next cluster
            NextCluster = fatNextCluster(Cluster); 
            if (NextCluster == 0)
                bSuccess = FALSE;
            else
                Cluster = NextCluster;    
            Pos -= BytesPerCluster;
            pFileHandle->Cluster = Cluster;
            pFileHandle->ClusterSeq++; 
        }
    }            
    
    // reading
    while(nReadCount < nBufferSize && bSuccess){
        if (gVolumeInfo.BPB_SecPerCluster == 32)
            PhysicalSecter = ((Cluster-2) << 5) + gVolumeInfo.DataEntrySecter; // -2: FAT0 & FAT1 are reserved
        else
            PhysicalSecter = (Cluster-2)*gVolumeInfo.BPB_SecPerCluster + gVolumeInfo.DataEntrySecter; // -2: FAT0 & FAT1 are reserved
        for(s=0;s<gVolumeInfo.BPB_SecPerCluster && nReadCount < nBufferSize;s++){
            if (Pos >= gVolumeInfo.BPB_BytsPerSec){
                Pos -= gVolumeInfo.BPB_BytsPerSec;
            }else{
                int nCopyCount;
                nCopyCount = gVolumeInfo.BPB_BytsPerSec;
                if (Pos)
                    nCopyCount -= Pos;
                if (nCopyCount > (nBufferSize-nReadCount))
                    nCopyCount = nBufferSize-nReadCount;
                if (nCopyCount == 512){
                    if (!SD_read_block(PhysicalSecter, (char *)pBuffer+nReadCount)){
                        bSuccess = FALSE; // fail
                        FAT_DEBUG(("[FAT] Fat_FileRead, SD_read_block fail (512)\r\n"));
                    }else{
                        nReadCount += nCopyCount;
                        if (Pos > 0)
                            Pos = 0;
                    }         
                }else{    
                    if (!SD_read_block(PhysicalSecter, szBlock)){
                        bSuccess = FALSE; // fail
                        FAT_DEBUG(("[FAT] Fat_FileRead, SD_read_block fail\r\n"));
                    }else{
                        memcpy((void *)((char *)pBuffer+nReadCount), szBlock+Pos,nCopyCount);
                        nReadCount += nCopyCount;
                        if (Pos > 0)
                            Pos = 0;
                    }         
                }    
                    
            }
            PhysicalSecter++;
        }    
            
        // next cluster
        if (nReadCount < nBufferSize){
            NextCluster = fatNextCluster(Cluster); 
            if (NextCluster == 0){
                bSuccess = FALSE;
                FAT_DEBUG(("[FAT] Fat_FileRead, no next cluster\r\n"));
            }else{
                Cluster = NextCluster;
            }    
            //    
            pFileHandle->ClusterSeq++; 
            pFileHandle->Cluster = Cluster;
        }                    
    }
    
    if (bSuccess){
        pFileHandle->SeekPos += nBufferSize;
    }        
        
    
    return bSuccess;
}

bool Fat_FileSeek(FAT_FILE_HANDLE *pFileHandle, const FAT_SEEK_POS SeekPos, const int nOffset){
    bool bSuccess= TRUE;
    if (!pFileHandle->IsOpened)
        return bSuccess;
        
    switch(SeekPos){
        case FILE_SEEK_BEGIN: 
            pFileHandle->SeekPos = nOffset; 
            break;
        case FILE_SEEK_CURRENT: 
            pFileHandle->SeekPos += nOffset; 
            break;
        case FILE_SEEK_END: 
            pFileHandle->SeekPos = pFileHandle->Directory.FileSize+nOffset; 
            break;
        default:
            bSuccess = FALSE;
            break;    
              
    }
    pFileHandle->Cluster = pFileHandle->Directory.FirstLogicalCluster;
    pFileHandle->ClusterSeq = 0;
    
    return bSuccess;
    
}

void Fat_FileClose(FAT_FILE_HANDLE *pFileHandle){
    if (pFileHandle->IsOpened)
        pFileHandle->IsOpened = FALSE;
}

bool Fat_FileIsOpened(FAT_FILE_HANDLE *pFileHandle){
    return pFileHandle->IsOpened;
}

